DefPixel 0.1.0__3-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.
Files changed (92) hide show
  1. DefPixel/DataAccessObject/Configuration.py +15 -0
  2. DefPixel/DataAccessObject/Connection.py +25 -0
  3. DefPixel/DataAccessObject/Implements/Sqlite/Configuration.py +16 -0
  4. DefPixel/DataAccessObject/Implements/Sqlite/Connection.py +96 -0
  5. DefPixel/DataAccessObject/Implements/Sqlite/Constraint.py +9 -0
  6. DefPixel/DataAccessObject/Implements/Sqlite/Constraints/ForeignKey.py +16 -0
  7. DefPixel/DataAccessObject/Implements/Sqlite/Constraints/PrimaryKey.py +15 -0
  8. DefPixel/DataAccessObject/Implements/Sqlite/Constraints/Unique.py +25 -0
  9. DefPixel/DataAccessObject/Implements/Sqlite/Constraints/__init__.py +11 -0
  10. DefPixel/DataAccessObject/Implements/Sqlite/Context.py +36 -0
  11. DefPixel/DataAccessObject/Implements/Sqlite/Cursor.py +87 -0
  12. DefPixel/DataAccessObject/Implements/Sqlite/Executors/Create.py +115 -0
  13. DefPixel/DataAccessObject/Implements/Sqlite/Executors/Delete.py +35 -0
  14. DefPixel/DataAccessObject/Implements/Sqlite/Executors/Drop.py +34 -0
  15. DefPixel/DataAccessObject/Implements/Sqlite/Executors/Get.py +24 -0
  16. DefPixel/DataAccessObject/Implements/Sqlite/Executors/Insert.py +59 -0
  17. DefPixel/DataAccessObject/Implements/Sqlite/Executors/Select.py +109 -0
  18. DefPixel/DataAccessObject/Implements/Sqlite/Executors/Update.py +34 -0
  19. DefPixel/DataAccessObject/Implements/Sqlite/Executors/__init__.py +19 -0
  20. DefPixel/DataAccessObject/Implements/Sqlite/Expr.py +42 -0
  21. DefPixel/DataAccessObject/Implements/Sqlite/Exprs/Alias.py +20 -0
  22. DefPixel/DataAccessObject/Implements/Sqlite/Exprs/Is.py +29 -0
  23. DefPixel/DataAccessObject/Implements/Sqlite/Exprs/__init__.py +13 -0
  24. DefPixel/DataAccessObject/Implements/Sqlite/Filters/Compare.py +149 -0
  25. DefPixel/DataAccessObject/Implements/Sqlite/Filters/Operator.py +26 -0
  26. DefPixel/DataAccessObject/Implements/Sqlite/Filters/String.py +23 -0
  27. DefPixel/DataAccessObject/Implements/Sqlite/Filters/__init__.py +30 -0
  28. DefPixel/DataAccessObject/Implements/Sqlite/Function.py +15 -0
  29. DefPixel/DataAccessObject/Implements/Sqlite/Functions/Common.py +34 -0
  30. DefPixel/DataAccessObject/Implements/Sqlite/Functions/Condition.py +21 -0
  31. DefPixel/DataAccessObject/Implements/Sqlite/Functions/__init__.py +16 -0
  32. DefPixel/DataAccessObject/Implements/Sqlite/Index.py +23 -0
  33. DefPixel/DataAccessObject/Implements/Sqlite/Joins/FullOuter.py +19 -0
  34. DefPixel/DataAccessObject/Implements/Sqlite/Joins/Inner.py +19 -0
  35. DefPixel/DataAccessObject/Implements/Sqlite/Joins/Join.py +31 -0
  36. DefPixel/DataAccessObject/Implements/Sqlite/Joins/LeftOuter.py +19 -0
  37. DefPixel/DataAccessObject/Implements/Sqlite/Joins/RightOuter.py +19 -0
  38. DefPixel/DataAccessObject/Implements/Sqlite/Joins/__init__.py +13 -0
  39. DefPixel/DataAccessObject/Implements/Sqlite/Mapping.py +303 -0
  40. DefPixel/DataAccessObject/Implements/Sqlite/Orders/Ascend.py +24 -0
  41. DefPixel/DataAccessObject/Implements/Sqlite/Orders/Descend.py +24 -0
  42. DefPixel/DataAccessObject/Implements/Sqlite/Orders/__init__.py +9 -0
  43. DefPixel/DataAccessObject/Implements/Sqlite/Session.py +37 -0
  44. DefPixel/DataAccessObject/Implements/Sqlite/Type.py +75 -0
  45. DefPixel/DataAccessObject/Implements/Sqlite/Types/Binary.py +28 -0
  46. DefPixel/DataAccessObject/Implements/Sqlite/Types/DateTime.py +42 -0
  47. DefPixel/DataAccessObject/Implements/Sqlite/Types/Numeric.py +49 -0
  48. DefPixel/DataAccessObject/Implements/Sqlite/Types/String.py +28 -0
  49. DefPixel/DataAccessObject/Implements/Sqlite/Types/__init__.py +34 -0
  50. DefPixel/DataAccessObject/Implements/Sqlite/__init__.py +39 -0
  51. DefPixel/DataAccessObject/Implements/__init__.py +1 -0
  52. DefPixel/DataAccessObject/Properties/Database/Context.py +33 -0
  53. DefPixel/DataAccessObject/Properties/Database/Cursor.py +27 -0
  54. DefPixel/DataAccessObject/Properties/Database/Database.py +48 -0
  55. DefPixel/DataAccessObject/Properties/Database/Executor.py +38 -0
  56. DefPixel/DataAccessObject/Properties/Database/Executors.py +31 -0
  57. DefPixel/DataAccessObject/Properties/Database/Fetch.py +37 -0
  58. DefPixel/DataAccessObject/Properties/Database/Filter.py +21 -0
  59. DefPixel/DataAccessObject/Properties/Database/Run.py +39 -0
  60. DefPixel/DataAccessObject/Properties/Database/Session.py +36 -0
  61. DefPixel/DataAccessObject/Properties/Database/__init__.py +25 -0
  62. DefPixel/DataAccessObject/Properties/Dataclass.py +25 -0
  63. DefPixel/DataAccessObject/Properties/Document/Document.py +36 -0
  64. DefPixel/DataAccessObject/Properties/Document/__init__.py +7 -0
  65. DefPixel/DataAccessObject/Properties/Index/Index.py +48 -0
  66. DefPixel/DataAccessObject/Properties/Index/__init__.py +7 -0
  67. DefPixel/DataAccessObject/Properties/KeyValue/KeyValue.py +44 -0
  68. DefPixel/DataAccessObject/Properties/KeyValue/__init__.py +7 -0
  69. DefPixel/DataAccessObject/Properties/__init__.py +15 -0
  70. DefPixel/DataAccessObject/__init__.py +9 -0
  71. DefPixel/Validator/Pattern.py +36 -0
  72. DefPixel/Validator/Patterns/Array.py +30 -0
  73. DefPixel/Validator/Patterns/Common.py +92 -0
  74. DefPixel/Validator/Patterns/Compare.py +180 -0
  75. DefPixel/Validator/Patterns/Condition.py +60 -0
  76. DefPixel/Validator/Patterns/DataObject.py +85 -0
  77. DefPixel/Validator/Patterns/DateTime.py +111 -0
  78. DefPixel/Validator/Patterns/Number.py +43 -0
  79. DefPixel/Validator/Patterns/Object.py +150 -0
  80. DefPixel/Validator/Patterns/Size.py +110 -0
  81. DefPixel/Validator/Patterns/String.py +85 -0
  82. DefPixel/Validator/Patterns/Type.py +350 -0
  83. DefPixel/Validator/Patterns/__init__.py +137 -0
  84. DefPixel/Validator/Validate.py +29 -0
  85. DefPixel/Validator/Validator.py +30 -0
  86. DefPixel/Validator/__init__.py +11 -0
  87. DefPixel/__init__.py +0 -0
  88. defpixel-0.1.0.dist-info/METADATA +114 -0
  89. defpixel-0.1.0.dist-info/RECORD +92 -0
  90. defpixel-0.1.0.dist-info/WHEEL +6 -0
  91. defpixel-0.1.0.dist-info/licenses/LICENSE +674 -0
  92. defpixel-0.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Join import Join
4
+
5
+ from DefPixel.DataAccessObject.Properties import Dataclass
6
+
7
+ from typing import Type
8
+
9
+ __all__ = (
10
+ 'LeftOuter'
11
+ )
12
+
13
+
14
+ class LeftOuter(Join):
15
+ """Left Outer Class"""
16
+
17
+ def __init__(self, table: Type[Dataclass], *args) -> None:
18
+ super(LeftOuter, self).__init__('LEFT OUTER', table, *args)
19
+ return
@@ -0,0 +1,19 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Join import Join
4
+
5
+ from DefPixel.DataAccessObject.Properties import Dataclass
6
+
7
+ from typing import Type
8
+
9
+ __all__ = (
10
+ 'RightOuter'
11
+ )
12
+
13
+
14
+ class RightOuter(Join):
15
+ """Right Outer Join Class"""
16
+
17
+ def __init__(self, table: Type[Dataclass], *args) -> None:
18
+ super(RightOuter, self).__init__('RIGHT OUTER', table, *args)
19
+ return
@@ -0,0 +1,13 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Inner import Inner
4
+ from .LeftOuter import LeftOuter
5
+ from .RightOuter import RightOuter
6
+ from .FullOuter import FullOuter
7
+
8
+ __all__ = (
9
+ 'Inner',
10
+ 'LeftOuter',
11
+ 'RightOuter',
12
+ 'FullOuter',
13
+ )
@@ -0,0 +1,303 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from copy import copy
4
+ from dataclasses import (
5
+ MISSING,
6
+ field,
7
+ fields,
8
+ is_dataclass,
9
+ )
10
+ from types import MemberDescriptorType
11
+ from typing import (
12
+ Any,
13
+ Callable,
14
+ Dict,
15
+ NamedTuple,
16
+ Optional,
17
+ Sequence,
18
+ Type as PythonType,
19
+ TypeVar,
20
+ )
21
+
22
+ from .Type import Type
23
+ from .Constraint import Constraint
24
+ from .Index import Index
25
+
26
+ __all__ = (
27
+ 'column',
28
+ 'table',
29
+ 'view',
30
+ )
31
+
32
+ METADATA = 'defpixel.sqlite'
33
+ MAPPING = '__defpixel_sqlite__'
34
+ T = TypeVar('T')
35
+
36
+
37
+ def identifier(name: str) -> str:
38
+ if not isinstance(name, str) or not name or '\x00' in name:
39
+ raise ValueError('SQL identifiers must be nonempty strings without null characters')
40
+ return '"{}"'.format(name.replace('"', '""'))
41
+
42
+
43
+ class Statement(NamedTuple):
44
+ """Satisfy the single-execution contract while preserving query and argument unpacking."""
45
+
46
+ query: str
47
+ args: Sequence[Any] = ()
48
+
49
+
50
+ class ColumnAccess:
51
+ """Expose a column on class access and a standard field value on instance access."""
52
+
53
+ def __init__(
54
+ self,
55
+ name: str,
56
+ spec: Type,
57
+ original: Any,
58
+ default: Any,
59
+ ):
60
+ self.name = name
61
+ self.spec = spec
62
+ self.original = original
63
+ self.default = default
64
+ return
65
+
66
+ def __get__(
67
+ self,
68
+ instance,
69
+ owner=None,
70
+ ):
71
+ if instance is None:
72
+ return self.spec
73
+ if self.original is not None:
74
+ return self.original.__get__(instance, owner)
75
+ if self.name in instance.__dict__:
76
+ return instance.__dict__[self.name]
77
+ if self.default is not MISSING:
78
+ return self.default
79
+ raise AttributeError(self.name)
80
+
81
+ def __set__(
82
+ self,
83
+ instance,
84
+ value,
85
+ ):
86
+ if self.original is not None:
87
+ self.original.__set__(instance, value)
88
+ else:
89
+ instance.__dict__[self.name] = value
90
+ return
91
+
92
+ def __delete__(self, instance):
93
+ if self.original is not None:
94
+ self.original.__delete__(instance)
95
+ else:
96
+ try:
97
+ del instance.__dict__[self.name]
98
+ except KeyError:
99
+ raise AttributeError(self.name) from None
100
+ return
101
+
102
+
103
+ class Mapping:
104
+ """Explicit mapping between a dataclass and SQLite storage."""
105
+
106
+ def __init__(
107
+ self,
108
+ model: PythonType,
109
+ name: str,
110
+ kind: str,
111
+ columns: Dict[str, Type],
112
+ constraints: Sequence,
113
+ indexes: Sequence,
114
+ executor=None,
115
+ description=None,
116
+ ):
117
+ self.model = model
118
+ self.name = name
119
+ self.kind = kind
120
+ self.columns = columns
121
+ self.constraints = constraints
122
+ self.indexes = indexes
123
+ self.executor = executor
124
+ self.description = description
125
+ return
126
+
127
+ def values(self, values):
128
+ unknown = set(values) - self.columns.keys()
129
+ if unknown:
130
+ raise ValueError('Unmapped fields: {}'.format(', '.join(sorted(unknown))))
131
+ return {
132
+ self.columns[name].key: self.columns[name].encode(value)
133
+ for name, value in values.items()
134
+ }
135
+
136
+ def restore(self, row):
137
+ members = {member.name: member for member in fields(self.model)}
138
+ values = {}
139
+ deferred = {}
140
+ by_column = {spec.key: name for name, spec in self.columns.items()}
141
+ for key, value in row.items():
142
+ if key not in by_column:
143
+ raise ValueError('Result column is not mapped to the model: {}'.format(key))
144
+ name = by_column[key]
145
+ value = self.columns[name].validate(value)
146
+ if members[name].init:
147
+ values[name] = value
148
+ else:
149
+ deferred[name] = value
150
+ for name, member in members.items():
151
+ if (
152
+ member.init and name not in values
153
+ and member.default is MISSING and member.default_factory is MISSING
154
+ ):
155
+ raise ValueError('Missing field required for object restoration: {}'.format(name))
156
+ instance = self.model(**values)
157
+ for name, value in deferred.items():
158
+ object.__setattr__(instance, name, value)
159
+ return instance
160
+
161
+
162
+ def column(
163
+ spec: Type,
164
+ *,
165
+ validator=None,
166
+ description: Optional[str] = None,
167
+ ) -> Any:
168
+ """Attach a SQLite specification and its default to a standard Field."""
169
+ if not isinstance(spec, Type):
170
+ raise TypeError('column requires a SQLite Type specification')
171
+ spec = copy(spec)
172
+ if validator is not None:
173
+ spec.validator = validator
174
+ return field(
175
+ default=spec.coldef,
176
+ metadata={
177
+ METADATA: spec,
178
+ 'description': description,
179
+ },
180
+ )
181
+
182
+
183
+ def inspect_model(
184
+ model: PythonType,
185
+ kind: Optional[str] = None,
186
+ ) -> Mapping:
187
+ if not isinstance(model, type) or not is_dataclass(model):
188
+ raise TypeError('ORM models must be dataclass classes')
189
+ mapping = vars(model).get(MAPPING)
190
+ if not isinstance(mapping, Mapping) or mapping.model is not model:
191
+ raise ValueError('{} has no @table or @view mapping'.format(model.__name__))
192
+ if kind is not None and mapping.kind != kind:
193
+ raise ValueError('A {} operation cannot use a {} mapping'.format(kind, mapping.kind))
194
+ return mapping
195
+
196
+
197
+ def mapped(
198
+ model: PythonType[T],
199
+ name,
200
+ kind,
201
+ constraints,
202
+ indexes,
203
+ executor,
204
+ description,
205
+ ) -> PythonType[T]:
206
+ if not isinstance(model, type) or not is_dataclass(model):
207
+ raise TypeError('@{} must be applied after @dataclass'.format(kind))
208
+ if MAPPING in vars(model):
209
+ raise ValueError('The model already has a SQLite mapping')
210
+ identifier(model.__name__ if name is None else name)
211
+ columns = {}
212
+ accessors = {}
213
+ keys = set()
214
+ for member in fields(model):
215
+ if member.name == MAPPING:
216
+ raise ValueError('The reserved mapping attribute cannot be used as a field name')
217
+ if METADATA not in member.metadata:
218
+ continue
219
+ spec = copy(member.metadata[METADATA])
220
+ spec.key = member.name if spec.key is None else spec.key
221
+ identifier(spec.key)
222
+ if spec.key in keys:
223
+ raise ValueError('Duplicate column: {}'.format(spec.key))
224
+ keys.add(spec.key)
225
+ spec.model = model.__name__ if name is None else name
226
+ columns[member.name] = spec
227
+ original = None
228
+ for base in model.__mro__:
229
+ attribute = vars(base).get(member.name)
230
+ if isinstance(attribute, ColumnAccess):
231
+ original = attribute.original
232
+ break
233
+ if isinstance(attribute, MemberDescriptorType):
234
+ original = attribute
235
+ break
236
+ accessors[member.name] = ColumnAccess(
237
+ member.name,
238
+ spec,
239
+ original,
240
+ member.default,
241
+ )
242
+ if not columns:
243
+ raise ValueError('No fields are declared with column()')
244
+ mapping = Mapping(
245
+ model,
246
+ model.__name__ if name is None else name,
247
+ kind,
248
+ columns,
249
+ (constraints,) if isinstance(constraints, Constraint) else tuple(constraints or ()),
250
+ (indexes,) if isinstance(indexes, Index) else tuple(indexes or ()),
251
+ executor,
252
+ description,
253
+ )
254
+ setattr(model, MAPPING, mapping)
255
+ for key, accessor in accessors.items():
256
+ setattr(model, key, accessor)
257
+ return model
258
+
259
+
260
+ def table(
261
+ name=None,
262
+ *,
263
+ constraints=None,
264
+ indexes=None,
265
+ description=None,
266
+ ) -> Callable[[PythonType[T]], PythonType[T]]:
267
+ """Map a processed dataclass to a table and return the same class."""
268
+ def decorate(model: PythonType[T]) -> PythonType[T]:
269
+ return mapped(
270
+ model,
271
+ name,
272
+ 'table',
273
+ constraints,
274
+ indexes,
275
+ None,
276
+ description,
277
+ )
278
+ return decorate
279
+
280
+
281
+ def view(
282
+ *,
283
+ executor,
284
+ name=None,
285
+ description=None,
286
+ ) -> Callable[[PythonType[T]], PythonType[T]]:
287
+ """Map a processed dataclass to a view defined by a query Executor."""
288
+ def decorate(model: PythonType[T]) -> PythonType[T]:
289
+ from DefPixel.DataAccessObject.Properties.Database import Executor
290
+ if not isinstance(executor, Executor):
291
+ raise TypeError('View definitions require a single Executor')
292
+ if executor.args:
293
+ raise ValueError('SQLite view definitions cannot contain bound parameters')
294
+ return mapped(
295
+ model,
296
+ name,
297
+ 'view',
298
+ None,
299
+ None,
300
+ executor,
301
+ description,
302
+ )
303
+ return decorate
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from ..Expr import Expr
4
+
5
+ from ..Type import Type
6
+
7
+ __all__ = (
8
+ 'Ascend'
9
+ )
10
+
11
+
12
+ class Ascend(Expr):
13
+ """Ascend Order Class"""
14
+
15
+ def __init__(self, col: Type, null='LAST') -> None:
16
+ self.col = col
17
+ self.null = null
18
+ return
19
+
20
+ def __str__(self):
21
+ return '{} ASC{}'.format(
22
+ str(self.col) if isinstance(self.col, Type) else self.col,
23
+ ' NULLS {}'.format(self.null) if self.null else '',
24
+ )
@@ -0,0 +1,24 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from ..Expr import Expr
4
+
5
+ from ..Type import Type
6
+
7
+ __all__ = (
8
+ 'Descend'
9
+ )
10
+
11
+
12
+ class Descend(Expr):
13
+ """Descend Order Class"""
14
+
15
+ def __init__(self, col: Type, null='LAST') -> None:
16
+ self.col = col
17
+ self.null = null
18
+ return
19
+
20
+ def __str__(self):
21
+ return '{} DESC{}'.format(
22
+ str(self.col) if isinstance(self.col, Type) else self.col,
23
+ ' NULLS {}'.format(self.null) if self.null else '',
24
+ )
@@ -0,0 +1,9 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Ascend import Ascend
4
+ from .Descend import Descend
5
+
6
+ __all__ = (
7
+ 'Ascend',
8
+ 'Descend',
9
+ )
@@ -0,0 +1,37 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from DefPixel.DataAccessObject.Properties.Database import Session as BaseSession
4
+ from .Cursor import Cursor
5
+
6
+ __all__ = (
7
+ 'Session',
8
+ )
9
+
10
+
11
+ class Session(Cursor, BaseSession):
12
+ """Explicit cursor scope sharing the connection transaction."""
13
+
14
+ def __init__(self, connection):
15
+ self.connection = connection
16
+ super().__init__(connection.cursor())
17
+ return
18
+
19
+ def __enter__(self):
20
+ if not self.connection.in_transaction:
21
+ self.connection.execute('BEGIN')
22
+ return self
23
+
24
+ def __exit__(
25
+ self,
26
+ exception_type,
27
+ exception,
28
+ traceback,
29
+ ):
30
+ try:
31
+ if exception_type is None:
32
+ self.connection.commit()
33
+ else:
34
+ self.connection.rollback()
35
+ finally:
36
+ self.close()
37
+ return False
@@ -0,0 +1,75 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from dataclasses import MISSING
4
+ from datetime import datetime
5
+ from typing import (
6
+ Any,
7
+ Callable,
8
+ Optional,
9
+ Type as PythonType,
10
+ )
11
+
12
+ __all__ = (
13
+ 'Type',
14
+ )
15
+
16
+
17
+ class Type:
18
+ """SQLite column specification for storage types and value conversion."""
19
+
20
+ typestr = ''
21
+
22
+ def __init__(
23
+ self,
24
+ key: Optional[str],
25
+ type: PythonType,
26
+ va: Optional[Callable] = None,
27
+ null: bool = False,
28
+ default: Any = MISSING,
29
+ ):
30
+ self.key = key
31
+ self.type = type
32
+ self.validator = va
33
+ self.null = null
34
+ self.coldef = default
35
+ self.model: Optional[str] = None
36
+ return
37
+
38
+ def __init_subclass__(
39
+ cls,
40
+ typestr: str,
41
+ ):
42
+ super().__init_subclass__()
43
+ cls.typestr = typestr
44
+ return
45
+
46
+ def __str__(self) -> str:
47
+ if self.model is None or self.key is None:
48
+ raise ValueError('The column is not bound to a model')
49
+ from .Mapping import identifier
50
+ return '{}.{}'.format(identifier(self.model), identifier(self.key))
51
+
52
+ def validate(
53
+ self,
54
+ value: Any,
55
+ ) -> Any:
56
+ if value is None:
57
+ if not self.null:
58
+ raise ValueError('Column {} does not allow None'.format(self.key))
59
+ return None
60
+ if self.validator is not None:
61
+ value = self.validator(value)
62
+ if not isinstance(value, self.type) or (
63
+ self.type is int and isinstance(value, bool)
64
+ ):
65
+ raise TypeError('Column {} requires a value of type {}'.format(self.key, self.type.__name__))
66
+ return value
67
+
68
+ def encode(
69
+ self,
70
+ value: Any,
71
+ ) -> Any:
72
+ value = self.validate(value)
73
+ if isinstance(value, datetime):
74
+ return value.isoformat()
75
+ return value
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from dataclasses import MISSING
4
+ from ..Type import Type
5
+
6
+ __all__ = (
7
+ 'Binary',
8
+ )
9
+
10
+
11
+ class Binary(Type, typestr='BLOB'):
12
+ """Binary column specification."""
13
+
14
+ def __init__(
15
+ self,
16
+ name=None,
17
+ va=None,
18
+ null=False,
19
+ default=MISSING,
20
+ ):
21
+ super().__init__(
22
+ key=name,
23
+ type=bytes,
24
+ va=va,
25
+ null=null,
26
+ default=default,
27
+ )
28
+ return
@@ -0,0 +1,42 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from dataclasses import MISSING
4
+ from datetime import datetime
5
+ from ..Type import Type
6
+
7
+ __all__ = (
8
+ 'DateTime',
9
+ 'Timestamp',
10
+ )
11
+
12
+
13
+ def to_datetime(value):
14
+ if isinstance(value, str):
15
+ return datetime.fromisoformat(value)
16
+ if isinstance(value, float):
17
+ return datetime.fromtimestamp(value)
18
+ return value
19
+
20
+
21
+ class DateTime(Type, typestr='DATETIME'):
22
+ """Date-time specification supporting ISO string and float timestamp conversion."""
23
+
24
+ def __init__(
25
+ self,
26
+ name=None,
27
+ va=None,
28
+ null=False,
29
+ default=MISSING,
30
+ ):
31
+ super().__init__(
32
+ key=name,
33
+ type=datetime,
34
+ va=to_datetime if va is None else va,
35
+ null=null,
36
+ default=default,
37
+ )
38
+ return
39
+
40
+
41
+ class Timestamp(DateTime, typestr='TIMESTAMP'):
42
+ """Date-time specification using a TIMESTAMP declaration."""
@@ -0,0 +1,49 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from dataclasses import MISSING
4
+ from ..Type import Type
5
+
6
+ __all__ = (
7
+ 'Integer',
8
+ 'Float',
9
+ )
10
+
11
+
12
+ class Integer(Type, typestr='INTEGER'):
13
+ """Integer column specification."""
14
+
15
+ def __init__(
16
+ self,
17
+ name=None,
18
+ va=None,
19
+ null=False,
20
+ default=MISSING,
21
+ ):
22
+ super().__init__(
23
+ key=name,
24
+ type=int,
25
+ va=va,
26
+ null=null,
27
+ default=default,
28
+ )
29
+ return
30
+
31
+
32
+ class Float(Type, typestr='REAL'):
33
+ """Floating-point column specification."""
34
+
35
+ def __init__(
36
+ self,
37
+ name=None,
38
+ va=None,
39
+ null=False,
40
+ default=MISSING,
41
+ ):
42
+ super().__init__(
43
+ key=name,
44
+ type=float,
45
+ va=va,
46
+ null=null,
47
+ default=default,
48
+ )
49
+ return
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from dataclasses import MISSING
4
+ from ..Type import Type
5
+
6
+ __all__ = (
7
+ 'Text',
8
+ )
9
+
10
+
11
+ class Text(Type, typestr='TEXT'):
12
+ """Text column specification."""
13
+
14
+ def __init__(
15
+ self,
16
+ name=None,
17
+ va=None,
18
+ null=False,
19
+ default=MISSING,
20
+ ):
21
+ super().__init__(
22
+ key=name,
23
+ type=str,
24
+ va=va,
25
+ null=null,
26
+ default=default,
27
+ )
28
+ return
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ from .Numeric import (
4
+ Integer,
5
+ Float,
6
+ )
7
+ from .String import Text
8
+ from .Binary import Binary
9
+ from .DateTime import (
10
+ DateTime,
11
+ Timestamp,
12
+ )
13
+
14
+ __all__ = (
15
+ 'Integer', 'INTEGER',
16
+ 'Float', 'FLOAT', 'REAL',
17
+ 'Text', 'TEXT', 'STRING',
18
+ 'Binary', 'BLOB',
19
+ 'DateTime', 'DATETIME',
20
+ 'Timestamp', 'TIMESTAMP',
21
+ )
22
+
23
+ # NUMERIC
24
+ INTEGER = Integer
25
+ FLOAT = Float
26
+ REAL = Float
27
+ # TEXT
28
+ TEXT = Text
29
+ STRING = Text
30
+ # BINARY
31
+ BLOB = Binary
32
+ # DATETIME
33
+ DATETIME = DateTime
34
+ TIMESTAMP = Timestamp