moose-lib 0.6.172__tar.gz → 0.6.173__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.
Potentially problematic release.
This version of moose-lib might be problematic. Click here for more details.
- {moose_lib-0.6.172 → moose_lib-0.6.173}/PKG-INFO +1 -1
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/__init__.py +1 -1
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/data_models.py +48 -3
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib.egg-info/PKG-INFO +1 -1
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib.egg-info/SOURCES.txt +1 -0
- moose_lib-0.6.173/tests/test_fixedstring.py +43 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/README.md +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/blocks.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/clients/__init__.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/clients/redis_client.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/commons.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/config/__init__.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/config/config_file.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/config/runtime.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/__init__.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/_registry.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/consumption.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/ingest_api.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/ingest_pipeline.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/life_cycle.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/materialized_view.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/olap_table.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/registry.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/sql_resource.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/stream.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/types.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/view.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/web_app.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/web_app_helpers.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2/workflow.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/dmv2_serializer.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/internal.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/main.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/query_builder.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/query_param.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/secrets.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/streaming/__init__.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/streaming/streaming_function_runner.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/utilities/__init__.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib/utilities/sql.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib.egg-info/dependency_links.txt +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib.egg-info/requires.txt +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/moose_lib.egg-info/top_level.txt +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/setup.cfg +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/setup.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/__init__.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/conftest.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_moose.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_olap_table_versioning.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_query_builder.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_redis_client.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_s3queue_config.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_secrets.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_simple_aggregate.py +0 -0
- {moose_lib-0.6.172 → moose_lib-0.6.173}/tests/test_web_app.py +0 -0
|
@@ -30,7 +30,7 @@ from .blocks import (
|
|
|
30
30
|
# Legacy enum (already exported via .blocks import, but explicit for clarity)
|
|
31
31
|
ClickHouseEngines
|
|
32
32
|
)
|
|
33
|
-
from .data_models import Key, AggregateFunction, StringToEnumMixin
|
|
33
|
+
from .data_models import Key, AggregateFunction, StringToEnumMixin, FixedString, ClickhouseFixedStringSize
|
|
34
34
|
from .commons import Logger
|
|
35
35
|
|
|
36
36
|
from .query_builder import *
|
|
@@ -26,6 +26,11 @@ class ClickhouseSize:
|
|
|
26
26
|
size: int
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
@dataclasses.dataclass(frozen=True)
|
|
30
|
+
class ClickhouseFixedStringSize:
|
|
31
|
+
size: int
|
|
32
|
+
|
|
33
|
+
|
|
29
34
|
@dataclasses.dataclass(frozen=True)
|
|
30
35
|
class ClickhouseDefault:
|
|
31
36
|
expression: str
|
|
@@ -61,6 +66,23 @@ def clickhouse_datetime64(precision: int) -> Type[datetime]:
|
|
|
61
66
|
return Annotated[datetime, ClickhousePrecision(precision=precision)]
|
|
62
67
|
|
|
63
68
|
|
|
69
|
+
def FixedString(size: int) -> ClickhouseFixedStringSize:
|
|
70
|
+
"""
|
|
71
|
+
Creates a FixedString(N) annotation for fixed-length strings.
|
|
72
|
+
|
|
73
|
+
ClickHouse stores exactly N bytes, padding shorter values with null bytes.
|
|
74
|
+
Values exceeding N bytes will raise an exception.
|
|
75
|
+
|
|
76
|
+
Use for fixed-length data like hashes, IPs, UUIDs, MAC addresses.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
md5_hash: Annotated[str, FixedString(16)] # 16-byte MD5
|
|
80
|
+
sha256: Annotated[str, FixedString(32)] # 32-byte SHA256
|
|
81
|
+
ipv6: Annotated[str, FixedString(16)] # 16-byte IPv6
|
|
82
|
+
"""
|
|
83
|
+
return ClickhouseFixedStringSize(size=size)
|
|
84
|
+
|
|
85
|
+
|
|
64
86
|
type Point = Annotated[tuple[float, float], "Point"]
|
|
65
87
|
type Ring = Annotated[list[tuple[float, float]], "Ring"]
|
|
66
88
|
type LineString = Annotated[list[tuple[float, float]], "LineString"]
|
|
@@ -279,7 +301,26 @@ def py_type_to_column_type(t: type, mds: list[Any]) -> Tuple[bool, list[Any], Da
|
|
|
279
301
|
data_type: DataType
|
|
280
302
|
|
|
281
303
|
if t is str:
|
|
282
|
-
|
|
304
|
+
# Check for FixedString annotation
|
|
305
|
+
fixed_string_size = next(
|
|
306
|
+
(md.size for md in mds if isinstance(md, ClickhouseFixedStringSize)),
|
|
307
|
+
None
|
|
308
|
+
)
|
|
309
|
+
if fixed_string_size:
|
|
310
|
+
data_type = f"FixedString({fixed_string_size})"
|
|
311
|
+
else:
|
|
312
|
+
data_type = "String"
|
|
313
|
+
elif t is bytes:
|
|
314
|
+
# Check for FixedString annotation
|
|
315
|
+
fixed_string_size = next(
|
|
316
|
+
(md.size for md in mds if isinstance(md, ClickhouseFixedStringSize)),
|
|
317
|
+
None
|
|
318
|
+
)
|
|
319
|
+
if fixed_string_size:
|
|
320
|
+
data_type = f"FixedString({fixed_string_size})"
|
|
321
|
+
else:
|
|
322
|
+
# Regular bytes without FixedString annotation
|
|
323
|
+
data_type = "String"
|
|
283
324
|
elif t is int:
|
|
284
325
|
# Check for int size annotations
|
|
285
326
|
int_size = next((md for md in mds if isinstance(md, str) and re.match(r'^u?int\d+$', md)), None)
|
|
@@ -437,9 +478,13 @@ def py_type_to_column_type(t: type, mds: list[Any]) -> Tuple[bool, list[Any], Da
|
|
|
437
478
|
def _to_columns(model: type[BaseModel]) -> list[Column]:
|
|
438
479
|
"""Convert Pydantic model fields to Column definitions."""
|
|
439
480
|
columns = []
|
|
481
|
+
# Get raw annotations from the model class to preserve type aliases
|
|
482
|
+
raw_annotations = getattr(model, '__annotations__', {})
|
|
483
|
+
|
|
440
484
|
for field_name, field_info in model.model_fields.items():
|
|
441
|
-
#
|
|
442
|
-
|
|
485
|
+
# Use raw annotation if available (preserves type aliases and their metadata)
|
|
486
|
+
# Fall back to field_info.annotation if not found in __annotations__
|
|
487
|
+
field_type = raw_annotations.get(field_name, field_info.annotation)
|
|
443
488
|
if field_type is None:
|
|
444
489
|
raise ValueError(f"Missing type for {field_name}")
|
|
445
490
|
primary_key, field_type = handle_key(field_type)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from datetime import datetime
|
|
2
|
+
from typing import Annotated
|
|
3
|
+
from pydantic import BaseModel
|
|
4
|
+
from moose_lib import Key, FixedString
|
|
5
|
+
from moose_lib.data_models import _to_columns
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def test_fixedstring_annotation():
|
|
9
|
+
"""Test FixedString annotation converts to correct ClickHouse type with str base type."""
|
|
10
|
+
|
|
11
|
+
class FixedStringTest(BaseModel):
|
|
12
|
+
id: Key[str]
|
|
13
|
+
created_at: datetime
|
|
14
|
+
md5_hash: Annotated[str, FixedString(16)]
|
|
15
|
+
sha256_hash: Annotated[str, FixedString(32)]
|
|
16
|
+
ipv6_address: Annotated[str, FixedString(16)]
|
|
17
|
+
|
|
18
|
+
columns = _to_columns(FixedStringTest)
|
|
19
|
+
by_name = {col.name: col for col in columns}
|
|
20
|
+
|
|
21
|
+
assert by_name["md5_hash"].data_type == "FixedString(16)"
|
|
22
|
+
assert by_name["sha256_hash"].data_type == "FixedString(32)"
|
|
23
|
+
assert by_name["ipv6_address"].data_type == "FixedString(16)"
|
|
24
|
+
|
|
25
|
+
# Verify other fields still work
|
|
26
|
+
assert by_name["id"].data_type == "String"
|
|
27
|
+
assert by_name["created_at"].data_type == "DateTime"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def test_fixedstring_different_sizes():
|
|
31
|
+
"""Test various FixedString sizes."""
|
|
32
|
+
|
|
33
|
+
class FixedStringSizes(BaseModel):
|
|
34
|
+
mac_address: Annotated[str, FixedString(6)]
|
|
35
|
+
uuid_binary: Annotated[str, FixedString(16)]
|
|
36
|
+
sha512_hash: Annotated[str, FixedString(64)]
|
|
37
|
+
|
|
38
|
+
columns = _to_columns(FixedStringSizes)
|
|
39
|
+
by_name = {col.name: col for col in columns}
|
|
40
|
+
|
|
41
|
+
assert by_name["mac_address"].data_type == "FixedString(6)"
|
|
42
|
+
assert by_name["uuid_binary"].data_type == "FixedString(16)"
|
|
43
|
+
assert by_name["sha512_hash"].data_type == "FixedString(64)"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|