Przeglądaj źródła

remove numpy dependency where possible

Falko Schindler 1 rok temu
rodzic
commit
3b81379878

+ 0 - 1
examples/simpy/async_realtime_environment.py

@@ -2,7 +2,6 @@ import asyncio
 from time import monotonic
 from typing import Any, Optional, Union
 
-from numpy import Infinity
 from simpy.core import EmptySchedule, Environment, Infinity, SimTime, StopSimulation
 from simpy.events import URGENT, Event
 from simpy.rt import RealtimeEnvironment

+ 1 - 1
nicegui/elements/plotly.py

@@ -43,7 +43,7 @@ class Plotly(Element, component='plotly.vue', libraries=['lib/plotly/plotly.min.
     def _get_figure_json(self) -> Dict:
         if isinstance(self.figure, go.Figure):
             # convert go.Figure to dict object which is directly JSON serializable
-            # orjson supports numpy array serialization
+            # orjson supports NumPy array serialization
             return self.figure.to_plotly_json()
 
         if isinstance(self.figure, dict):

+ 3 - 4
nicegui/elements/scene_objects.py

@@ -1,7 +1,6 @@
+import math
 from typing import List, Optional
 
-import numpy as np
-
 from .scene_object3d import Object3D
 
 
@@ -60,7 +59,7 @@ class Ring(Object3D):
                  theta_segments: int = 8,
                  phi_segments: int = 1,
                  theta_start: float = 0,
-                 theta_length: float = 2 * np.pi,
+                 theta_length: float = 2 * math.pi,
                  wireframe: bool = False,
                  ) -> None:
         super().__init__('ring',
@@ -164,7 +163,7 @@ class SpotLight(Object3D):
                  color: str = '#ffffff',
                  intensity: float = 1.0,
                  distance: float = 0.0,
-                 angle: float = np.pi / 3,
+                 angle: float = math.pi / 3,
                  penumbra: float = 0.0,
                  decay: float = 1.0,
                  ) -> None:

+ 2 - 2
nicegui/globals.py

@@ -4,7 +4,7 @@ import logging
 from contextlib import contextmanager
 from enum import Enum
 from pathlib import Path
-from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Iterator, List, Optional, Union
+from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Iterator, List, Optional, Set, Union
 
 from socketio import AsyncServer
 from uvicorn import Server
@@ -33,7 +33,7 @@ loop: Optional[asyncio.AbstractEventLoop] = None
 log: logging.Logger = logging.getLogger('nicegui')
 state: State = State.STOPPED
 ui_run_has_been_called: bool = False
-optional_features: List[str] = []
+optional_features: Set[str] = set()
 
 reload: bool
 title: str

+ 10 - 7
nicegui/json/builtin_wrapper.py

@@ -2,15 +2,18 @@ import json
 from datetime import date, datetime
 from typing import Any, Optional, Tuple
 
-import numpy as np
 from fastapi import Response
 
+try:
+    import numpy as np
+except ImportError:
+    np = None
+
 
 def dumps(obj: Any, sort_keys: bool = False, separators: Optional[Tuple[str, str]] = None):
     """Serializes a Python object to a JSON-encoded string.
 
-    This implementation uses Python's default json module, but extends it
-    in order to support numpy arrays.
+    This implementation uses Python's default json module, but extends it in order to support NumPy arrays.
     """
     if separators is None:
         separators = (',', ':')
@@ -41,14 +44,14 @@ class NiceGUIJSONResponse(Response):
 
 
 class NumpyJsonEncoder(json.JSONEncoder):
-    """Special json encoder that supports numpy arrays and date/datetime objects."""
+    """Special json encoder that supports NumPy arrays and date/datetime objects."""
 
     def default(self, obj):
-        if isinstance(obj, np.integer):
+        if np and isinstance(obj, np.integer):
             return int(obj)
-        if isinstance(obj, np.floating):
+        if np and isinstance(obj, np.floating):
             return float(obj)
-        if isinstance(obj, np.ndarray):
+        if np and isinstance(obj, np.ndarray):
             return obj.tolist()
         if isinstance(obj, (datetime, date)):
             return obj.isoformat()

+ 8 - 5
nicegui/json/orjson_wrapper.py

@@ -1,18 +1,21 @@
 from decimal import Decimal
 from typing import Any, Optional, Tuple
 
-import numpy as np
 import orjson
 from fastapi import Response
 
+try:
+    import numpy as np
+except ImportError:
+    np = None
+
 ORJSON_OPTS = orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_NON_STR_KEYS
 
 
 def dumps(obj: Any, sort_keys: bool = False, separators: Optional[Tuple[str, str]] = None):
     """Serializes a Python object to a JSON-encoded string.
 
-    By default, this function supports serializing numpy arrays,
-    which Python's json module does not.
+    By default, this function supports serializing NumPy arrays, which Python's json module does not.
 
     Uses package `orjson` internally.
     """
@@ -40,8 +43,8 @@ def loads(value: str) -> Any:
 
 
 def _orjson_converter(obj):
-    """Custom serializer/converter, e.g. for numpy object arrays."""
-    if isinstance(obj, np.ndarray) and obj.dtype == np.object_:
+    """Custom serializer/converter, e.g. for NumPy object arrays."""
+    if np and isinstance(obj, np.ndarray) and obj.dtype == np.object_:
         return obj.tolist()
     if isinstance(obj, Decimal):
         return float(obj)

+ 3 - 3
nicegui/ui.py

@@ -88,6 +88,7 @@ __all__ = [
     'run_with',
 ]
 
+from . import globals
 from .deprecation import deprecated
 from .element import Element as element
 from .elements.aggrid import AgGrid as aggrid
@@ -173,10 +174,9 @@ from .page_layout import RightDrawer as right_drawer
 from .run import run
 from .run_with import run_with
 
-from .globals import optional_features
 try:
     from .elements.plotly import Plotly as plotly
-    optional_features.append('plotly')
+    globals.optional_features.add('plotly')
 except ImportError:
     def plotly(*args, **kwargs):
         raise ImportError('Plotly is not installed. Please run "pip install plotly".')
@@ -187,7 +187,7 @@ if os.environ.get('MATPLOTLIB', 'true').lower() == 'true':
         from .elements.line_plot import LinePlot as line_plot
         from .elements.pyplot import Pyplot as pyplot
         plot = deprecated(pyplot, 'ui.plot', 'ui.pyplot', 317)
-        optional_features.append('matplotlib')
+        globals.optional_features.add('matplotlib')
     except ImportError:
         def line_plot(*args, **kwargs):
             raise ImportError('Matplotlib is not installed. Please run "pip install matplotlib".')

+ 3 - 3
nicegui/welcome.py

@@ -2,17 +2,17 @@ import os
 import socket
 from typing import List
 
-from .globals import optional_features
+from . import globals
 
 try:
     import netifaces
-    optional_features.append('netifaces')
+    globals.optional_features.add('netifaces')
 except ImportError:
     pass
 
 
 def get_all_ips() -> List[str]:
-    if not 'netifaces' in optional_features:
+    if 'netifaces' not in globals.optional_features:
         return [info[4][0] for info in socket.getaddrinfo(socket.gethostname(), None) if len(info[4]) == 2]
     ips = []
     for interface in netifaces.interfaces():

+ 1 - 0
pyproject.toml

@@ -52,6 +52,7 @@ pandas = [
 ]
 secure = "^0.3.0"
 webdriver-manager = "^3.8.6"
+numpy = "^1.25.0"
 
 [build-system]
 requires = [

+ 1 - 1
tests/test_plotly.py

@@ -12,7 +12,7 @@ def test_plotly(screen: Screen):
     plot = ui.plotly(fig)
 
     ui.button('Add trace', on_click=lambda: (
-        # test numpy array support for value arrays
+        # test NumPy array support for value arrays
         fig.add_trace(go.Scatter(x=np.array([0, 1, 2]), y=np.array([2, 1, 0]), name='Trace 2')),
         plot.update()
     ))

+ 2 - 2
website/more_documentation/chart_documentation.py

@@ -2,7 +2,7 @@ from nicegui import ui
 
 
 def main_demo() -> None:
-    from numpy.random import random
+    from random import random
 
     chart = ui.chart({
         'title': False,
@@ -15,7 +15,7 @@ def main_demo() -> None:
     }).classes('w-full h-64')
 
     def update():
-        chart.options['series'][0]['data'][:] = random(2)
+        chart.options['series'][0]['data'][0] = random()
         chart.update()
 
     ui.button('Update', on_click=update)

+ 3 - 4
website/more_documentation/line_plot_documentation.py

@@ -2,18 +2,17 @@ from nicegui import events, ui
 
 
 def main_demo() -> None:
+    import math
     from datetime import datetime
 
-    import numpy as np
-
     line_plot = ui.line_plot(n=2, limit=20, figsize=(3, 2), update_every=5) \
         .with_legend(['sin', 'cos'], loc='upper center', ncol=2)
 
     def update_line_plot() -> None:
         now = datetime.now()
         x = now.timestamp()
-        y1 = np.sin(x)
-        y2 = np.cos(x)
+        y1 = math.sin(x)
+        y2 = math.cos(x)
         line_plot.push([now], [[y1], [y2]])
 
     line_updates = ui.timer(0.1, update_line_plot, active=False)

+ 4 - 5
website/more_documentation/plotly_documentation.py

@@ -1,5 +1,3 @@
-import numpy as np
-
 from nicegui import ui
 
 from ..documentation_tools import text_demo
@@ -34,7 +32,7 @@ def more() -> None:
                     'type': 'scatter',
                     'name': 'Trace 2',
                     'x': [1, 2, 3, 4],
-                    'y': np.array([1.4, 1.8, 3.8, 3.2]),
+                    'y': [1.4, 1.8, 3.8, 3.2],
                     'line': {'dash': 'dot', 'width': 3},
                 },
             ],
@@ -53,7 +51,8 @@ def more() -> None:
         To send the new plot to the browser, make sure to explicitly call `plot.update()` or `ui.update(plot)`.
     ''')
     def plot_updates():
-        import numpy as np
+        from random import random
+
         import plotly.graph_objects as go
 
         fig = go.Figure()
@@ -61,7 +60,7 @@ def more() -> None:
         plot = ui.plotly(fig).classes('w-full h-40')
 
         def add_trace():
-            fig.add_trace(go.Scatter(x=np.arange(10), y=np.random.randn(10)))
+            fig.add_trace(go.Scatter(x=[1, 2, 3], y=[random(), random(), random()]))
             plot.update()
 
         ui.button('Add trace', on_click=add_trace)