TypeDAL 3.16.4__py3-none-any.whl → 3.17.0__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__ = "3.16.4"
8
+ __version__ = "3.17.0"
typedal/__init__.py CHANGED
@@ -3,11 +3,28 @@ TypeDAL Library.
3
3
  """
4
4
 
5
5
  from . import fields
6
- from .core import Relationship, TypeDAL, TypedField, TypedRows, TypedTable, relationship
6
+ from .core import (
7
+ Relationship,
8
+ TypeDAL,
9
+ TypedField,
10
+ TypedRows,
11
+ TypedTable,
12
+ relationship,
13
+ )
14
+ from .helpers import sql_expression
7
15
 
8
16
  try:
9
17
  from .for_py4web import DAL as P4W_DAL
10
18
  except ImportError: # pragma: no cover
11
19
  P4W_DAL = None # type: ignore
12
20
 
13
- __all__ = ["Relationship", "TypeDAL", "TypedField", "TypedRows", "TypedTable", "fields", "relationship"]
21
+ __all__ = [
22
+ "Relationship",
23
+ "TypeDAL",
24
+ "TypedField",
25
+ "TypedRows",
26
+ "TypedTable",
27
+ "fields",
28
+ "relationship",
29
+ "sql_expression",
30
+ ]
typedal/cli.py CHANGED
@@ -392,7 +392,8 @@ def fake_migrations(
392
392
 
393
393
  previously_migrated = (
394
394
  db(
395
- db.ewh_implemented_features.name.belongs(to_fake) & (db.ewh_implemented_features.installed == True) # noqa E712
395
+ db.ewh_implemented_features.name.belongs(to_fake)
396
+ & (db.ewh_implemented_features.installed == True) # noqa E712
396
397
  )
397
398
  .select(db.ewh_implemented_features.name)
398
399
  .column("name")
typedal/core.py CHANGED
@@ -25,10 +25,12 @@ from typing import Any, Optional, Type
25
25
 
26
26
  import pydal
27
27
  from pydal._globals import DEFAULT
28
- from pydal.objects import Field as _Field
29
- from pydal.objects import Query as _Query
28
+
29
+ # from pydal.objects import Field as _Field
30
+ # from pydal.objects import Query as _Query
30
31
  from pydal.objects import Row
31
- from pydal.objects import Table as _Table
32
+
33
+ # from pydal.objects import Table as _Table
32
34
  from typing_extensions import Self, Unpack
33
35
 
34
36
  from .config import TypeDALConfig, load_config
@@ -45,6 +47,7 @@ from .helpers import (
45
47
  looks_like,
46
48
  mktable,
47
49
  origin_is_subclass,
50
+ sql_expression,
48
51
  to_snake,
49
52
  unwrap_type,
50
53
  )
@@ -71,7 +74,7 @@ from .types import (
71
74
 
72
75
  # use typing.cast(type, ...) to make mypy happy with unions
73
76
  T_annotation = Type[Any] | types.UnionType
74
- T_Query = typing.Union["Table", Query, bool, None, "TypedTable", Type["TypedTable"]]
77
+ T_Query = typing.Union["Table", Query, bool, None, "TypedTable", Type["TypedTable"], Expression]
75
78
  T_Value = typing.TypeVar("T_Value") # actual type of the Field (via Generic)
76
79
  T_MetaInstance = typing.TypeVar("T_MetaInstance", bound="TypedTable") # bound="TypedTable"; bound="TableMeta"
77
80
  T = typing.TypeVar("T")
@@ -132,7 +135,7 @@ class Relationship(typing.Generic[To_Type]):
132
135
  Define a relationship to another table.
133
136
  """
134
137
 
135
- _type: To_Type
138
+ _type: Type[To_Type]
136
139
  table: Type["TypedTable"] | type | str
137
140
  condition: Condition
138
141
  condition_and: Condition
@@ -142,7 +145,7 @@ class Relationship(typing.Generic[To_Type]):
142
145
 
143
146
  def __init__(
144
147
  self,
145
- _type: To_Type,
148
+ _type: Type[To_Type],
146
149
  condition: Condition = None,
147
150
  join: JOIN_OPTIONS = None,
148
151
  on: OnQuery = None,
@@ -165,7 +168,7 @@ class Relationship(typing.Generic[To_Type]):
165
168
  self.table = unwrap_type(args[0])
166
169
  self.multiple = True
167
170
  else:
168
- self.table = _type
171
+ self.table = typing.cast(type[TypedTable], _type)
169
172
  self.multiple = False
170
173
 
171
174
  if isinstance(self.table, str):
@@ -234,7 +237,7 @@ class Relationship(typing.Generic[To_Type]):
234
237
 
235
238
  return str(table)
236
239
 
237
- def __get__(self, instance: Any, owner: Any) -> typing.Optional[list[Any]] | "Relationship[To_Type]":
240
+ def __get__(self, instance: Any, owner: Any) -> "typing.Optional[list[Any]] | Relationship[To_Type]":
238
241
  """
239
242
  Relationship is a descriptor class, which can be returned from a class but not an instance.
240
243
 
@@ -756,7 +759,7 @@ class TypeDAL(pydal.DAL): # type: ignore
756
759
  if mapping := BASIC_MAPPINGS.get(ftype):
757
760
  # basi types
758
761
  return mapping
759
- elif isinstance(ftype, _Table):
762
+ elif isinstance(ftype, pydal.objects.Table):
760
763
  # db.table
761
764
  return f"reference {ftype._tablename}"
762
765
  elif issubclass(type(ftype), type) and issubclass(ftype, TypedTable):
@@ -829,13 +832,34 @@ class TypeDAL(pydal.DAL): # type: ignore
829
832
  """
830
833
  return to_snake(camel)
831
834
 
835
+ def sql_expression(
836
+ self,
837
+ sql_fragment: str,
838
+ *raw_args: str,
839
+ output_type: str | None = None,
840
+ **raw_kwargs: str,
841
+ ) -> str:
842
+ """
843
+ Creates a pydal Expression object representing a raw SQL fragment.
844
+
845
+ Args:
846
+ sql_fragment: The raw SQL fragment.
847
+ *raw_args: Arguments to be interpolated into the SQL fragment.
848
+ output_type: The expected output type of the expression.
849
+ **raw_kwargs: Keyword arguments to be interpolated into the SQL fragment.
850
+
851
+ Returns:
852
+ A pydal Expression object.
853
+ """
854
+ return sql_expression(self, sql_fragment, *raw_args, output_type=output_type, **raw_kwargs)
855
+
832
856
 
833
857
  def default_representer(field: TypedField[T], value: T, table: Type[TypedTable]) -> str:
834
858
  """
835
859
  Simply call field.represent on the value.
836
860
  """
837
861
  if represent := getattr(field, "represent", None):
838
- return field.represent(value, table)
862
+ return str(represent(value, table))
839
863
  else:
840
864
  return repr(value)
841
865
 
@@ -848,7 +872,7 @@ R = typing.TypeVar("R")
848
872
 
849
873
  def reorder_fields(
850
874
  table: pydal.objects.Table,
851
- fields: typing.Iterable[str | Field | TypedField],
875
+ fields: typing.Iterable[str | Field | TypedField[Any]],
852
876
  keep_others: bool = True,
853
877
  ) -> None:
854
878
  """
@@ -1423,7 +1447,7 @@ class TableMeta(type):
1423
1447
  """
1424
1448
  return cls._hook_once(cls._after_delete, fn)
1425
1449
 
1426
- def reorder_fields(cls, *fields: str | Field | TypedField, keep_others: bool = True):
1450
+ def reorder_fields(cls, *fields: str | Field | TypedField[Any], keep_others: bool = True) -> None:
1427
1451
  """
1428
1452
  Reorder fields of a typedal table.
1429
1453
 
@@ -1433,7 +1457,6 @@ class TableMeta(type):
1433
1457
  - True (default): keep other fields at the end, in their original order.
1434
1458
  - False: remove other fields (only keep what's specified).
1435
1459
  """
1436
-
1437
1460
  return reorder_fields(cls._table, fields, keep_others=keep_others)
1438
1461
 
1439
1462
 
@@ -1770,13 +1793,13 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
1770
1793
 
1771
1794
  raise AttributeError(item)
1772
1795
 
1773
- def keys(self):
1796
+ def keys(self) -> list[str]:
1774
1797
  """
1775
1798
  Return the combination of row + relationship keys.
1776
1799
 
1777
1800
  Used by dict(row).
1778
1801
  """
1779
- return list(self._row.keys()) + getattr(self, "_with", [])
1802
+ return list(self._row.keys() if self._row else ()) + getattr(self, "_with", [])
1780
1803
 
1781
1804
  def get(self, item: str, default: Any = None) -> Any:
1782
1805
  """
@@ -2037,7 +2060,17 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
2037
2060
 
2038
2061
  return pydal2sql.generate_sql(cls)
2039
2062
 
2040
- def render(self, fields=None, compact=False) -> Self:
2063
+ def render(self, fields: list[Field] = None, compact: bool = False) -> Self:
2064
+ """
2065
+ Renders a copy of the object with potentially modified values.
2066
+
2067
+ Args:
2068
+ fields: A list of fields to render. Defaults to all representable fields in the table.
2069
+ compact: Whether to return only the value of the first field if there is only one field.
2070
+
2071
+ Returns:
2072
+ A copy of the object with potentially modified values.
2073
+ """
2041
2074
  row = copy.deepcopy(self)
2042
2075
  keys = list(row)
2043
2076
  if not fields:
@@ -2057,6 +2090,8 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
2057
2090
  for relation_name in getattr(row, "_with", []):
2058
2091
  if relation := self._relationships.get(relation_name):
2059
2092
  relation_table = relation.table
2093
+ if isinstance(relation_table, str):
2094
+ relation_table = self._db[relation_table]
2060
2095
 
2061
2096
  relation_row = row[relation_name]
2062
2097
 
@@ -2089,7 +2124,7 @@ class TypedTable(_TypedTable, metaclass=TableMeta):
2089
2124
  )
2090
2125
 
2091
2126
  if compact and len(keys) == 1 and keys[0] != "_extra": # pragma: no cover
2092
- return row[keys[0]]
2127
+ return typing.cast(Self, row[keys[0]])
2093
2128
  return row
2094
2129
 
2095
2130
 
@@ -2288,11 +2323,11 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
2288
2323
 
2289
2324
  def as_dict(
2290
2325
  self,
2291
- key: str | Field = None,
2326
+ key: str | Field | None = None,
2292
2327
  compact: bool = False,
2293
2328
  storage_to_dict: bool = False,
2294
2329
  datetime_to_str: bool = False,
2295
- custom_types: list[type] = None,
2330
+ custom_types: list[type] | None = None,
2296
2331
  ) -> dict[int, AnyDict]:
2297
2332
  """
2298
2333
  Get the data in a dict of dicts.
@@ -2466,10 +2501,12 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
2466
2501
  self.__dict__.update(state)
2467
2502
  # db etc. set after undill by caching.py
2468
2503
 
2469
- def render(self, i=None, fields=None) -> typing.Generator[T_MetaInstance, None, None]:
2504
+ def render(
2505
+ self, i: int | None = None, fields: list[Field] | None = None
2506
+ ) -> typing.Generator[T_MetaInstance, None, None]:
2470
2507
  """
2471
- Takes an index and returns a copy of the indexed row with values
2472
- transformed via the "represent" attributes of the associated fields.
2508
+ Takes an index and returns a copy of the indexed row with values \
2509
+ transformed via the "represent" attributes of the associated fields.
2473
2510
 
2474
2511
  Args:
2475
2512
  i: index. If not specified, a generator is returned for iteration
@@ -2479,7 +2516,7 @@ class TypedRows(typing.Collection[T_MetaInstance], Rows):
2479
2516
  """
2480
2517
  if i is None:
2481
2518
  # difference: uses .keys() instead of index
2482
- return (self.render(i, fields=fields) for i in self.records.keys())
2519
+ return (self.render(i, fields=fields) for i in self.records)
2483
2520
 
2484
2521
  if not self.db.has_representer("rows_render"): # pragma: no cover
2485
2522
  raise RuntimeError(
@@ -2501,10 +2538,10 @@ from .caching import ( # noqa: E402
2501
2538
  )
2502
2539
 
2503
2540
 
2504
- def normalize_table_keys(row: Row, pattern: re.Pattern = re.compile(r"^([a-zA-Z_]+)_(\d{5,})$")) -> Row:
2541
+ def normalize_table_keys(row: Row, pattern: re.Pattern[str] = re.compile(r"^([a-zA-Z_]+)_(\d{5,})$")) -> Row:
2505
2542
  """
2506
- Normalize table keys in a PyDAL Row object by stripping numeric hash suffixes
2507
- from table names, only if the suffix is 5 or more digits.
2543
+ Normalize table keys in a PyDAL Row object by stripping numeric hash suffixes from table names, \
2544
+ only if the suffix is 5 or more digits.
2508
2545
 
2509
2546
  For example:
2510
2547
  Row({'articles_12345': {...}}) -> Row({'articles': {...}})
@@ -2643,7 +2680,7 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2643
2680
 
2644
2681
  def where(
2645
2682
  self,
2646
- *queries_or_lambdas: Query | typing.Callable[[Type[T_MetaInstance]], Query] | dict,
2683
+ *queries_or_lambdas: Query | typing.Callable[[Type[T_MetaInstance]], Query] | dict[str, Any],
2647
2684
  **filters: Any,
2648
2685
  ) -> "QueryBuilder[T_MetaInstance]":
2649
2686
  """
@@ -2667,15 +2704,15 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2667
2704
  filters,
2668
2705
  )
2669
2706
 
2670
- subquery: DummyQuery | Query = DummyQuery()
2707
+ subquery = typing.cast(Query, DummyQuery())
2671
2708
  for query_part in queries_or_lambdas:
2672
- if isinstance(query_part, _Query):
2709
+ if isinstance(query_part, (Field, pydal.objects.Field)) or is_typed_field(query_part):
2710
+ subquery |= typing.cast(Query, query_part != None)
2711
+ elif isinstance(query_part, (pydal.objects.Query, Expression, pydal.objects.Expression)):
2673
2712
  subquery |= typing.cast(Query, query_part)
2674
2713
  elif callable(query_part):
2675
2714
  if result := query_part(self.model):
2676
2715
  subquery |= result
2677
- elif isinstance(query_part, (Field, _Field)) or is_typed_field(query_part):
2678
- subquery |= typing.cast(Query, query_part != None)
2679
2716
  elif isinstance(query_part, dict):
2680
2717
  subsubquery = DummyQuery()
2681
2718
  for field, value in query_part.items():
@@ -2725,8 +2762,9 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2725
2762
  if isinstance(condition, pydal.objects.Query):
2726
2763
  condition = as_lambda(condition)
2727
2764
 
2765
+ to_field = typing.cast(Type[TypedTable], fields[0])
2728
2766
  relationships = {
2729
- str(fields[0]): Relationship(fields[0], condition=condition, join=method, condition_and=condition_and)
2767
+ str(to_field): Relationship(to_field, condition=condition, join=method, condition_and=condition_and)
2730
2768
  }
2731
2769
  elif on:
2732
2770
  if len(fields) != 1:
@@ -2737,7 +2775,9 @@ class QueryBuilder(typing.Generic[T_MetaInstance]):
2737
2775
 
2738
2776
  if isinstance(on, list):
2739
2777
  on = as_lambda(on)
2740
- relationships = {str(fields[0]): Relationship(fields[0], on=on, join=method, condition_and=condition_and)}
2778
+
2779
+ to_field = typing.cast(Type[TypedTable], fields[0])
2780
+ relationships = {str(to_field): Relationship(to_field, on=on, join=method, condition_and=condition_and)}
2741
2781
 
2742
2782
  else:
2743
2783
  if fields:
typedal/fields.py CHANGED
@@ -256,18 +256,50 @@ def TimestampField(**kw: Unpack[FieldSettings]) -> TypedField[dt.datetime]:
256
256
  )
257
257
 
258
258
 
259
- def safe_decode_native_point(value: str | None):
259
+ def safe_decode_native_point(value: str | None) -> tuple[float, ...]:
260
+ """
261
+ Safely decode a string into a tuple of floats.
262
+
263
+ The function attempts to parse the input string using `ast.literal_eval`.
264
+ If the parsing is successful, the function casts the parsed value to a tuple of floats and returns it.
265
+ Otherwise, the function returns an empty tuple.
266
+
267
+ Args:
268
+ value: The string to decode.
269
+
270
+ Returns:
271
+ A tuple of floats.
272
+ """
260
273
  if not value:
261
274
  return ()
262
275
 
263
276
  try:
264
- return ast.literal_eval(value)
277
+ parsed = ast.literal_eval(value)
278
+ return typing.cast(tuple[float, ...], parsed)
265
279
  except ValueError: # pragma: no cover
266
280
  # should not happen when inserted with `safe_encode_native_point` but you never know
267
281
  return ()
268
282
 
269
283
 
270
- def safe_encode_native_point(value: tuple[str, str] | str) -> str:
284
+ def safe_encode_native_point(value: tuple[str, str] | tuple[float, float] | str) -> str:
285
+ """
286
+
287
+ Safe encodes a point value.
288
+
289
+ The function takes a point value as input.
290
+ It can be a string in the format "x,y" or a tuple of two numbers.
291
+ The function converts the string to a tuple if necessary, validates the tuple,
292
+ and formats it into the expected string format.
293
+
294
+ Args:
295
+ value: The point value to be encoded.
296
+
297
+ Returns:
298
+ The encoded point value as a string in the format "x,y".
299
+
300
+ Raises:
301
+ ValueError: If the input value is not a valid point.
302
+ """
271
303
  if not value:
272
304
  return ""
273
305
 
@@ -276,13 +308,15 @@ def safe_encode_native_point(value: tuple[str, str] | str) -> str:
276
308
  value = value.strip("() ")
277
309
  if not value:
278
310
  return ""
279
- value = tuple(float(x.strip()) for x in value.split(","))
311
+ value_tup = tuple(float(x.strip()) for x in value.split(","))
312
+ else:
313
+ value_tup = value # type: ignore
280
314
 
281
315
  # Validate and format
282
- if len(value) != 2:
316
+ if len(value_tup) != 2:
283
317
  raise ValueError("Point must have exactly 2 coordinates")
284
318
 
285
- x, y = value
319
+ x, y = value_tup
286
320
  return f"({x},{y})"
287
321
 
288
322
 
typedal/helpers.py CHANGED
@@ -2,6 +2,8 @@
2
2
  Helpers that work independently of core.
3
3
  """
4
4
 
5
+ from __future__ import annotations
6
+
5
7
  import datetime as dt
6
8
  import fnmatch
7
9
  import io
@@ -12,10 +14,10 @@ from typing import Any
12
14
 
13
15
  from pydal import DAL
14
16
 
15
- from .types import AnyDict, Field, Table
17
+ from .types import AnyDict, Expression, Field, Table
16
18
 
17
19
  if typing.TYPE_CHECKING:
18
- from . import TypeDAL, TypedField, TypedTable # noqa: F401
20
+ from . import TypeDAL, TypedField, TypedTable
19
21
 
20
22
  T = typing.TypeVar("T")
21
23
 
@@ -101,12 +103,14 @@ def origin_is_subclass(obj: Any, _type: type) -> bool:
101
103
  return bool(
102
104
  typing.get_origin(obj)
103
105
  and isinstance(typing.get_origin(obj), type)
104
- and issubclass(typing.get_origin(obj), _type)
106
+ and issubclass(typing.get_origin(obj), _type),
105
107
  )
106
108
 
107
109
 
108
110
  def mktable(
109
- data: dict[Any, Any], header: typing.Optional[typing.Iterable[str] | range] = None, skip_first: bool = True
111
+ data: dict[Any, Any],
112
+ header: typing.Optional[typing.Iterable[str] | range] = None,
113
+ skip_first: bool = True,
110
114
  ) -> str:
111
115
  """
112
116
  Display a table for 'data'.
@@ -331,3 +335,61 @@ class classproperty:
331
335
  The value returned by the function.
332
336
  """
333
337
  return self.fget(owner)
338
+
339
+
340
+ def sql_escape(db: TypeDAL, sql_fragment: str, *raw_args: Any, **raw_kwargs: Any) -> str:
341
+ """
342
+ Generates escaped SQL fragments with placeholders.
343
+
344
+ Args:
345
+ db: Database object.
346
+ sql_fragment: SQL fragment with placeholders.
347
+ *raw_args: Positional arguments to be escaped.
348
+ **raw_kwargs: Keyword arguments to be escaped.
349
+
350
+ Returns:
351
+ Escaped SQL fragment with placeholders replaced with escaped values.
352
+
353
+ Raises:
354
+ ValueError: If both args and kwargs are provided.
355
+ """
356
+ if raw_args and raw_kwargs: # pragma: no cover
357
+ raise ValueError("Please provide either args or kwargs, not both.")
358
+
359
+ elif raw_args:
360
+ # list
361
+ return sql_fragment % tuple(db._adapter.adapt(placeholder) for placeholder in raw_args)
362
+ else:
363
+ # dict
364
+ return sql_fragment % {key: db._adapter.adapt(placeholder) for key, placeholder in raw_kwargs.items()}
365
+
366
+
367
+ def sql_expression(
368
+ db: TypeDAL,
369
+ sql_fragment: str,
370
+ *raw_args: str,
371
+ output_type: str | None = None,
372
+ **raw_kwargs: str,
373
+ ) -> Expression:
374
+ """
375
+ Creates a pydal Expression object representing a raw SQL fragment.
376
+
377
+ Args:
378
+ db: The TypeDAL object.
379
+ sql_fragment: The raw SQL fragment.
380
+ *raw_args: Arguments to be interpolated into the SQL fragment.
381
+ output_type: The expected output type of the expression.
382
+ **raw_kwargs: Keyword arguments to be interpolated into the SQL fragment.
383
+
384
+ Returns:
385
+ A pydal Expression object.
386
+ """
387
+ safe_sql = sql_escape(db, sql_fragment, *raw_args, **raw_kwargs)
388
+
389
+ # create a pydal Expression wrapping a raw SQL fragment + placeholders
390
+ return Expression(
391
+ db,
392
+ db._adapter.dialect.raw,
393
+ safe_sql,
394
+ type=output_type, # optional type hint
395
+ )
typedal/mixins.py CHANGED
@@ -180,7 +180,7 @@ class SlugMixin(Mixin):
180
180
  if slug_field is None:
181
181
  raise ValueError(
182
182
  "SlugMixin requires a valid slug_field setting: "
183
- "e.g. `class MyClass(TypedTable, SlugMixin, slug_field='title'): ...`"
183
+ "e.g. `class MyClass(TypedTable, SlugMixin, slug_field='title'): ...`",
184
184
  )
185
185
 
186
186
  if slug_suffix:
@@ -197,7 +197,7 @@ class SlugMixin(Mixin):
197
197
 
198
198
  @classmethod
199
199
  def __generate_slug_before_insert(cls, row: OpRow) -> None:
200
- if row.get("slug"):
200
+ if row.get("slug"): # type: ignore
201
201
  # manually set -> skip
202
202
  return None
203
203
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: TypeDAL
3
- Version: 3.16.4
3
+ Version: 3.17.0
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
@@ -0,0 +1,19 @@
1
+ typedal/__about__.py,sha256=6xOJ9nMOnP3X9kALmHamZ6MhTwflJ564cHVnjqBINXg,207
2
+ typedal/__init__.py,sha256=mCU8C3xPYiGcai4PYIoZNJSkFa1jb0MHkV8s2XXqEeY,484
3
+ typedal/caching.py,sha256=6YUzUMpan56nSy3D-Jl0FS-8V4LbTnpRSoDJHj6yPYo,11782
4
+ typedal/cli.py,sha256=e08L8k6q1NGSzpKs7ywin0uwkK7Kz07I4REVjHdbyyE,19267
5
+ typedal/config.py,sha256=0qy1zrTUdtmXPM9jHzFnSR1DJsqGJqcdG6pvhzKQHe0,11625
6
+ typedal/core.py,sha256=eFcm3wyjydYpP3cEfoGz1jUVzaNBzRSoYSScYweZRmE,117701
7
+ typedal/fields.py,sha256=bZIgjl3Lj7eMqFCyt-bogsS_BqIs3cETEUH4W59qiXw,8425
8
+ typedal/for_py4web.py,sha256=KIIu8XgnAfRQCJfZCra79k8SInOHiFuLDKUv3hzTJng,1908
9
+ typedal/for_web2py.py,sha256=xn7zo6ImsmTkH6LacbjLQl2oqyBvP0zLqRxEJvMQk1w,1929
10
+ typedal/helpers.py,sha256=eQjH3JpL1MPqMCcaFtj01tj0LxdwdT2zbnvKlDnXo5E,10702
11
+ typedal/mixins.py,sha256=NiGp-3Lr1lllWjP-whUhc_2HXwCS_ROB0bs5N_s9wk4,7976
12
+ typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
+ typedal/types.py,sha256=1FIgv1s0be0E8r5Wd9E1nvDvK4ETV8u2NlfI7_P6UUY,6752
14
+ typedal/web2py_py4web_shared.py,sha256=UYmD0_aK1bSVBt_f3j59Mxq-zOmQNkYkb8sPDUibq68,1539
15
+ typedal/serializers/as_json.py,sha256=3JZlFhPrdvZVFAmH7P5DUAz8-TIk-br0F1CjKG3PFDM,2246
16
+ typedal-3.17.0.dist-info/METADATA,sha256=SdNlSCbh3PyB8TUqVf_lE-x8r57BW4zUDijqcyLCR-w,10461
17
+ typedal-3.17.0.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
+ typedal-3.17.0.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
19
+ typedal-3.17.0.dist-info/RECORD,,
@@ -1,19 +0,0 @@
1
- typedal/__about__.py,sha256=b4WRvaxkT5bkr-NnSsTloyIQ-ekyL970tZv3Hsazu-s,207
2
- typedal/__init__.py,sha256=Y6LT5UE3HrfWND_drJddYFbDjfnvqQER8MxZiEREFGw,366
3
- typedal/caching.py,sha256=6YUzUMpan56nSy3D-Jl0FS-8V4LbTnpRSoDJHj6yPYo,11782
4
- typedal/cli.py,sha256=SnWceLPDd-t90VPHAV9O3RR7JZtpVniT55TqtrRv3VM,19255
5
- typedal/config.py,sha256=0qy1zrTUdtmXPM9jHzFnSR1DJsqGJqcdG6pvhzKQHe0,11625
6
- typedal/core.py,sha256=FGrxs8PCTO8mTGD4RjGRWxG4dLjrqECJPG1nNfqWkRA,116013
7
- typedal/fields.py,sha256=oOmTonXG-g4Lpj5_gSr8GJ-EZIEqO435Fm8-MS_cmoc,7356
8
- typedal/for_py4web.py,sha256=KIIu8XgnAfRQCJfZCra79k8SInOHiFuLDKUv3hzTJng,1908
9
- typedal/for_web2py.py,sha256=xn7zo6ImsmTkH6LacbjLQl2oqyBvP0zLqRxEJvMQk1w,1929
10
- typedal/helpers.py,sha256=LpBgTwKmt9f1b4Mz98mxusvIHGiCpW_abDMZLP81g6Y,8850
11
- typedal/mixins.py,sha256=p8OAx1N9MVEF4bhdxB_cC-GTczXzt76OMl_KBKtMYAU,7959
12
- typedal/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
13
- typedal/types.py,sha256=1FIgv1s0be0E8r5Wd9E1nvDvK4ETV8u2NlfI7_P6UUY,6752
14
- typedal/web2py_py4web_shared.py,sha256=UYmD0_aK1bSVBt_f3j59Mxq-zOmQNkYkb8sPDUibq68,1539
15
- typedal/serializers/as_json.py,sha256=3JZlFhPrdvZVFAmH7P5DUAz8-TIk-br0F1CjKG3PFDM,2246
16
- typedal-3.16.4.dist-info/METADATA,sha256=Vx98N5MXgzHzLrx79BWgQc-YQomJue_8T_zFmU3t5ZI,10461
17
- typedal-3.16.4.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
18
- typedal-3.16.4.dist-info/entry_points.txt,sha256=m1wqcc_10rHWPdlQ71zEkmJDADUAnZtn7Jac_6mbyUc,44
19
- typedal-3.16.4.dist-info/RECORD,,