database-wrapper 0.2.7__py3-none-any.whl → 0.2.9__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.
@@ -15,6 +15,7 @@ from .db_wrapper import DBWrapper
15
15
  from .db_wrapper_async import DBWrapperAsync
16
16
  from .serialization import SerializeType
17
17
  from .utils.dataclass_addons import ignore_unknown_kwargs
18
+ from .db_introspector import ColumnMetaIntrospector, DBIntrospector
18
19
 
19
20
  # Set the logger to a quiet default, can be enabled if needed
20
21
  logger = logging.getLogger("database_wrapper")
@@ -40,6 +41,8 @@ __all__ = [
40
41
  "utils",
41
42
  "SerializeType",
42
43
  "ignore_unknown_kwargs",
44
+ "ColumnMetaIntrospector",
45
+ "DBIntrospector",
43
46
  # Abstract classes
44
47
  "ConnectionABC",
45
48
  "CursorABC",
@@ -3,7 +3,7 @@ from typing import Any
3
3
  CONFIG: dict[str, Any] = {
4
4
  # These are supposed to be set automatically by a git pre-compile script
5
5
  # They are one git commit hash behind, if used automatically
6
- "git_commit_hash": "0bb23fda10ccaf5b4fd326e020c9bc14913fd56d",
7
- "git_commit_date": "19.06.2025 09:18",
8
- "app_version": "0.2.7",
6
+ "git_commit_hash": "aba940d794c0b9d2390f87bd32a86c13769311d7",
7
+ "git_commit_date": "10.10.2025 01:49",
8
+ "app_version": "0.2.9",
9
9
  }
@@ -185,9 +185,7 @@ class DBDataModel:
185
185
  for field_name, field_obj in self.__dataclass_fields__.items():
186
186
  metadata = cast(MetadataDict, field_obj.metadata)
187
187
  assert (
188
- "db_field" in metadata
189
- and isinstance(metadata["db_field"], tuple)
190
- and len(metadata["db_field"]) == 2
188
+ "db_field" in metadata and isinstance(metadata["db_field"], tuple) and len(metadata["db_field"]) == 2
191
189
  ), f"db_field metadata is not set for {field_name}"
192
190
  field_type: str = metadata["db_field"][1]
193
191
  schema["properties"][field_name] = {"type": field_type}
@@ -321,9 +319,7 @@ class DBDataModel:
321
319
  serialize = metadata.get("serialize", None)
322
320
  if serialize is not None:
323
321
  if isinstance(serialize, SerializeType):
324
- store_data[field_name] = serialize_value(
325
- store_data[field_name], serialize
326
- )
322
+ store_data[field_name] = serialize_value(store_data[field_name], serialize)
327
323
  else:
328
324
  store_data[field_name] = serialize(store_data[field_name])
329
325
 
@@ -347,9 +343,7 @@ class DBDataModel:
347
343
  serialize = metadata.get("serialize", None)
348
344
  if serialize is not None:
349
345
  if isinstance(serialize, SerializeType):
350
- update_data[field_name] = serialize_value(
351
- update_data[field_name], serialize
352
- )
346
+ update_data[field_name] = serialize_value(update_data[field_name], serialize)
353
347
  else:
354
348
  update_data[field_name] = serialize(update_data[field_name])
355
349
 
@@ -394,6 +388,26 @@ class DBDefaultsDataModel(DBDataModel):
394
388
  )
395
389
  """updated_at should be present in all tables and is updated automatically"""
396
390
 
391
+ disabled_at: datetime.datetime = field(
392
+ default_factory=datetime.datetime.now,
393
+ metadata={
394
+ "db_field": ("disabled_at", "timestamptz"),
395
+ "store": False,
396
+ "update": False,
397
+ "serialize": SerializeType.DATETIME,
398
+ },
399
+ )
400
+
401
+ deleted_at: datetime.datetime = field(
402
+ default_factory=datetime.datetime.now,
403
+ metadata={
404
+ "db_field": ("deleted_at", "timestamptz"),
405
+ "store": False,
406
+ "update": False,
407
+ "serialize": SerializeType.DATETIME,
408
+ },
409
+ )
410
+
397
411
  enabled: bool = field(
398
412
  default=True,
399
413
  metadata={
@@ -0,0 +1,397 @@
1
+ from dataclasses import MISSING, dataclass, make_dataclass, field, fields as dc_fields
2
+ from datetime import datetime, date
3
+ from enum import Enum
4
+ from pathlib import Path
5
+ from typing import Any, Optional, Type, Union, get_origin, get_args
6
+
7
+ from database_wrapper import DBDefaultsDataModel, MetadataDict, SerializeType
8
+
9
+
10
+ def type_to_str(t: Any) -> str:
11
+ """Render annotations like 'str | None' or 'RV4ProductionJobStatus | None'."""
12
+ origin = get_origin(t)
13
+ if origin is Union:
14
+ args = list(get_args(t))
15
+ # Optional[T] -> Union[T, NoneType]
16
+ if type(None) in args and len(args) == 2:
17
+ other = args[0] if args[1] is type(None) else args[1]
18
+ return f"{type_to_str(other)} | None"
19
+ # General Union
20
+ return " | ".join(type_to_str(a) for a in args)
21
+ if hasattr(t, "__name__"):
22
+ return t.__name__
23
+ if getattr(t, "__module__", None) and getattr(t, "__qualname__", None):
24
+ return f"{t.__qualname__}"
25
+ return str(t)
26
+
27
+
28
+ def _make_enum(name: str, labels: list[str]) -> Enum:
29
+ # Normalize labels to valid identifiers
30
+ members = {}
31
+ for raw in labels:
32
+ key = raw.upper().replace(" ", "_")
33
+ # avoid starting with digit
34
+ if key and key[0].isdigit():
35
+ key = f"_{key}"
36
+ members[key] = raw
37
+ return Enum(name, members)
38
+
39
+
40
+ @dataclass(frozen=True)
41
+ class ColumnMetaIntrospector:
42
+ col_name: str
43
+ db_type: str
44
+ is_nullable: bool
45
+ has_default: bool
46
+ default_expr: str | None = None
47
+ enum_labels: list[str] | None = None
48
+
49
+
50
+ class DBIntrospector:
51
+ conn: Any
52
+
53
+ def __init__(self, dbCursor: Any = None):
54
+ self.conn = dbCursor
55
+
56
+ @staticmethod
57
+ def _default_class_name(schema: str, table: str) -> str:
58
+ # Very naive PascalCase maker
59
+ def pascal(s: str) -> str:
60
+ return "".join(p.capitalize() for p in s.replace("-", "_").split("_"))
61
+
62
+ return f"{pascal(schema)}{pascal(table)}"
63
+
64
+ def get_table_columns(self, schema: str, table: str) -> list[ColumnMetaIntrospector]:
65
+ raise NotImplementedError
66
+
67
+ def map_db_type(self, db_type: str) -> str:
68
+ raise NotImplementedError
69
+
70
+ def get_schema_table_name(self, full_table: str) -> tuple[str, str]:
71
+ (schema, table) = full_table.split(".") if "." in full_table else ("public", full_table)
72
+ return (schema, table)
73
+
74
+ def is_meta_field(self, col_name: str) -> bool:
75
+ """
76
+ Return True if the column is a common meta field we want to skip.
77
+
78
+ Args:
79
+ col_name: The name of the column
80
+ """
81
+
82
+ return (
83
+ col_name == "id"
84
+ or col_name == "created_at"
85
+ or col_name == "updated_at"
86
+ or col_name == "disabled_at"
87
+ or col_name == "deleted_at"
88
+ or col_name == "enabled"
89
+ or col_name == "deleted"
90
+ )
91
+
92
+ def generate_dataclass(
93
+ self,
94
+ table_name: str,
95
+ *,
96
+ class_name: Optional[str] = None,
97
+ base: Type[DBDefaultsDataModel] = DBDefaultsDataModel,
98
+ enum_overrides: dict[str, Type[Enum]] | None = None,
99
+ defaults_for_nullable: bool = True,
100
+ include_id_field: bool = True,
101
+ ) -> Type[DBDefaultsDataModel]:
102
+ (schema, table) = self.get_schema_table_name(table_name)
103
+ cols = self.get_table_columns(schema, table)
104
+ if not cols:
105
+ raise ValueError(f"No columns found for {schema}.{table}")
106
+
107
+ class_name = class_name or self._default_class_name(schema, table)
108
+ enum_overrides = enum_overrides or {}
109
+
110
+ fields_defs = []
111
+
112
+ for c in cols:
113
+ # Skip meta fields
114
+ if self.is_meta_field(c.col_name):
115
+ continue
116
+
117
+ # Enums
118
+ enum_class: Optional[Type[Enum]] = None
119
+ if c.enum_labels:
120
+ enum_class = enum_overrides.get(c.col_name)
121
+ if not enum_class:
122
+ enum_class = _make_enum(f"{class_name}_{c.col_name}_Enum", list(c.enum_labels))
123
+ py_type = enum_class
124
+ serialize = SerializeType.ENUM
125
+ else:
126
+ py_type, serialize = self.map_db_type(c.db_type)
127
+
128
+ # Optional typing if nullable
129
+ if c.is_nullable:
130
+ ann = Optional[py_type] # type: ignore
131
+ else:
132
+ ann = py_type # type: ignore
133
+
134
+ # Default value choice
135
+ default = None
136
+ default_factory = None
137
+ if c.is_nullable:
138
+ default = None
139
+ else:
140
+ # give some sane defaults for common not-nullables that aren't id/serial
141
+ if py_type is bool:
142
+ default = False
143
+ elif py_type in (int, float, str):
144
+ default = py_type() # 0, 0.0, ""
145
+ elif py_type is datetime:
146
+ default_factory = datetime.now
147
+ elif py_type is datetime.date:
148
+ default_factory = date.today
149
+ elif enum_class:
150
+ # pick first enum value as default
151
+ enum_values = list(enum_class)
152
+ if enum_values:
153
+ default = enum_values[0]
154
+ elif py_type is dict:
155
+ default_factory = dict
156
+ elif py_type is list:
157
+ default_factory = list
158
+ elif py_type is set:
159
+ default_factory = set
160
+ elif py_type is bytes:
161
+ default = bytes()
162
+ else:
163
+ # Leave unset so dataclass enforces passing it explicitly
164
+ default = None if defaults_for_nullable else None
165
+
166
+ md: MetadataDict = {
167
+ "db_field": (c.col_name, c.db_type),
168
+ "store": True, # opinion: new rows insert everything unless you override
169
+ "update": True, # opinion: updates allowed unless you override
170
+ }
171
+ if serialize:
172
+ md["serialize"] = serialize
173
+ if enum_class:
174
+ md["enum_class"] = enum_class
175
+
176
+ if default_factory:
177
+ fld = field(default_factory=default_factory, metadata=md)
178
+ else:
179
+ fld = field(default=default, metadata=md)
180
+ fields_defs.append(
181
+ (
182
+ c.col_name,
183
+ ann,
184
+ fld,
185
+ )
186
+ )
187
+
188
+ # Build class with properties schemaName/tableName
189
+ # We’ll generate methods dynamically and attach.
190
+ cls = make_dataclass(
191
+ class_name,
192
+ fields_defs,
193
+ bases=(base,),
194
+ namespace={}, # we’ll add properties below
195
+ frozen=False,
196
+ eq=True,
197
+ repr=True,
198
+ )
199
+
200
+ # Attach schemaName/tableName as properties
201
+ def _schemaName(self) -> str:
202
+ return schema
203
+
204
+ def _tableName(self) -> str:
205
+ return table
206
+
207
+ setattr(cls, "schemaName", property(_schemaName))
208
+ setattr(cls, "tableName", property(_tableName))
209
+
210
+ return cls
211
+
212
+ # TODO: Need to improve handling of imports for external classes, including enums.
213
+ def render_dataclass_source(
214
+ self,
215
+ cls: Type,
216
+ table_name: str,
217
+ *,
218
+ extra_imports: list[str] | None = None,
219
+ emit_ignore_unknown_kwargs: bool = True,
220
+ ) -> str:
221
+ """
222
+ Turn a runtime dataclass into a source file close to user's example.
223
+
224
+ - Hardcodes schemaName/tableName.
225
+ - Emits @ignore_unknown_kwargs() above @dataclass (optional).
226
+ - Renders fields as:
227
+ name: T | None = field(
228
+ default=..., or default_factory=...,
229
+ metadata=MetadataDict(
230
+ db_field=("col", "pg_type"),
231
+ store=True/False,
232
+ update=True/False,
233
+ [serialize=SerializeType.X],
234
+ [enum_class=SomeEnum],
235
+ ),
236
+ )
237
+ - If an enum_class was dynamically created (module == 'enum'), embeds it into the file.
238
+ Otherwise, adds a "from {module} import {name}" import.
239
+ - Adds a toDict() that enumerates fields and applies .isoformat()
240
+ for fields with serialize=SerializeType.DATETIME.
241
+ """
242
+ # Collect enums: dynamic vs external
243
+ extra_imports = extra_imports or []
244
+ dynamic_enums: list[tuple[str, list[tuple[str, Any]]]] = []
245
+ external_enum_imports: set[tuple[str, str]] = set()
246
+ for f in dc_fields(cls):
247
+ md: MetadataDict = dict(f.metadata) if f.metadata else {} # type: ignore
248
+ enum_class = md.get("enum_class")
249
+ if enum_class:
250
+ mod = getattr(enum_class, "__module__", "")
251
+ name = getattr(enum_class, "__name__", "UnknownEnum")
252
+ # If it's the built-in Enum module (meaning we made it dynamically),
253
+ # embed it. Otherwise, import from its module.
254
+ if mod == "enum":
255
+ members = [(m.name, m.value) for m in enum_class] # type: ignore
256
+ dynamic_enums.append((name, members))
257
+ else:
258
+ external_enum_imports.add((mod, name))
259
+
260
+ lines: list[str] = []
261
+ # Imports
262
+ lines.append("from typing import Any, Optional")
263
+ lines.append("from datetime import datetime")
264
+ lines.append("from dataclasses import dataclass, field")
265
+ lines.append("")
266
+ lines.append("from database_wrapper import MetadataDict, DBDefaultsDataModel, SerializeType")
267
+ if emit_ignore_unknown_kwargs:
268
+ lines.append("from database_wrapper.utils import ignore_unknown_kwargs")
269
+ if dynamic_enums:
270
+ lines.append("from enum import Enum")
271
+ for mod, name in sorted(external_enum_imports):
272
+ lines.append(f"from {mod} import {name}")
273
+ for imp in extra_imports:
274
+ lines.append(imp)
275
+ lines.append("")
276
+
277
+ # Dynamic enums embedded
278
+ for name, members in dynamic_enums:
279
+ lines.append(f"class {name}(Enum):")
280
+ for k, v in members:
281
+ lines.append(f" {k} = {repr(v)}")
282
+ lines.append("")
283
+
284
+ # Class header
285
+ (schema, table) = self.get_schema_table_name(table_name)
286
+ if emit_ignore_unknown_kwargs:
287
+ lines.append("@ignore_unknown_kwargs()")
288
+ lines.append("@dataclass")
289
+ lines.append(f"class {cls.__name__}(DBDefaultsDataModel):")
290
+ lines.append(' """Auto-generated from database schema"""')
291
+ lines.append("")
292
+ lines.append(" @property")
293
+ lines.append(" def schemaName(self) -> str:")
294
+ lines.append(f" return {schema!r}")
295
+ lines.append("")
296
+ lines.append(" @property")
297
+ lines.append(" def tableName(self) -> str:")
298
+ lines.append(f" return {table!r}")
299
+ lines.append("")
300
+
301
+ # Render fields (skip the inherited ones we know exist on base if they aren't present here)
302
+ for f in dc_fields(cls):
303
+ md: MetadataDict = dict(f.metadata) if f.metadata else {} # type: ignore
304
+ # We always render all fields that exist in this dataclass (your make_dataclass created them)
305
+ db_field = md.get("db_field", (f.name, "Any"))
306
+ if not (isinstance(db_field, tuple) and len(db_field) == 2):
307
+ col_name, db_type = f.name, "Any"
308
+ else:
309
+ col_name, db_type = db_field
310
+
311
+ # Skip meta fields
312
+ if self.is_meta_field(col_name):
313
+ continue
314
+
315
+ store = bool(md.get("store", False))
316
+ update = bool(md.get("update", False))
317
+ serialize = md.get("serialize")
318
+ enum_class = md.get("enum_class")
319
+
320
+ # Type annotation
321
+ ann_str = type_to_str(f.type)
322
+
323
+ # Default
324
+ # dataclasses._MISSING_TYPE shows up for no default; we’ll set explicit default=None
325
+ # if annotation is Optional[...] and no concrete default.
326
+ default_val = getattr(f, "default", None)
327
+ default_factory = getattr(f, "default_factory", None)
328
+
329
+ # Build field(...) call
330
+ lines.append(f" {f.name}: {ann_str} = field(")
331
+
332
+ if default_factory is not None and default_factory is not MISSING:
333
+ lines.append(f" default_factory={getattr(default_factory, '__qualname__', None)},")
334
+ elif default_val is not None and default_val is not MISSING:
335
+ lines.append(f" default={repr(default_val)},")
336
+ else:
337
+ # be explicit for optionals; otherwise we omit default and let required be required
338
+ if "| None" in ann_str:
339
+ lines.append(" default=None,")
340
+
341
+ # Metadata
342
+ lines.append(" metadata=MetadataDict(")
343
+ lines.append(f" db_field=({col_name!r}, {db_type!r}),")
344
+ lines.append(f" store={str(store)},")
345
+ lines.append(f" update={str(update)},")
346
+ if serialize:
347
+ lines.append(f" serialize=SerializeType.{serialize.name},")
348
+ if enum_class:
349
+ lines.append(f" enum_class={enum_class.__name__},")
350
+ lines.append(" ),")
351
+ lines.append(" )")
352
+ lines.append("")
353
+
354
+ # toDict() that matches your style and ISO-formats DATETIME fields
355
+ # We enumerate in a stable order and treat DATETIME specially.
356
+ # (jsonEncoder in your base also works, but you asked for "close to example".)
357
+ lines.append(" # Methods")
358
+ lines.append(" def toDict(self) -> dict[str, Any]:")
359
+ lines.append(" out: dict[str, Any] = {}")
360
+ lines.append(" # Explicitly list each field (stable order)")
361
+ for f in dc_fields(cls):
362
+ md: MetadataDict = dict(f.metadata) if f.metadata else {} # type: ignore
363
+ serialize = md.get("serialize")
364
+ key = f.name
365
+ if serialize == SerializeType.DATETIME:
366
+ lines.append(f" out[{key!r}] = self.{key}.isoformat() if self.{key} else None")
367
+ else:
368
+ lines.append(f" out[{key!r}] = self.{key}")
369
+ lines.append(" return out")
370
+
371
+ return "\n".join(lines)
372
+
373
+ def save_to_file(self, class_model_source: str, filepath: str, overwrite: bool) -> str:
374
+ """
375
+ Render `cls` to a Python source file and save it to `filepath`.
376
+
377
+ Args:
378
+ cls: The generated dataclass type to render.
379
+ class_model_source: The source code of the class to write.
380
+ filepath: Path to write the file to.
381
+ overwrite: If False and file exists, raises FileExistsError.
382
+
383
+ Returns:
384
+ The absolute path of the written file.
385
+ """
386
+ path = Path(filepath)
387
+ path.parent.mkdir(parents=True, exist_ok=True)
388
+
389
+ if path.exists() and not overwrite:
390
+ raise FileExistsError(f"Refusing to overwrite existing file: {path}")
391
+
392
+ # Normalize line endings + ensure trailing newline
393
+ if not class_model_source.endswith("\n"):
394
+ class_model_source += "\n"
395
+
396
+ path.write_text(class_model_source, encoding="utf-8", newline="\n")
397
+ return str(path.resolve())
@@ -9,6 +9,8 @@ from zoneinfo import ZoneInfo
9
9
 
10
10
  class SerializeType(Enum):
11
11
  DATETIME = "datetime"
12
+ DATE = "date"
13
+ TIME = "time"
12
14
  JSON = "json"
13
15
  ENUM = "enum"
14
16
 
@@ -36,6 +38,18 @@ def serialize_value(value: Any, s_type: SerializeType) -> Any:
36
38
 
37
39
  return value.isoformat()
38
40
 
41
+ if s_type == SerializeType.DATE:
42
+ if not isinstance(value, datetime.date):
43
+ return value
44
+
45
+ return value.isoformat()
46
+
47
+ if s_type == SerializeType.TIME:
48
+ if not isinstance(value, datetime.time):
49
+ return value
50
+
51
+ return value.isoformat()
52
+
39
53
  if s_type == SerializeType.JSON:
40
54
  return json.dumps(value, default=json_encoder)
41
55
 
@@ -67,6 +81,18 @@ def deserialize_value(
67
81
 
68
82
  return datetime.datetime.fromisoformat(value)
69
83
 
84
+ if s_type == SerializeType.DATE:
85
+ if isinstance(value, datetime.date):
86
+ return value
87
+
88
+ return datetime.date.fromisoformat(str(value))
89
+
90
+ if s_type == SerializeType.TIME:
91
+ if isinstance(value, datetime.time):
92
+ return value
93
+
94
+ return datetime.time.fromisoformat(str(value))
95
+
70
96
  if s_type == SerializeType.JSON:
71
97
  if isinstance(value, dict) or isinstance(value, list) or value is None:
72
98
  return value # type: ignore
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: database_wrapper
3
- Version: 0.2.7
3
+ Version: 0.2.9
4
4
  Summary: A Different Approach to Database Wrappers in Python
5
5
  Author-email: Gints Murans <gm@gm.lv>
6
6
  License: GNU General Public License v3.0 (GPL-3.0)
@@ -33,15 +33,15 @@ Classifier: Topic :: Software Development :: Libraries :: Python Modules
33
33
  Requires-Python: >=3.8
34
34
  Description-Content-Type: text/markdown
35
35
  Provides-Extra: pgsql
36
- Requires-Dist: database_wrapper_pgsql==0.2.7; extra == "pgsql"
36
+ Requires-Dist: database_wrapper_pgsql==0.2.9; extra == "pgsql"
37
37
  Provides-Extra: mysql
38
- Requires-Dist: database_wrapper_mysql==0.2.7; extra == "mysql"
38
+ Requires-Dist: database_wrapper_mysql==0.2.9; extra == "mysql"
39
39
  Provides-Extra: mssql
40
- Requires-Dist: database_wrapper_mssql==0.2.7; extra == "mssql"
40
+ Requires-Dist: database_wrapper_mssql==0.2.9; extra == "mssql"
41
41
  Provides-Extra: sqlite
42
- Requires-Dist: database_wrapper_sqlite==0.2.7; extra == "sqlite"
42
+ Requires-Dist: database_wrapper_sqlite==0.2.9; extra == "sqlite"
43
43
  Provides-Extra: redis
44
- Requires-Dist: database_wrapper_redis==0.2.7; extra == "redis"
44
+ Requires-Dist: database_wrapper_redis==0.2.9; extra == "redis"
45
45
  Provides-Extra: all
46
46
  Requires-Dist: database_wrapper[mssql,mysql,pgsql,redis,sqlite]; extra == "all"
47
47
  Provides-Extra: dev
@@ -1,17 +1,18 @@
1
- database_wrapper/__init__.py,sha256=p-HLz9_zByIUeAS1tVxS7ZieslsryHSLw_IKZphqB7w,1200
1
+ database_wrapper/__init__.py,sha256=cGqsWr7imdMOO7vrcGye8FVul_v3-32mw4wgrfxnhJA,1320
2
2
  database_wrapper/abc.py,sha256=JiQo6Yfv7xALrrHeICJCSgmyP2gHrp16Ov83mPBTGbE,2058
3
3
  database_wrapper/common.py,sha256=fsxe28o_4xCrotPbB274dmzQ9rOyes0sBtcHog-9RVc,258
4
- database_wrapper/config.py,sha256=etPdRvN1X_zGxPAMclfw947h6aU-vXWWgwadMUpcJLA,333
4
+ database_wrapper/config.py,sha256=ETbnBY7XXHlIiseyafmCP59I6DBYCFZdk4JO2v2fldQ,333
5
5
  database_wrapper/db_backend.py,sha256=pV_XGu0tR5naz7Ni6BvpqcmN9U7TdQ0bRkg7YiNZlO0,7928
6
- database_wrapper/db_data_model.py,sha256=w0nJ6gfHGZj9Zta31--CM7l-q9GPC-s7DCl3Es5nLxA,14450
6
+ database_wrapper/db_data_model.py,sha256=6yMArPQPfbiFn9GvfRKYQzEh67LfaUn7JqkKTTCc7W8,14888
7
+ database_wrapper/db_introspector.py,sha256=OsQDPr4Bp0SkekrIHIyKh4QX_bLrk7SOq4rbvzicKjE,15496
7
8
  database_wrapper/db_wrapper.py,sha256=zwaA-5BfUhBTG3eI-Im4BKztbKN0DQLg5heIR-rbQCo,14928
8
9
  database_wrapper/db_wrapper_async.py,sha256=EnF66zOJ9BEH2tQF-5v8hywEHl8KiryDM9l610ES7O0,15244
9
10
  database_wrapper/db_wrapper_mixin.py,sha256=QCB9qjWLxeFY5f2_apJVL1rtp75spvZ1HuEApBkzID8,10071
10
11
  database_wrapper/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
- database_wrapper/serialization.py,sha256=rXWNIiroDAYKyIM43_0r5N5ZaBbb59XSgzpwKzFCMMk,2250
12
+ database_wrapper/serialization.py,sha256=Tti1-nA7H4g3hzqr3mE2WurxOnEjFaxBJZTOR3ONZSo,2906
12
13
  database_wrapper/utils/__init__.py,sha256=uC8YaJqfyFIZIeNdTRTbZwcOUVhmnS5eyOG-9gMs70c,96
13
14
  database_wrapper/utils/dataclass_addons.py,sha256=Og98FTL8_m07AjpAsbIdSkHQO099xt9asD3W2QasypY,759
14
- database_wrapper-0.2.7.dist-info/METADATA,sha256=5QXWgpGRmyZMRgk9AGr2r43jp5RjrW6l-q6MvZJiQAQ,3609
15
- database_wrapper-0.2.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
16
- database_wrapper-0.2.7.dist-info/top_level.txt,sha256=QcnS4ocJygxcKE5eoOqriuja306oVu-zJRn6yjRRhBw,17
17
- database_wrapper-0.2.7.dist-info/RECORD,,
15
+ database_wrapper-0.2.9.dist-info/METADATA,sha256=8chFSBLQVJN7072NkmVtC0rO4Fp9L6KIe6veVsjQBcw,3609
16
+ database_wrapper-0.2.9.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
17
+ database_wrapper-0.2.9.dist-info/top_level.txt,sha256=QcnS4ocJygxcKE5eoOqriuja306oVu-zJRn6yjRRhBw,17
18
+ database_wrapper-0.2.9.dist-info/RECORD,,