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.
- DefPixel/DataAccessObject/Configuration.py +15 -0
- DefPixel/DataAccessObject/Connection.py +25 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Configuration.py +16 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Connection.py +96 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Constraint.py +9 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Constraints/ForeignKey.py +16 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Constraints/PrimaryKey.py +15 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Constraints/Unique.py +25 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Constraints/__init__.py +11 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Context.py +36 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Cursor.py +87 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/Create.py +115 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/Delete.py +35 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/Drop.py +34 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/Get.py +24 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/Insert.py +59 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/Select.py +109 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/Update.py +34 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Executors/__init__.py +19 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Expr.py +42 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Exprs/Alias.py +20 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Exprs/Is.py +29 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Exprs/__init__.py +13 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Filters/Compare.py +149 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Filters/Operator.py +26 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Filters/String.py +23 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Filters/__init__.py +30 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Function.py +15 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Functions/Common.py +34 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Functions/Condition.py +21 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Functions/__init__.py +16 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Index.py +23 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Joins/FullOuter.py +19 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Joins/Inner.py +19 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Joins/Join.py +31 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Joins/LeftOuter.py +19 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Joins/RightOuter.py +19 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Joins/__init__.py +13 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Mapping.py +303 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Orders/Ascend.py +24 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Orders/Descend.py +24 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Orders/__init__.py +9 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Session.py +37 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Type.py +75 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Types/Binary.py +28 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Types/DateTime.py +42 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Types/Numeric.py +49 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Types/String.py +28 -0
- DefPixel/DataAccessObject/Implements/Sqlite/Types/__init__.py +34 -0
- DefPixel/DataAccessObject/Implements/Sqlite/__init__.py +39 -0
- DefPixel/DataAccessObject/Implements/__init__.py +1 -0
- DefPixel/DataAccessObject/Properties/Database/Context.py +33 -0
- DefPixel/DataAccessObject/Properties/Database/Cursor.py +27 -0
- DefPixel/DataAccessObject/Properties/Database/Database.py +48 -0
- DefPixel/DataAccessObject/Properties/Database/Executor.py +38 -0
- DefPixel/DataAccessObject/Properties/Database/Executors.py +31 -0
- DefPixel/DataAccessObject/Properties/Database/Fetch.py +37 -0
- DefPixel/DataAccessObject/Properties/Database/Filter.py +21 -0
- DefPixel/DataAccessObject/Properties/Database/Run.py +39 -0
- DefPixel/DataAccessObject/Properties/Database/Session.py +36 -0
- DefPixel/DataAccessObject/Properties/Database/__init__.py +25 -0
- DefPixel/DataAccessObject/Properties/Dataclass.py +25 -0
- DefPixel/DataAccessObject/Properties/Document/Document.py +36 -0
- DefPixel/DataAccessObject/Properties/Document/__init__.py +7 -0
- DefPixel/DataAccessObject/Properties/Index/Index.py +48 -0
- DefPixel/DataAccessObject/Properties/Index/__init__.py +7 -0
- DefPixel/DataAccessObject/Properties/KeyValue/KeyValue.py +44 -0
- DefPixel/DataAccessObject/Properties/KeyValue/__init__.py +7 -0
- DefPixel/DataAccessObject/Properties/__init__.py +15 -0
- DefPixel/DataAccessObject/__init__.py +9 -0
- DefPixel/Validator/Pattern.py +36 -0
- DefPixel/Validator/Patterns/Array.py +30 -0
- DefPixel/Validator/Patterns/Common.py +92 -0
- DefPixel/Validator/Patterns/Compare.py +180 -0
- DefPixel/Validator/Patterns/Condition.py +60 -0
- DefPixel/Validator/Patterns/DataObject.py +85 -0
- DefPixel/Validator/Patterns/DateTime.py +111 -0
- DefPixel/Validator/Patterns/Number.py +43 -0
- DefPixel/Validator/Patterns/Object.py +150 -0
- DefPixel/Validator/Patterns/Size.py +110 -0
- DefPixel/Validator/Patterns/String.py +85 -0
- DefPixel/Validator/Patterns/Type.py +350 -0
- DefPixel/Validator/Patterns/__init__.py +137 -0
- DefPixel/Validator/Validate.py +29 -0
- DefPixel/Validator/Validator.py +30 -0
- DefPixel/Validator/__init__.py +11 -0
- DefPixel/__init__.py +0 -0
- defpixel-0.1.0.dist-info/METADATA +114 -0
- defpixel-0.1.0.dist-info/RECORD +92 -0
- defpixel-0.1.0.dist-info/WHEEL +6 -0
- defpixel-0.1.0.dist-info/licenses/LICENSE +674 -0
- 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,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,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
|