TypeDAL 2.3.0__py3-none-any.whl → 2.3.2__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.

Potentially problematic release.


This version of TypeDAL might be problematic. Click here for more details.

typedal/__about__.py CHANGED
@@ -5,4 +5,4 @@ This file contains the Version info for this package.
5
5
  # SPDX-FileCopyrightText: 2023-present Robin van der Noord <robinvandernoord@gmail.com>
6
6
  #
7
7
  # SPDX-License-Identifier: MIT
8
- __version__ = "2.3.0"
8
+ __version__ = "2.3.2"
typedal/core.py CHANGED
@@ -39,6 +39,7 @@ from .helpers import (
39
39
  to_snake,
40
40
  unwrap_type,
41
41
  )
42
+ from .serializers import as_json
42
43
  from .types import (
43
44
  AfterDeleteCallable,
44
45
  AfterInsertCallable,
@@ -485,7 +486,9 @@ class TypeDAL(pydal.DAL): # type: ignore
485
486
  # ! dont' use full_dict here:
486
487
  other_kwargs = kwargs | {
487
488
  k: v for k, v in cls.__dict__.items() if k not in annotations and not k.startswith("_")
488
- }
489
+ } # other_kwargs was previously used to pass kwargs to typedal, but use @define(**kwargs) for that.
490
+ # now it's only used to extract relationships from the object.
491
+ # other properties of the class (incl methods) should not be touched
489
492
 
490
493
  for key in typedfields.keys() - full_dict.keys():
491
494
  # typed fields that don't haven't been added to the object yet
@@ -512,9 +515,9 @@ class TypeDAL(pydal.DAL): # type: ignore
512
515
  if k not in relationships and (new_relationship := to_relationship(cls, k, annotations[k]))
513
516
  }
514
517
 
515
- cache_dependency = other_kwargs.pop("cache_dependency", True)
518
+ cache_dependency = kwargs.pop("cache_dependency", True)
516
519
 
517
- table: Table = self.define_table(tablename, *fields.values(), **other_kwargs)
520
+ table: Table = self.define_table(tablename, *fields.values(), **kwargs)
518
521
 
519
522
  for name, typed_field in typedfields.items():
520
523
  field = fields[name]
@@ -1495,7 +1498,7 @@ class TypedTable(metaclass=TableMeta):
1495
1498
  return typing.cast(dict[str, Any], result)
1496
1499
 
1497
1500
  @classmethod
1498
- def as_json(cls, sanitize: bool = True) -> str:
1501
+ def as_json(cls, sanitize: bool = True, indent: Optional[int] = None, **kwargs: Any) -> str:
1499
1502
  """
1500
1503
  Dump the object to json.
1501
1504
 
@@ -1503,8 +1506,8 @@ class TypedTable(metaclass=TableMeta):
1503
1506
  - dumps the table info if it's a class
1504
1507
  - dumps the row info if it's an instance (see _as_json)
1505
1508
  """
1506
- table = cls._ensure_table_defined()
1507
- return typing.cast(str, table.as_json(sanitize))
1509
+ data = cls.as_dict(sanitize=sanitize)
1510
+ return as_json.encode(data, indent=indent, **kwargs)
1508
1511
 
1509
1512
  @classmethod
1510
1513
  def as_xml(cls, sanitize: bool = True) -> str: # pragma: no cover
@@ -1534,15 +1537,25 @@ class TypedTable(metaclass=TableMeta):
1534
1537
  self, datetime_to_str: bool = False, custom_types: typing.Iterable[type] | type | None = None
1535
1538
  ) -> dict[str, Any]:
1536
1539
  row = self._ensure_matching_row()
1540
+
1537
1541
  result = row.as_dict(datetime_to_str=datetime_to_str, custom_types=custom_types)
1538
1542
 
1543
+ def asdict_method(obj: Any) -> Any: # pragma: no cover
1544
+ if hasattr(obj, "_as_dict"): # typedal
1545
+ return obj._as_dict()
1546
+ elif hasattr(obj, "as_dict"): # pydal
1547
+ return obj.as_dict()
1548
+ else: # something else??
1549
+ return obj.__dict__
1550
+
1539
1551
  if _with := getattr(self, "_with", None):
1540
1552
  for relationship in _with:
1541
1553
  data = self.get(relationship)
1554
+
1542
1555
  if isinstance(data, list):
1543
- data = [_.as_dict() if getattr(_, "as_dict", None) else _ for _ in data]
1556
+ data = [asdict_method(_) for _ in data]
1544
1557
  elif data:
1545
- data = data.as_dict()
1558
+ data = asdict_method(data)
1546
1559
 
1547
1560
  result[relationship] = data
1548
1561
 
@@ -1550,14 +1563,12 @@ class TypedTable(metaclass=TableMeta):
1550
1563
 
1551
1564
  def _as_json(
1552
1565
  self,
1553
- mode: str = "object",
1554
1566
  default: typing.Callable[[Any], Any] = None,
1555
- colnames: list[str] = None,
1556
- serialize: bool = True,
1567
+ indent: Optional[int] = None,
1557
1568
  **kwargs: Any,
1558
1569
  ) -> str:
1559
- row = self._ensure_matching_row()
1560
- return typing.cast(str, row.as_json(mode, default, colnames, serialize, *kwargs))
1570
+ data = self._as_dict()
1571
+ return as_json.encode(data, default=default, indent=indent, **kwargs)
1561
1572
 
1562
1573
  def _as_xml(self, sanitize: bool = True) -> str: # pragma: no cover
1563
1574
  row = self._ensure_matching_row()
@@ -1635,14 +1646,6 @@ class TypedTable(metaclass=TableMeta):
1635
1646
  # __del__ is also called on the end of a scope so don't remove records on every del!!
1636
1647
 
1637
1648
  # pickling:
1638
- def __setstate__(self, state: dict[str, Any]) -> None:
1639
- """
1640
- Used by dill when loading from a bytestring.
1641
- """
1642
- # as_dict also includes table info, so dump as json to only get the actual row data
1643
- # then create a new (more empty) row object:
1644
- state["_row"] = Row(json.loads(state["_row"]))
1645
- self.__dict__ |= state
1646
1649
 
1647
1650
  def __getstate__(self) -> dict[str, Any]:
1648
1651
  """
@@ -1655,6 +1658,7 @@ class TypedTable(metaclass=TableMeta):
1655
1658
  result: dict[str, Any] = row.as_dict()
1656
1659
 
1657
1660
  if _with := getattr(self, "_with", None):
1661
+ result["_with"] = _with
1658
1662
  for relationship in _with:
1659
1663
  data = self.get(relationship)
1660
1664
 
@@ -1663,6 +1667,15 @@ class TypedTable(metaclass=TableMeta):
1663
1667
  result["_row"] = self._row.as_json() if self._row else ""
1664
1668
  return result
1665
1669
 
1670
+ def __setstate__(self, state: dict[str, Any]) -> None:
1671
+ """
1672
+ Used by dill when loading from a bytestring.
1673
+ """
1674
+ # as_dict also includes table info, so dump as json to only get the actual row data
1675
+ # then create a new (more empty) row object:
1676
+ state["_row"] = Row(json.loads(state["_row"]))
1677
+ self.__dict__ |= state
1678
+
1666
1679
 
1667
1680
  # backwards compat:
1668
1681
  TypedRow = TypedTable
@@ -1862,17 +1875,19 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1862
1875
 
1863
1876
  return {k: v.as_dict() for k, v in self.records.items()}
1864
1877
 
1865
- def as_json(self, mode: str = "object", default: typing.Callable[[Any], Any] = None) -> str:
1878
+ def as_json(self, default: typing.Callable[[Any], Any] = None, indent: Optional[int] = None, **kwargs: Any) -> str:
1866
1879
  """
1867
1880
  Turn the data into a dict and then dump to JSON.
1868
1881
  """
1869
- return typing.cast(str, super().as_json(mode=mode, default=default))
1882
+ data = self.as_list()
1883
+
1884
+ return as_json.encode(data, default=default, indent=indent, **kwargs)
1870
1885
 
1871
- def json(self, mode: str = "object", default: typing.Callable[[Any], Any] = None) -> str:
1886
+ def json(self, default: typing.Callable[[Any], Any] = None, indent: Optional[int] = None, **kwargs: Any) -> str:
1872
1887
  """
1873
1888
  Turn the data into a dict and then dump to JSON.
1874
1889
  """
1875
- return typing.cast(str, super().as_json(mode=mode, default=default))
1890
+ return self.as_json(default=default, indent=indent, **kwargs)
1876
1891
 
1877
1892
  def as_list(
1878
1893
  self,
@@ -1888,6 +1903,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
1888
1903
  return typing.cast(
1889
1904
  list[dict[str, Any]], super().as_list(compact, storage_to_dict, datetime_to_str, custom_types)
1890
1905
  )
1906
+
1891
1907
  return [_.as_dict() for _ in self.records.values()]
1892
1908
 
1893
1909
  def __getitem__(self, item: int) -> T_MetaInstance:
@@ -0,0 +1,42 @@
1
+ """
2
+ Replacement for pydal's json serializer.
3
+ """
4
+
5
+ import datetime as dt
6
+ import json
7
+ from json import JSONEncoder
8
+ from typing import Any
9
+
10
+
11
+ class SerializedJson(JSONEncoder):
12
+ """
13
+ Custom encoder class with slightly improved defaults.
14
+ """
15
+
16
+ def default(self, o: Any) -> Any:
17
+ """
18
+ If no logic exists for a type yet, it is processed by this method.
19
+
20
+ It supports sets (turned into list), __json__ methods and will just str() otherwise.
21
+ """
22
+ if isinstance(o, set):
23
+ return list(o)
24
+ elif isinstance(o, dt.date):
25
+ return str(o)
26
+ elif hasattr(o, "__json__"):
27
+ if callable(o.__json__):
28
+ return o.__json__()
29
+ else:
30
+ return o.__json__
31
+ elif hasattr(o, "__dict__"):
32
+ return o.__dict__
33
+ else:
34
+ # warnings.warn(f"Unkown type {type(o)}")
35
+ return str(o)
36
+
37
+
38
+ def encode(something: Any, indent: int = None, **kw: Any) -> str:
39
+ """
40
+ Encode anything to JSON with some improved defaults.
41
+ """
42
+ return json.dumps(something, indent=indent, cls=SerializedJson, **kw)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: TypeDAL
3
- Version: 2.3.0
3
+ Version: 2.3.2
4
4
  Summary: Typing support for PyDAL
5
5
  Project-URL: Documentation, https://typedal.readthedocs.io/
6
6
  Project-URL: Issues, https://github.com/trialandsuccess/TypeDAL/issues
@@ -1,15 +1,16 @@
1
- typedal/__about__.py,sha256=PmuJ59D6ihUaOnWSryXIFc6p-q-9F-kjIa_42zL1oLs,206
1
+ typedal/__about__.py,sha256=6-cBVRoxdWtgxVEjZ7nYfLXipTL1o9Jjywbqts994pk,206
2
2
  typedal/__init__.py,sha256=QQpLiVl9w9hm2LBxey49Y_tCF_VB2bScVaS_mCjYy54,366
3
3
  typedal/caching.py,sha256=cglkCphbg93Iy9-KHefQN9-JJxuA5-HjpzZAdw2BGvY,7709
4
4
  typedal/cli.py,sha256=F7M9D2pu2D-FoKZJrT3K9Wmbv5_ScUR8j5jNGoTgOfk,11800
5
5
  typedal/config.py,sha256=oDYSuPMm261hBoDBAXoq37Umf1Vw8rNx4BlP35WwCSY,11882
6
- typedal/core.py,sha256=B4GZMHE3PEP2fOPT97UOz0nIFJU5y3cc8BGcJWVaphI,94750
6
+ typedal/core.py,sha256=wARmyKpwm-3ZGrt2ZdOUfBqfqbrWf_DgqWmaqAdmIow,95392
7
7
  typedal/fields.py,sha256=z2PD9vLWqBR_zXtiY0DthqTG4AeF3yxKoeuVfGXnSdg,5197
8
8
  typedal/for_py4web.py,sha256=kw44_55-dBWlYmepB7YEev5sj7tbYcgzvp-Ecc7K9_I,2230
9
9
  typedal/helpers.py,sha256=ZpHdwBMSANw-P9I5gs56Vf6GUbxGzFsIwbBvASKXX8s,6487
10
10
  typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  typedal/types.py,sha256=5qm3PgS8DXGCu9ZTUWQiIi2XXD8gz4_4Csg_vZlu_yo,3379
12
- typedal-2.3.0.dist-info/METADATA,sha256=5kiRyeukicOHgBdmfz-83VYEdXKkv4jHX8D_0jCPp2A,7623
13
- typedal-2.3.0.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
14
- typedal-2.3.0.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
15
- typedal-2.3.0.dist-info/RECORD,,
12
+ typedal/serializers/as_json.py,sha256=2sWSiU3yecPMhE-1jman227oLSfvSY-Iw-gbg3PnCKk,1138
13
+ typedal-2.3.2.dist-info/METADATA,sha256=uMe7GkSU2AyP23eFObW278v5dUdN4yit7fsi-nvhu9w,7623
14
+ typedal-2.3.2.dist-info/WHEEL,sha256=KGYbc1zXlYddvwxnNty23BeaKzh7YuoSIvIMO4jEhvw,87
15
+ typedal-2.3.2.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
16
+ typedal-2.3.2.dist-info/RECORD,,