pgwidgets-python 0.1.3__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
pgwidgets/__init__.py CHANGED
@@ -16,4 +16,17 @@ Usage (asynchronous):
16
16
  top = await W.TopLevel(title="Hello", resizable=True)
17
17
  ...
18
18
  await app.run()
19
+
20
+ Version:
21
+ import pgwidgets
22
+ print(pgwidgets.__version__)
19
23
  """
24
+
25
+ from importlib.metadata import version as _pkg_version, PackageNotFoundError
26
+
27
+ try:
28
+ __version__ = _pkg_version("pgwidgets")
29
+ except PackageNotFoundError:
30
+ # Package not installed (e.g. running from a source checkout
31
+ # without `pip install -e .`). Fall back to a sentinel.
32
+ __version__ = "0.0.0+unknown"
pgwidgets/_json.py ADDED
@@ -0,0 +1,63 @@
1
+ """JSON encoding helper for the remote protocol.
2
+
3
+ The wire format between Python and the browser is JSON. The stdlib
4
+ encoder has two issues for scientific data:
5
+
6
+ 1. It rejects numpy scalars other than ``np.float64`` (which inherits
7
+ from float), numpy arrays, and similar buffer-protocol objects —
8
+ silently dropping a TreeView payload that happens to contain
9
+ ``np.int64`` cell values.
10
+
11
+ 2. It writes the literal tokens ``NaN`` / ``Infinity`` / ``-Infinity``
12
+ for non-finite floats, which browsers' ``JSON.parse`` reject — a
13
+ single masked / missing cell in a float column makes the whole
14
+ payload silently fail to parse on the JS side.
15
+
16
+ ``JsonEncoder`` handles both: ``.item()`` / ``.tolist()`` fall-backs
17
+ for numpy/pandas types, and a pre-walk that replaces non-finite floats
18
+ with ``None`` (encoded as JSON ``null``). Use it via
19
+ ``json.dumps(obj, cls=JsonEncoder)``.
20
+ """
21
+
22
+ import json
23
+ import math
24
+
25
+
26
+ def _scrub_nan(obj):
27
+ """Recursively replace non-finite floats with None so the result
28
+ is RFC-8259-valid JSON when re-encoded. Cheap walk: O(n) and
29
+ only visits dicts/lists/tuples/floats."""
30
+ if isinstance(obj, float):
31
+ return obj if math.isfinite(obj) else None
32
+ if isinstance(obj, dict):
33
+ return {k: _scrub_nan(v) for k, v in obj.items()}
34
+ if isinstance(obj, (list, tuple)):
35
+ return [_scrub_nan(v) for v in obj]
36
+ return obj
37
+
38
+
39
+ def _coerce_scalar(v):
40
+ """If *v* is a float, replace non-finite values with None."""
41
+ if isinstance(v, float) and not math.isfinite(v):
42
+ return None
43
+ return v
44
+
45
+
46
+ class JsonEncoder(json.JSONEncoder):
47
+ def default(self, obj):
48
+ item = getattr(obj, "item", None)
49
+ if callable(item):
50
+ try:
51
+ return _coerce_scalar(item())
52
+ except (TypeError, ValueError):
53
+ pass
54
+ tolist = getattr(obj, "tolist", None)
55
+ if callable(tolist):
56
+ try:
57
+ return _scrub_nan(tolist())
58
+ except (TypeError, ValueError):
59
+ pass
60
+ return super().default(obj)
61
+
62
+ def iterencode(self, o, _one_shot=False):
63
+ return super().iterencode(_scrub_nan(o), _one_shot=_one_shot)
@@ -0,0 +1,22 @@
1
+ """
2
+ Module-level widget classes for the asynchronous API.
3
+
4
+ All widget classes are built once at import time from the shared
5
+ definitions. They can be imported and subclassed normally::
6
+
7
+ from pgwidgets.async_.Widgets import FileDialog, Button
8
+
9
+ class MyFileDialog(FileDialog):
10
+ async def pick_file(self):
11
+ await self.open()
12
+
13
+ Instances are created through the ``get_widgets()`` factory on a
14
+ Session, which binds them to a session and handles constructor
15
+ argument parsing, state tracking, and callback registration.
16
+ """
17
+
18
+ from pgwidgets.async_.widget import build_all_widget_classes, Widget
19
+
20
+ _classes = build_all_widget_classes()
21
+ globals().update(_classes)
22
+ __all__ = list(_classes.keys()) + ["Widget"]