nuql 0.0.1__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 (65) hide show
  1. nuql/__init__.py +3 -0
  2. nuql/api/__init__.py +13 -0
  3. nuql/api/adapter.py +34 -0
  4. nuql/api/batch_get/__init__.py +2 -0
  5. nuql/api/batch_get/batch_get.py +40 -0
  6. nuql/api/batch_get/queue.py +120 -0
  7. nuql/api/batch_write.py +99 -0
  8. nuql/api/condition_check.py +39 -0
  9. nuql/api/create.py +25 -0
  10. nuql/api/delete.py +88 -0
  11. nuql/api/get.py +30 -0
  12. nuql/api/put_item.py +112 -0
  13. nuql/api/put_update.py +25 -0
  14. nuql/api/query/__init__.py +4 -0
  15. nuql/api/query/condition.py +157 -0
  16. nuql/api/query/condition_builder.py +211 -0
  17. nuql/api/query/key_condition.py +200 -0
  18. nuql/api/query/query.py +166 -0
  19. nuql/api/transaction.py +145 -0
  20. nuql/api/update/__init__.py +3 -0
  21. nuql/api/update/expression_builder.py +33 -0
  22. nuql/api/update/update_item.py +139 -0
  23. nuql/api/update/utils.py +126 -0
  24. nuql/api/upsert.py +32 -0
  25. nuql/client.py +88 -0
  26. nuql/connection.py +43 -0
  27. nuql/exceptions.py +66 -0
  28. nuql/fields/__init__.py +11 -0
  29. nuql/fields/boolean.py +29 -0
  30. nuql/fields/datetime.py +49 -0
  31. nuql/fields/datetime_timestamp.py +45 -0
  32. nuql/fields/float.py +40 -0
  33. nuql/fields/integer.py +40 -0
  34. nuql/fields/key.py +207 -0
  35. nuql/fields/list.py +90 -0
  36. nuql/fields/map.py +67 -0
  37. nuql/fields/string.py +184 -0
  38. nuql/fields/ulid.py +39 -0
  39. nuql/fields/uuid.py +42 -0
  40. nuql/generators/__init__.py +3 -0
  41. nuql/generators/datetime.py +37 -0
  42. nuql/generators/ulid.py +10 -0
  43. nuql/generators/uuid.py +19 -0
  44. nuql/resources/__init__.py +4 -0
  45. nuql/resources/fields/__init__.py +3 -0
  46. nuql/resources/fields/field.py +153 -0
  47. nuql/resources/fields/field_map.py +85 -0
  48. nuql/resources/fields/value.py +5 -0
  49. nuql/resources/records/__init__.py +3 -0
  50. nuql/resources/records/projections.py +49 -0
  51. nuql/resources/records/serialiser.py +144 -0
  52. nuql/resources/records/validator.py +48 -0
  53. nuql/resources/tables/__init__.py +2 -0
  54. nuql/resources/tables/indexes.py +140 -0
  55. nuql/resources/tables/table.py +151 -0
  56. nuql/resources/utils/__init__.py +2 -0
  57. nuql/resources/utils/dict.py +21 -0
  58. nuql/resources/utils/validators.py +165 -0
  59. nuql/types/__init__.py +3 -0
  60. nuql/types/config.py +27 -0
  61. nuql/types/fields.py +27 -0
  62. nuql/types/serialisation.py +10 -0
  63. nuql-0.0.1.dist-info/METADATA +12 -0
  64. nuql-0.0.1.dist-info/RECORD +65 -0
  65. nuql-0.0.1.dist-info/WHEEL +4 -0
@@ -0,0 +1,165 @@
1
+ __all__ = ['validate_condition_dict', 'validate_schema']
2
+
3
+ import inspect
4
+ from typing import Dict, Any, List, Type
5
+
6
+ import nuql
7
+ from nuql import types, resources
8
+
9
+
10
+ def validate_condition_dict(condition: Dict[str, Any] | None, required: bool = False) -> None:
11
+ """
12
+ Validates a condition dict when provided.
13
+
14
+ :arg condition: Dict or None.
15
+ :param required: If condition is required.
16
+ """
17
+ # Validate empty condition
18
+ if condition is None and not required:
19
+ return None
20
+ elif condition is None and required:
21
+ raise nuql.ValidationError([{'name': 'condition', 'message': 'Condition is required.'}])
22
+
23
+ # Type check
24
+ if not isinstance(condition, dict):
25
+ raise nuql.ValidationError([{'name': 'condition', 'message': 'Condition must be a dict.'}])
26
+
27
+ # Check where key is present
28
+ if 'where' not in condition or not isinstance(condition['where'], str):
29
+ raise nuql.ValidationError([{
30
+ 'name': 'condition.where',
31
+ 'message': 'Condition must contain a \'where\' key and must be a string.'
32
+ }])
33
+
34
+ # Type check variables
35
+ if 'variables' in condition and not isinstance(condition['variables'], dict):
36
+ raise nuql.ValidationError([{
37
+ 'name': 'condition.variables',
38
+ 'message': 'Condition variables must be a dict if defined.'
39
+ }])
40
+
41
+ # Validate no extra keys were passed
42
+ extra_keys = set(condition.keys()) - {'where', 'variables'}
43
+ if extra_keys:
44
+ raise nuql.ValidationError([{
45
+ 'name': 'condition',
46
+ 'message': f'Condition contains unexpected keys: {", ".join(extra_keys)}'
47
+ }])
48
+
49
+
50
+ def validate_table(name: str, config: Dict[str, Any], fields: Dict[str, Type['types.FieldType']]) -> None:
51
+ """
52
+ Validate a table.
53
+
54
+ :arg name: Table name.
55
+ :arg config: Table config.
56
+ :arg fields: Field map.
57
+ """
58
+ if not isinstance(config, dict):
59
+ raise nuql.ValidationError([{
60
+ 'name': f'schema.tables.{name}',
61
+ 'message': 'Table config must be a dict.'
62
+ }])
63
+
64
+ for field_name, field_config in config.items():
65
+ if not isinstance(field_name, str):
66
+ raise nuql.ValidationError([{
67
+ 'name': f'schema.tables.{name}.fields',
68
+ 'message': 'All field names in table config must be a string.'
69
+ }])
70
+
71
+ if not isinstance(field_config, dict):
72
+ raise nuql.ValidationError([{
73
+ 'name': f'schema.tables.{name}.fields.{field_name}',
74
+ 'message': 'Field config must be a dict.'
75
+ }])
76
+
77
+ field_type = field_config.get('type')
78
+
79
+ if not field_type:
80
+ raise nuql.ValidationError([{
81
+ 'name': f'schema.tables.{name}.fields.{field_name}.type',
82
+ 'message': 'Field type is required.'
83
+ }])
84
+
85
+ if field_type not in fields:
86
+ raise nuql.ValidationError([{
87
+ 'name': f'schema.tables.{name}.fields.{field_name}.type',
88
+ 'message': f'Field type \'{field_type}\' is not defined.'
89
+ }])
90
+
91
+ if 'required' in field_config and not isinstance(field_config['required'], bool):
92
+ raise nuql.ValidationError([{
93
+ 'name': f'schema.tables.{name}.fields.{field_name}.required',
94
+ 'message': 'Field key \'required\' must be a boolean value if provided.'
95
+ }])
96
+
97
+ if 'on_create' in field_config and not inspect.isfunction(field_config['on_create']):
98
+ raise nuql.ValidationError([{
99
+ 'name': f'schema.tables.{name}.fields.{field_name}.on_create',
100
+ 'message': 'Field key \'on_create\' must be a function if provided.'
101
+ }])
102
+
103
+ if 'on_update' in field_config and not inspect.isfunction(field_config['on_update']):
104
+ raise nuql.ValidationError([{
105
+ 'name': f'schema.tables.{name}.fields.{field_name}.on_update',
106
+ 'message': 'Field key \'on_update\' must be a function if provided.'
107
+ }])
108
+
109
+ if 'on_write' in field_config and not inspect.isfunction(field_config['on_write']):
110
+ raise nuql.ValidationError([{
111
+ 'name': f'schema.tables.{name}.fields.{field_name}.on_write',
112
+ 'message': 'Field key \'on_write\' must be a function if provided.'
113
+ }])
114
+
115
+ if 'validator' in field_config and not inspect.isfunction(field_config['validator']):
116
+ raise nuql.ValidationError([{
117
+ 'name': f'schema.tables.{name}.fields.{field_name}.validator',
118
+ 'message': 'Field key \'validator\' must be a function if provided.'
119
+ }])
120
+
121
+ if 'enum' in field_config and not isinstance(field_config['enum'], list):
122
+ raise nuql.ValidationError([{
123
+ 'name': f'schema.tables.{name}.fields.{field_name}.enum',
124
+ 'message': 'Field key \'enum\' must be a list if provided.'
125
+ }])
126
+
127
+ accepted_keys = [
128
+ 'type', 'required', 'default', 'value', 'on_create', 'on_update', 'on_write', 'validator', 'enum',
129
+ 'of', 'fields',
130
+ ]
131
+ invalid_field_config_keys = [x for x in field_config.keys() if x not in accepted_keys]
132
+ if invalid_field_config_keys:
133
+ raise nuql.ValidationError([{
134
+ 'name': f'schema.tables.{name}.fields.{field_name}',
135
+ 'message': f'Field config contains unexpected keys: {", ".join(invalid_field_config_keys)}. '
136
+ f'Accepted keys are: {", ".join(accepted_keys)}'
137
+ }])
138
+
139
+
140
+ def validate_schema(schema: Dict[str, Any], fields: List[Type['types.FieldType']]) -> None:
141
+ """
142
+ Validate a schema.
143
+
144
+ :arg schema: Schema dict.
145
+ :arg fields: List of fields passed from client.
146
+ """
147
+ fields = resources.get_field_types(fields)
148
+
149
+ # Type check schema
150
+ if not isinstance(schema, dict):
151
+ raise nuql.ValidationError([{
152
+ 'name': 'schema',
153
+ 'message': 'Schema must be a dict.'
154
+ }])
155
+
156
+ for table_name, config in schema.items():
157
+ # Type check table name
158
+ if not isinstance(table_name, str):
159
+ raise nuql.ValidationError([{
160
+ 'name': 'schema.table_name',
161
+ 'message': 'Table name in schema must be a string.',
162
+ }])
163
+
164
+ # Validate table schema
165
+ validate_table(table_name, config, fields)
nuql/types/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ from .fields import *
2
+ from .serialisation import *
3
+ from .config import *
nuql/types/config.py ADDED
@@ -0,0 +1,27 @@
1
+ __all__ = ['TableConfig', 'SchemaConfig', 'DBIndexType', 'IndexesType', 'PrimaryIndex', 'SecondaryIndex']
2
+
3
+ from typing import Dict, TypedDict, NotRequired, Literal, List
4
+
5
+ from nuql import types
6
+
7
+
8
+ TableConfig = Dict[str, types.FieldConfig]
9
+ SchemaConfig = Dict[str, TableConfig]
10
+ DBIndexType = Literal['local', 'global']
11
+
12
+
13
+ class PrimaryIndex(TypedDict):
14
+ hash: str
15
+ sort: NotRequired[str | None]
16
+
17
+
18
+ class SecondaryIndex(TypedDict):
19
+ hash: str
20
+ sort: str
21
+ type: DBIndexType
22
+ name: str
23
+ follow: NotRequired[bool]
24
+ projection: NotRequired[Literal['keys', 'all']]
25
+
26
+
27
+ IndexesType = List[PrimaryIndex | SecondaryIndex]
nuql/types/fields.py ADDED
@@ -0,0 +1,27 @@
1
+ __all__ = ['FieldConfig', 'IndexType', 'GeneratorCallback', 'ValidatorCallback', 'FieldType']
2
+
3
+ from typing import TypedDict, NotRequired, Any, Literal, Callable, List, TypeVar, TYPE_CHECKING, Dict
4
+
5
+ from nuql import resources
6
+
7
+
8
+ IndexType = Literal['local', 'global']
9
+
10
+ GeneratorCallback = Callable[[], Any]
11
+ ValidatorCallback = Callable[[Any, resources.Validator], None]
12
+
13
+ FieldType = TypeVar('FieldType', bound=resources.FieldBase)
14
+
15
+
16
+ class FieldConfig(TypedDict):
17
+ type: str
18
+ required: NotRequired[bool]
19
+ default: NotRequired[Any]
20
+ value: NotRequired[Any]
21
+ on_create: NotRequired[GeneratorCallback]
22
+ on_update: NotRequired[GeneratorCallback]
23
+ on_write: NotRequired[GeneratorCallback]
24
+ validator: NotRequired[ValidatorCallback]
25
+ enum: NotRequired[List[Any]]
26
+ of: NotRequired['FieldConfig']
27
+ fields: NotRequired[Dict[str, Any]]
@@ -0,0 +1,10 @@
1
+ __all__ = ['SerialisationType', 'ValidationErrorItem']
2
+
3
+ from typing import Literal, TypedDict
4
+
5
+ SerialisationType = Literal['create', 'update', 'write', 'query']
6
+
7
+
8
+ class ValidationErrorItem(TypedDict):
9
+ name: str
10
+ message: str
@@ -0,0 +1,12 @@
1
+ Metadata-Version: 2.4
2
+ Name: nuql
3
+ Version: 0.0.1
4
+ Summary: Nuql (pronounced 'nuckle') is a lightweight DynamoDB library for implementing the single table model pattern.
5
+ Classifier: License :: OSI Approved :: MIT License
6
+ Classifier: Operating System :: OS Independent
7
+ Classifier: Programming Language :: Python :: 3
8
+ Requires-Python: >=3.13
9
+ Requires-Dist: boto3>=1.40.0
10
+ Requires-Dist: pyparsing>=3.2.3
11
+ Requires-Dist: python-ulid>=3.0.0
12
+ Requires-Dist: uuid-utils>=0.11.0
@@ -0,0 +1,65 @@
1
+ nuql/__init__.py,sha256=IjH6hvOvMlqYwZUk7n2x0wy0XWUtZo0cwADTlv9VW-A,77
2
+ nuql/client.py,sha256=aT4TKJ-Rnixo67h3jhmsunqH7oJS_-joHJvPTwJTDZI,2846
3
+ nuql/connection.py,sha256=6PusMgUwPoOT8X0CqWLKdgsdvdAEwmb_dfelz0WpJE8,1471
4
+ nuql/exceptions.py,sha256=1dhtHoLJZyP-njCPC2hNTK48tSllbGV7amlwq3tJQps,2044
5
+ nuql/api/__init__.py,sha256=DKxuIAjckS1vjMIPykbI3h81HszlBB9LdwQuFf53-wo,324
6
+ nuql/api/adapter.py,sha256=VIrItHyAW87xGgjavXRiN2-99shRjDn0kik_iLHeEl0,1355
7
+ nuql/api/batch_write.py,sha256=IZi349BdEiahmWqtQlch4lN6yXNi921czxNTBQIzbzY,2998
8
+ nuql/api/condition_check.py,sha256=uSB01Gp3ZwSWPFLJk6pZDv8zXF_QCZFLVu9yDNq2Vcs,1588
9
+ nuql/api/create.py,sha256=SUfsZtRrp1WWILsdwvSGXKdSGIiiRFsKlttX8uS8AMw,793
10
+ nuql/api/delete.py,sha256=8d7LP2SLje9YHNXrCQNbt3IS67xGMtPLY5ZOx7NtJrg,3038
11
+ nuql/api/get.py,sha256=8K29b-wRW4szYInPvdgyTe7lq3SsvQf1VaiABxsaw_0,934
12
+ nuql/api/put_item.py,sha256=5dWZsYSYpIerVoRkZYDC3l6vw4ctGFua-wAXD3HY4cU,4109
13
+ nuql/api/put_update.py,sha256=ZkNUjX3GkBrymVenoI4RHmjn5Xzh4u1YcRxTte9X22Q,795
14
+ nuql/api/transaction.py,sha256=tL7S_AvKXpcH0OwYh3Fn8N_TWDsGy3mihyjyM6gT5bg,4687
15
+ nuql/api/upsert.py,sha256=X7Qwg_1x8-RUUlQZUq6qLKzlzapOLyKMlFBiDMN8QOg,1133
16
+ nuql/api/batch_get/__init__.py,sha256=N1NQ_WFov4bANyCdS1HBPaPYnlDhZ4QEJSMiEN0sL0k,48
17
+ nuql/api/batch_get/batch_get.py,sha256=DgGCYLQqiCUzwZdrf1XyWHEecY4nzQ-mweWhLPe68RQ,1069
18
+ nuql/api/batch_get/queue.py,sha256=6S70p0i4xXlh8pU8X0-F8-U8P3uzoxJDzbUNaMEhrzU,4032
19
+ nuql/api/query/__init__.py,sha256=_YhXEUvd82bBhOlkBHtPYC8a8VgCHskeg4l95X8w2O0,112
20
+ nuql/api/query/condition.py,sha256=SNCEJZah7v2ioSfxHUmco73HzuKCKVvrzDx6kSLMBQY,5965
21
+ nuql/api/query/condition_builder.py,sha256=kHlpSYcrEX-vQvkqjOG6GVC2_a63zJSjJgIs2_zhGIo,6201
22
+ nuql/api/query/key_condition.py,sha256=B7POd7R4dLV-o629J7rEtvkm9kyqRQAqpAs_Pofun1U,7145
23
+ nuql/api/query/query.py,sha256=Wb3TPmzUdnN96KV_kyXzrVmOsqrVbxFUv3sE48AZxuk,6365
24
+ nuql/api/update/__init__.py,sha256=15mjrAPhrOM74c3Q6NQXTGVjFFgwPCiTAlBLHIDmPQ8,85
25
+ nuql/api/update/expression_builder.py,sha256=jFkp0EASDx7uK2b0TanRALGusXqtXwvOVy02WW2OfGM,1147
26
+ nuql/api/update/update_item.py,sha256=KCfrAZiDPmQc0k20UAA9H3dw_8WFYJ8fDOqHb_xSYNo,5295
27
+ nuql/api/update/utils.py,sha256=ZT2EuSSvoD6-C3Gpybn68N9MohVYDGa6DdpSDIGh-GA,3728
28
+ nuql/fields/__init__.py,sha256=Ldf91fDoiNRkseWo3NksQlOxJTEI-EdS6i1OPjzE_9Y,256
29
+ nuql/fields/boolean.py,sha256=OnmxZyaFH2up1dHDc5p9iHi8fvYZQV1TPI-LMwSb0NA,712
30
+ nuql/fields/datetime.py,sha256=4K28yydnzUc1HsMH-lgSSNOx2NbEL4qXC3kTpuRBVis,1386
31
+ nuql/fields/datetime_timestamp.py,sha256=drJLpmGhL8ANrfGsArKeVLb4v7maiignhzC4qpQzMHY,1275
32
+ nuql/fields/float.py,sha256=x48Spo_GNku0djO0Ogs8usZTfwOY-27Mh5OPOQBO3OY,1316
33
+ nuql/fields/integer.py,sha256=pHs3CJM4xKN60IYg5aTe4QjSQQBR4jp08HyANtidHgs,1312
34
+ nuql/fields/key.py,sha256=v8lsEW7K0HFGvguCq5PeiMmJaK-ygqrkKnrkm67_fbI,7338
35
+ nuql/fields/list.py,sha256=EJI4wlqg8H2FsaB0jAhjnESAeHxX-dt9a-jb3ezFWEM,3067
36
+ nuql/fields/map.py,sha256=dlKA2c9wLupr2soIig5-TYHfNkm05VF2svCfkRv2G1w,2340
37
+ nuql/fields/string.py,sha256=UrG6JpMFpfCSdgSnw_U4e75rWQpxNScSW-7MlWRDVng,6264
38
+ nuql/fields/ulid.py,sha256=mcwTztzIqE_3q1cRa2pf6pN8s0322NQlfQcky9xAWkI,971
39
+ nuql/fields/uuid.py,sha256=fhwAcMUHuih2NgPh7QOs4hcCwrl8Cu2CqSpvLMBAV-c,1041
40
+ nuql/generators/__init__.py,sha256=YgTwNlvZYM4EYo2u3gSN4bIuXz7f8SQF7Y7jnpXN_Ls,67
41
+ nuql/generators/datetime.py,sha256=eX10E9mbCaFVJb3xsVaNxTCpBpl1lsd6HTGYS6hsqNs,981
42
+ nuql/generators/ulid.py,sha256=MuxmKQBPEBSqhlD4NPCnUrDgwgn82a0sTQISQ0iIYmM,194
43
+ nuql/generators/uuid.py,sha256=MoEQVxgEsqGlhpf4W-OuRiLKqNwRtE2NtS3eFTOc5lo,394
44
+ nuql/resources/__init__.py,sha256=8HRJ_H5yTdQtfeVZ29ikdbLQSPKiZRyNkoocQd5QtZ8,92
45
+ nuql/resources/fields/__init__.py,sha256=bajUBAvrK8kIMyb2vD_0NCwibRHOLo4Yoq1sac0Dobc,70
46
+ nuql/resources/fields/field.py,sha256=zDufEscRJr-vfSlbfdk9lBxoQffW_Qr2m_Es0plBGhg,4889
47
+ nuql/resources/fields/field_map.py,sha256=tDDoXdyXnxRJWJ9Il7-uu81R_zSOUXiwrY5mllS3jYU,2517
48
+ nuql/resources/fields/value.py,sha256=CG3PnGeSMHDQciIMR6jkMhogCJZhpEfipLdyk9gnjJw,59
49
+ nuql/resources/records/__init__.py,sha256=gD8-xt4uIOpx_ZTnw1QTzOiwUpjKhR3nhkZw4AmEvaI,81
50
+ nuql/resources/records/projections.py,sha256=PTwoc8-XXieXsYw0H-3Fv7G8SzSN__j35QsqempXBj8,1618
51
+ nuql/resources/records/serialiser.py,sha256=yhMUZ-E0NMMxDszOSfl9ZWNdc2mSJDZfSIjUxAGKnLM,5039
52
+ nuql/resources/records/validator.py,sha256=tuEkNEbZApV-3v2YkTkXemFfRBJyt03IZDtsl7Lmitw,1526
53
+ nuql/resources/tables/__init__.py,sha256=Bk8ewpPb_myliQPmyIarzma_Vezd4XR-Z1lABsTUMPQ,46
54
+ nuql/resources/tables/indexes.py,sha256=5e5bQ_yRgyLmnoXuKAqEw0mJJhY_NE4Sk0vavvMG2m4,5076
55
+ nuql/resources/tables/table.py,sha256=2UjpxhW_0C2nXQr6gMf2k8JLRYA6M1vf5QaGXMDRZw4,5513
56
+ nuql/resources/utils/__init__.py,sha256=vRZp-isHvATfzDlIPLOSu8x6sj3GprPQViL2l6p4OQk,48
57
+ nuql/resources/utils/dict.py,sha256=K-BWbeaXEIDwSRLUXI8BIKr9UluOE0dhd378733j6ug,553
58
+ nuql/resources/utils/validators.py,sha256=tUv6-u29KMpazdvM8vKHEUtyaMUkE61KCdJCEG8LhZk,6583
59
+ nuql/types/__init__.py,sha256=Ea4KR4mUs1RNUEskKF9sXfGpoQw2V9cnIYeE20wkChs,76
60
+ nuql/types/config.py,sha256=lFfPPe62l4DkfLXR8ecw5AiSRAFzsTYy_3a0d_1M7hY,659
61
+ nuql/types/fields.py,sha256=LjGgXM84WO54gRDkgvnwF3pjFvFxErdGYOvs0BskF_o,883
62
+ nuql/types/serialisation.py,sha256=XAy_gARcwYmfEuxWAJox2ClWK9ow0F5WXKVbMUVBq9w,242
63
+ nuql-0.0.1.dist-info/METADATA,sha256=an5t_-VKW6mYk3UM01vGq1R-PCAOjqqWnWYjy4z_Dsc,466
64
+ nuql-0.0.1.dist-info/WHEEL,sha256=qtCwoSJWgHk21S1Kb4ihdzI2rlJ1ZKaIurTj_ngOhyQ,87
65
+ nuql-0.0.1.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.27.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any