apexdevkit 1.18.6__tar.gz → 1.18.8__tar.gz

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 (52) hide show
  1. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/PKG-INFO +1 -1
  2. apexdevkit-1.18.8/apexdevkit/id.py +54 -0
  3. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/query/generator.py +39 -37
  4. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/pyproject.toml +1 -1
  5. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/LICENSE +0 -0
  6. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/README.md +0 -0
  7. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/__init__.py +0 -0
  8. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/annotation/__init__.py +0 -0
  9. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/annotation/deprecate.py +0 -0
  10. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/environment.py +0 -0
  11. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/error.py +0 -0
  12. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/__init__.py +0 -0
  13. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/builder.py +0 -0
  14. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/dependable.py +0 -0
  15. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/docs.py +0 -0
  16. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/name.py +0 -0
  17. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/request.py +0 -0
  18. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/resource.py +0 -0
  19. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/response.py +0 -0
  20. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/router.py +0 -0
  21. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/schema.py +0 -0
  22. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fastapi/service.py +0 -0
  23. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/fluent.py +0 -0
  24. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/formatter.py +0 -0
  25. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/http/__init__.py +0 -0
  26. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/http/fake.py +0 -0
  27. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/http/fluent.py +0 -0
  28. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/http/httpx.py +0 -0
  29. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/http/json.py +0 -0
  30. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/http/url.py +0 -0
  31. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/key_fn.py +0 -0
  32. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/py.typed +0 -0
  33. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/query/__init__.py +0 -0
  34. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/query/query.py +0 -0
  35. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/__init__.py +0 -0
  36. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/base.py +0 -0
  37. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/connector.py +0 -0
  38. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/database.py +0 -0
  39. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/decorator.py +0 -0
  40. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/in_memory.py +0 -0
  41. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/interface.py +0 -0
  42. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/mongo.py +0 -0
  43. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/mssql.py +0 -0
  44. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/sql.py +0 -0
  45. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/repository/sqlite.py +0 -0
  46. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/server.py +0 -0
  47. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/synchronization.py +0 -0
  48. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/testing/__init__.py +0 -0
  49. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/testing/database.py +0 -0
  50. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/testing/fake.py +0 -0
  51. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/testing/rest.py +0 -0
  52. {apexdevkit-1.18.6 → apexdevkit-1.18.8}/apexdevkit/value.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: apexdevkit
3
- Version: 1.18.6
3
+ Version: 1.18.8
4
4
  Summary: Apex Development Tools for python.
5
5
  Author: Apex Dev
6
6
  Author-email: dev@apex.ge
@@ -0,0 +1,54 @@
1
+ import os
2
+ from threading import Lock
3
+ from time import time
4
+
5
+
6
+ class ApexID:
7
+ last_timestamp: int = -1
8
+ sequence: int = 0
9
+ lock: Lock = Lock()
10
+
11
+ _metadata_bitmask: int = 0x0FFF
12
+ _sequence_bitmask: int = 0x03FF
13
+ _timestamp_bitmask: int = 0x03FFFFFFFFFF
14
+
15
+ @classmethod
16
+ def id(cls) -> int:
17
+ """
18
+ Generates a unique identifier with the following structure
19
+ from most significant to least significant:
20
+ 1 bit reserved
21
+ 41 bits for unix timestamp (millisecond precision)
22
+ 12 bits for metadata
23
+ 10 bits for sequence
24
+ """
25
+ with cls.lock:
26
+ current_timestamp = cls._get_timestamp()
27
+ if cls.last_timestamp == current_timestamp:
28
+ cls.sequence += 1
29
+ else:
30
+ cls.sequence = 0
31
+
32
+ if cls.sequence > cls._sequence_bitmask:
33
+ raise DuplicateIDException("Duplicate ID Generated")
34
+
35
+ cls.last_timestamp = current_timestamp
36
+
37
+ return (
38
+ (0 << 63)
39
+ | ((cls.last_timestamp & cls._timestamp_bitmask) << 22)
40
+ | ((cls._metadata() & cls._metadata_bitmask) << 10)
41
+ | (cls.sequence & cls._sequence_bitmask)
42
+ )
43
+
44
+ @classmethod
45
+ def _get_timestamp(cls) -> int:
46
+ return int(time() * 1000)
47
+
48
+ @classmethod
49
+ def _metadata(cls) -> int:
50
+ return int(os.getenv("APEX_ID_METADATA", "0"))
51
+
52
+
53
+ class DuplicateIDException(Exception):
54
+ pass
@@ -2,11 +2,12 @@ from __future__ import annotations
2
2
 
3
3
  from collections import defaultdict
4
4
  from dataclasses import dataclass, field
5
- from functools import cached_property
6
5
  from typing import Any, ClassVar, Generic, Iterable, Protocol, TypeVar
7
6
 
7
+ from apexdevkit.annotation import deprecated
8
8
  from apexdevkit.error import ForbiddenError
9
9
  from apexdevkit.query.query import (
10
+ Aggregation,
10
11
  AggregationOption,
11
12
  Filter,
12
13
  FooterOptions,
@@ -149,11 +150,7 @@ class MsSqlQuery:
149
150
  class MsSqlQueryBuilder:
150
151
  source: str = field(init=False)
151
152
  username: str = field(init=False)
152
- translations: dict[str, str] = field(init=False)
153
-
154
- @cached_property
155
- def _fields(self) -> list[MsSqlField]:
156
- return [MsSqlField(name, alias) for alias, name in self.translations.items()]
153
+ _fields: list[MsSqlField] = field(init=False)
157
154
 
158
155
  def with_source(self, value: str) -> MsSqlQueryBuilder:
159
156
  self.source = value
@@ -165,17 +162,23 @@ class MsSqlQueryBuilder:
165
162
 
166
163
  return self
167
164
 
165
+ def with_fields(self, values: list[MsSqlField]) -> MsSqlQueryBuilder:
166
+ self._fields = values
167
+
168
+ return self
169
+
170
+ @deprecated(".with_translations is deprecated, use .with_fields instead")
168
171
  def with_translations(self, value: dict[str, str]) -> MsSqlQueryBuilder:
169
- self.translations = value
172
+ self._fields = [MsSqlField(name, alias) for alias, name in value.items()]
170
173
 
171
174
  return self
172
175
 
173
176
  def aggregate(self, footer: FooterOptions) -> DatabaseCommand:
174
177
  return MsSqlQuery(
175
178
  user=MsSqlUserGenerator(self.username),
176
- selection=MsSqlFooterGenerator(footer.aggregations, self.translations),
179
+ selection=MsSqlFooterGenerator(footer.aggregations, self._fields),
177
180
  filter=MsSqlSourceGenerator(self.source, footer.filter),
178
- condition=MsSqlConditionGenerator(footer.condition, self.translations),
181
+ condition=MsSqlConditionGenerator(footer.condition, self._fields),
179
182
  ).generate()
180
183
 
181
184
  def filter(self, options: QueryOptions) -> DatabaseCommand:
@@ -183,7 +186,7 @@ class MsSqlQueryBuilder:
183
186
  user=MsSqlUserGenerator(self.username),
184
187
  selection=MsSqlSelectionGenerator(self._fields),
185
188
  filter=MsSqlSourceGenerator(self.source, options.filter),
186
- condition=MsSqlConditionGenerator(options.condition, self.translations),
189
+ condition=MsSqlConditionGenerator(options.condition, self._fields),
187
190
  ordering=MsSqlOrderGenerator(options.ordering, self._fields),
188
191
  paging=MsSqlPagingGenerator(options.paging),
189
192
  ).generate()
@@ -219,6 +222,9 @@ class MsSqlField:
219
222
 
220
223
  return result
221
224
 
225
+ def as_aggregation_part(self, option: Aggregation) -> str:
226
+ return f"{option.value}({self.name}) AS {self.alias}_{option.value.lower()}"
227
+
222
228
 
223
229
  @dataclass
224
230
  class MsSqlSelectionGenerator:
@@ -233,27 +239,27 @@ class MsSqlSelectionGenerator:
233
239
  @dataclass
234
240
  class MsSqlFooterGenerator:
235
241
  aggregations: list[AggregationOption]
236
- translations: dict[str, str]
242
+ fields: list[MsSqlField]
237
243
 
238
244
  def generate(self) -> str:
239
- self._validate()
240
245
  fields = ", ".join(
241
246
  [
242
- f"{footer.aggregation.value}"
243
- f"({self.translations[footer.name] if footer.name else '*'}) AS "
244
- f"{footer.name if footer.name else 'general'}"
245
- f"_{footer.aggregation.value.lower()}"
247
+ self.field_for(footer.name).as_aggregation_part(footer.aggregation)
246
248
  for footer in self.aggregations
247
249
  ]
248
250
  )
249
251
 
250
252
  return f"SELECT {fields}"
251
253
 
252
- def _validate(self) -> None:
253
- if self.translations.keys():
254
- for footer in self.aggregations:
255
- if footer.name and footer.name not in self.translations.keys():
256
- raise ForbiddenError(message=f"Invalid field name: {footer.name}")
254
+ def field_for(self, name: str | None) -> MsSqlField:
255
+ if name is None:
256
+ return MsSqlField("*", alias="general")
257
+
258
+ for f in self.fields:
259
+ if f.alias == name:
260
+ return f
261
+
262
+ raise ForbiddenError(message=f"Invalid field name: {name}")
257
263
 
258
264
 
259
265
  @dataclass
@@ -280,12 +286,11 @@ class MsSqlSourceGenerator:
280
286
  @dataclass
281
287
  class MsSqlConditionGenerator:
282
288
  condition: Operator | None
283
- translations: dict[str, str]
289
+ fields: list[MsSqlField]
284
290
 
285
291
  def generate(self) -> str:
286
292
  if self.condition is None:
287
293
  return ""
288
- self._validate(self.condition)
289
294
 
290
295
  return f"WHERE ({self._traverse(self.condition)})"
291
296
 
@@ -306,22 +311,12 @@ class MsSqlConditionGenerator:
306
311
  assert len(node.operands) == 1
307
312
 
308
313
  return OperationEvaluator(
309
- node.operation, self.translations
314
+ node.operation,
315
+ fields=self.fields,
310
316
  ).evaluate_for(
311
317
  node.operands[0], # type: ignore
312
318
  )
313
319
 
314
- def _validate(self, condition: Operator | None) -> None:
315
- if condition is not None and self.translations:
316
- for operand in condition.operands:
317
- if isinstance(operand, Operator):
318
- self._validate(operand)
319
- else:
320
- if operand.name not in self.translations.keys():
321
- raise ForbiddenError(
322
- message=f"Invalid field name: {operand.name}"
323
- )
324
-
325
320
 
326
321
  @dataclass
327
322
  class MsSqlOrderGenerator:
@@ -365,7 +360,7 @@ class MsSqlPagingGenerator:
365
360
  @dataclass
366
361
  class OperationEvaluator:
367
362
  operation: Operation
368
- translations: dict[str, str]
363
+ fields: list[MsSqlField]
369
364
 
370
365
  _TEMPLATES: ClassVar[dict[Operation, str]] = defaultdict(
371
366
  lambda: "[{column}] {operation} {a}",
@@ -385,13 +380,20 @@ class OperationEvaluator:
385
380
  def evaluate_for(self, node: Leaf) -> str:
386
381
  return self._TEMPLATES[self.operation].format(
387
382
  operation=self.operation.value,
388
- column=self.translations[node.name],
383
+ column=self._column_for(node),
389
384
  raw_a=self._get_raw_value(node),
390
385
  a=self._get_value(node, 0),
391
386
  b=self._get_value(node, 1),
392
387
  values=", ".join(val.eval() for val in node.values),
393
388
  )
394
389
 
390
+ def _column_for(self, node: Leaf) -> str:
391
+ for f in self.fields:
392
+ if f.alias == node.name:
393
+ return f.name
394
+
395
+ raise ForbiddenError(message=f"Invalid field name: {node.name}")
396
+
395
397
  def _get_raw_value(self, node: Leaf) -> str | None:
396
398
  raw_a = self._get_value(node, 0)
397
399
  return (
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "apexdevkit"
3
- version = "1.18.6"
3
+ version = "1.18.8"
4
4
  description = "Apex Development Tools for python."
5
5
  authors = ["Apex Dev <dev@apex.ge>"]
6
6
  readme = "README.md"
File without changes
File without changes