dbzero-modelkit 0.1.3__tar.gz → 1.1.0__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 (24) hide show
  1. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/PKG-INFO +1 -1
  2. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/active.py +7 -3
  3. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/calendars.py +7 -3
  4. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/language.py +5 -2
  5. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/month_store.py +9 -2
  6. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/object_lock.py +9 -2
  7. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/queues.py +4 -3
  8. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit.egg-info/PKG-INFO +1 -1
  9. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/pyproject.toml +1 -1
  10. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/tests/test_active.py +41 -0
  11. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/tests/test_calendars.py +54 -0
  12. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/tests/test_language.py +21 -0
  13. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/tests/test_month_store.py +21 -0
  14. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/tests/test_object_lock.py +39 -0
  15. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/tests/test_queues.py +28 -1
  16. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/README.md +0 -0
  17. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/__init__.py +0 -0
  18. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/rpc_integration.py +0 -0
  19. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit/time_utils.py +0 -0
  20. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit.egg-info/SOURCES.txt +0 -0
  21. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit.egg-info/dependency_links.txt +0 -0
  22. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit.egg-info/requires.txt +0 -0
  23. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/dbzero_modelkit.egg-info/top_level.txt +0 -0
  24. {dbzero_modelkit-0.1.3 → dbzero_modelkit-1.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbzero-modelkit
3
- Version: 0.1.3
3
+ Version: 1.1.0
4
4
  Summary: Reusable dbzero-backed data model utilities
5
5
  Author-email: Selltime <dev@selltime.ai>
6
6
  License-Expression: MIT
@@ -11,7 +11,7 @@ from dbzero_modelkit.rpc_integration import rpc as db0_rpc
11
11
  from dbzero_modelkit.time_utils import normalize_end, normalize_start
12
12
 
13
13
 
14
- @db0.memo(no_default_tags=True)
14
+ @db0.memo(id="/dbzero/dbzero-modelkit/ActiveBase", no_default_tags=True)
15
15
  class ActiveBase:
16
16
  """Base object for models that are active only within a period of time."""
17
17
 
@@ -19,7 +19,10 @@ class ActiveBase:
19
19
  self,
20
20
  active_from: datetime | date | None = None,
21
21
  expires_on: datetime | date | None = None,
22
+ *,
23
+ prefix: str | None = None,
22
24
  ) -> None:
25
+ db0.set_prefix(self, prefix)
23
26
  self.active_from = active_from
24
27
  self.expires_on = expires_on
25
28
 
@@ -116,11 +119,12 @@ class ActiveBase:
116
119
  return merged_active_from, merged_expires_on
117
120
 
118
121
 
119
- @db0.memo(no_default_tags=True)
122
+ @db0.memo(id="/dbzero/dbzero-modelkit/ActiveIndex", no_default_tags=True)
120
123
  class ActiveIndex:
121
124
  """Container indexing ActiveBase-compatible objects by active period."""
122
125
 
123
- def __init__(self) -> None:
126
+ def __init__(self, *, prefix: str | None = None) -> None:
127
+ db0.set_prefix(self, prefix)
124
128
  self.__ix_active_from = db0.index()
125
129
  self.__ix_expires_on = db0.index()
126
130
 
@@ -19,7 +19,7 @@ def get_date_from_month_index(base_year: int, month_index: int) -> Date:
19
19
  return Date(base_year + month_index // 12, month_index % 12 + 1, 1)
20
20
 
21
21
 
22
- @db0.memo(no_default_tags=True)
22
+ @db0.memo(id="/dbzero/dbzero-modelkit/MonthCalendar", no_default_tags=True)
23
23
  class MonthCalendar:
24
24
  """Single sparse month view for Calendar."""
25
25
 
@@ -82,7 +82,7 @@ class MonthCalendar:
82
82
  actual_day += timedelta(days=1)
83
83
 
84
84
 
85
- @db0.memo(no_default_tags=True)
85
+ @db0.memo(id="/dbzero/dbzero-modelkit/Calendar", no_default_tags=True)
86
86
  class Calendar:
87
87
  """Sparse date calendar with lazy month creation."""
88
88
 
@@ -101,7 +101,11 @@ class Calendar:
101
101
  else:
102
102
  return None
103
103
  if self.__months[month_index] is None and create is True:
104
- self.__months[month_index] = MonthCalendar(self, month_index)
104
+ self.__months[month_index] = MonthCalendar(
105
+ self,
106
+ month_index,
107
+ prefix=db0.get_prefix_of(self).name,
108
+ )
105
109
  return self.__months[month_index]
106
110
 
107
111
  def date_range(
@@ -7,12 +7,15 @@ from typing import Optional
7
7
  import dbzero as db0
8
8
 
9
9
 
10
- @db0.enum(values=["LEN", "LPL", "LGER", "LFR", "LESP"])
10
+ @db0.enum(
11
+ values=["LEN", "LPL", "LGER", "LFR", "LESP"],
12
+ type_id="/dbzero/dbzero-modelkit/LanguageCode",
13
+ )
11
14
  class LanguageCode:
12
15
  """Common language codes used by multi-language strings."""
13
16
 
14
17
 
15
- @db0.memo(no_default_tags=True)
18
+ @db0.memo(id="/dbzero/dbzero-modelkit/ML_String", no_default_tags=True)
16
19
  class ML_String:
17
20
  """Store textual information in a primary language and optional translations."""
18
21
 
@@ -10,11 +10,18 @@ import dbzero as db0
10
10
  from dbzero_modelkit.rpc_integration import rpc as db0_rpc
11
11
 
12
12
 
13
- @db0.memo(no_default_tags=True)
13
+ @db0.memo(id="/dbzero/dbzero-modelkit/MonthStore", no_default_tags=True)
14
14
  class MonthStore:
15
15
  """General-purpose container for storing one object per month."""
16
16
 
17
- def __init__(self, item_type: type, base_year: int = 2025) -> None:
17
+ def __init__(
18
+ self,
19
+ item_type: type,
20
+ base_year: int = 2025,
21
+ *,
22
+ prefix: str | None = None,
23
+ ) -> None:
24
+ db0.set_prefix(self, prefix)
18
25
  self.__item_type = item_type
19
26
  self.__base_year = base_year
20
27
  self.__months = []
@@ -8,11 +8,18 @@ from typing import Any, Iterable, Type
8
8
  import dbzero as db0
9
9
 
10
10
 
11
- @db0.memo
11
+ @db0.memo(id="/dbzero/dbzero-modelkit/ObjectLock")
12
12
  class ObjectLock:
13
13
  """General-purpose lock that holds references to locked objects."""
14
14
 
15
- def __init__(self, locked_objects: Any | list[Any], duration: int = 300) -> None:
15
+ def __init__(
16
+ self,
17
+ locked_objects: Any | list[Any],
18
+ duration: int = 300,
19
+ *,
20
+ prefix: str | None = None,
21
+ ) -> None:
22
+ db0.set_prefix(self, prefix)
16
23
  if not isinstance(locked_objects, list):
17
24
  locked_objects = [locked_objects]
18
25
  self.locked_objects = locked_objects
@@ -7,7 +7,7 @@ from typing import Callable, Dict, List, Optional
7
7
  import dbzero as db0
8
8
 
9
9
 
10
- @db0.memo
10
+ @db0.memo(id="/dbzero/dbzero-modelkit/FQ_Item")
11
11
  class FQ_Item: # pylint: disable=invalid-name
12
12
  """Single FIFO queue entry storing keyword arguments and its integer key."""
13
13
 
@@ -31,11 +31,12 @@ class FQ_Item: # pylint: disable=invalid-name
31
31
  }
32
32
 
33
33
 
34
- @db0.memo
34
+ @db0.memo(id="/dbzero/dbzero-modelkit/FiFoQueue")
35
35
  class FiFoQueue:
36
36
  """FIFO queue container backed by a dbzero index."""
37
37
 
38
- def __init__(self) -> None:
38
+ def __init__(self, *, prefix: str | None = None) -> None:
39
+ db0.set_prefix(self, prefix)
39
40
  self.__items = db0.index()
40
41
  self.__next_key = 0
41
42
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dbzero-modelkit
3
- Version: 0.1.3
3
+ Version: 1.1.0
4
4
  Summary: Reusable dbzero-backed data model utilities
5
5
  Author-email: Selltime <dev@selltime.ai>
6
6
  License-Expression: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dbzero-modelkit"
7
- version = "0.1.3"
7
+ version = "1.1.0"
8
8
  description = "Reusable dbzero-backed data model utilities"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -5,9 +5,50 @@ from __future__ import annotations
5
5
  from datetime import date, datetime, timedelta
6
6
  from unittest.mock import patch
7
7
 
8
+ import dbzero as db0
9
+
8
10
  from dbzero_modelkit.active import ActiveBase, ActiveIndex
9
11
 
10
12
 
13
+ def test_active_model_type_ids(db0_fixture):
14
+ active_base = ActiveBase()
15
+ active_index = ActiveIndex()
16
+
17
+ assert (
18
+ db0.get_type_stats(type(active_base))["type_id"]
19
+ == "/dbzero/dbzero-modelkit/ActiveBase"
20
+ )
21
+ assert (
22
+ db0.get_type_stats(type(active_index))["type_id"]
23
+ == "/dbzero/dbzero-modelkit/ActiveIndex"
24
+ )
25
+
26
+
27
+ def test_active_models_accept_keyword_only_prefix(db0_fixture):
28
+ db0.open("alternate_prefix", "rw")
29
+
30
+ active_base = ActiveBase(prefix="alternate_prefix")
31
+ active_index = ActiveIndex(prefix="alternate_prefix")
32
+
33
+ assert db0.get_prefix_of(active_base).name == "alternate_prefix"
34
+ assert db0.get_prefix_of(active_index).name == "alternate_prefix"
35
+ assert db0.get_prefix_of(active_index.active_from_index).name == "alternate_prefix"
36
+ assert db0.get_prefix_of(active_index.expires_on_index).name == "alternate_prefix"
37
+
38
+
39
+ def test_active_base_subclass_without_prefix_remains_compatible(db0_fixture):
40
+ @db0.memo(no_default_tags=True)
41
+ class TimedThing(ActiveBase):
42
+ def __init__(self, name: str) -> None:
43
+ super().__init__()
44
+ self.name = name
45
+
46
+ thing = TimedThing("night shift")
47
+
48
+ assert thing.name == "night shift"
49
+ assert thing.active is True
50
+
51
+
11
52
  class DatetimeMock:
12
53
  """Deterministic datetime replacement for active-window tests."""
13
54
 
@@ -11,6 +11,60 @@ import pytest
11
11
  from dbzero_modelkit.calendars import Calendar, MonthCalendar, get_month_index
12
12
 
13
13
 
14
+ def test_calendar_model_type_ids(db0_fixture):
15
+ calendar = Calendar()
16
+ month_calendar = MonthCalendar(calendar, 0)
17
+
18
+ assert db0.get_type_stats(type(calendar))["type_id"] == "/dbzero/dbzero-modelkit/Calendar"
19
+ assert (
20
+ db0.get_type_stats(type(month_calendar))["type_id"]
21
+ == "/dbzero/dbzero-modelkit/MonthCalendar"
22
+ )
23
+
24
+
25
+ def test_calendar_prefix_propagates_to_created_month(db0_fixture):
26
+ db0.open("calendar_prefix", "rw")
27
+ calendar = Calendar(prefix="calendar_prefix")
28
+
29
+ month = calendar.get_month(date(2025, 1, 1), create=True)
30
+
31
+ assert db0.get_prefix_of(calendar).name == "calendar_prefix"
32
+ assert db0.get_prefix_of(month).name == "calendar_prefix"
33
+
34
+
35
+ def test_calendar_default_prefix_propagates_to_created_month(db0_fixture):
36
+ calendar = Calendar()
37
+
38
+ month = calendar.get_month(date(2025, 1, 1), create=True)
39
+
40
+ assert db0.get_prefix_of(calendar).name == "test_prefix"
41
+ assert db0.get_prefix_of(month).name == "test_prefix"
42
+
43
+
44
+ def test_calendar_child_prefix_survives_reopen(tmp_path):
45
+ db0_path = tmp_path / "db0"
46
+ db0.init(str(db0_path), read_write=True)
47
+ db0.open("default_prefix", "rw")
48
+ db0.open("calendar_prefix", "rw")
49
+ calendar = Calendar(prefix="calendar_prefix")
50
+ month = calendar.get_month(date(2025, 1, 1), create=True)
51
+ calendar_uuid = db0.uuid(calendar)
52
+ month_uuid = db0.uuid(month)
53
+ db0.commit()
54
+ db0.close()
55
+
56
+ db0.init(str(db0_path), read_write=True)
57
+ db0.open("default_prefix", "rw")
58
+ db0.open("calendar_prefix", "rw")
59
+ persisted_calendar = db0.fetch(calendar_uuid)
60
+ persisted_month = persisted_calendar.get_month(date(2025, 1, 1))
61
+
62
+ assert db0.uuid(persisted_month) == month_uuid
63
+ assert db0.get_prefix_of(persisted_calendar).name == "calendar_prefix"
64
+ assert db0.get_prefix_of(persisted_month).name == "calendar_prefix"
65
+ db0.close()
66
+
67
+
14
68
  class MockDate:
15
69
  """Date-like object used to test invalid day validation."""
16
70
 
@@ -2,11 +2,32 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import inspect
6
+
7
+ import dbzero as db0
5
8
  import dbzero_modelkit
6
9
 
10
+ import dbzero_modelkit.language as language_module
7
11
  from dbzero_modelkit.language import LanguageCode, ML_String
8
12
 
9
13
 
14
+ def test_language_model_type_ids(db0_fixture):
15
+ text = ML_String("Dieta standardowa", LanguageCode.LPL)
16
+
17
+ assert db0.get_type_stats(type(text))["type_id"] == "/dbzero/dbzero-modelkit/ML_String"
18
+ # Future: Replace this with runtime enum metadata when dbzero exposes enum
19
+ # type_id introspection.
20
+ assert "type_id=\"/dbzero/dbzero-modelkit/LanguageCode\"" in inspect.getsource(language_module)
21
+
22
+
23
+ def test_language_models_use_requested_prefix(db0_fixture):
24
+ db0.open("language_prefix", "rw")
25
+
26
+ text = ML_String("Dieta standardowa", LanguageCode.LPL, prefix="language_prefix")
27
+
28
+ assert db0.get_prefix_of(text).name == "language_prefix"
29
+
30
+
10
31
  def test_language_code_enum_values(db0_fixture):
11
32
  assert hasattr(LanguageCode, "LEN")
12
33
  assert hasattr(LanguageCode, "LPL")
@@ -10,6 +10,27 @@ import pytest
10
10
  from dbzero_modelkit.month_store import MonthStore
11
11
 
12
12
 
13
+ def test_month_store_model_type_id(db0_fixture):
14
+ @db0.memo
15
+ class TestItem:
16
+ pass
17
+
18
+ month_store = MonthStore(TestItem)
19
+
20
+ assert db0.get_type_stats(type(month_store))["type_id"] == "/dbzero/dbzero-modelkit/MonthStore"
21
+
22
+
23
+ def test_month_store_accepts_keyword_only_prefix(db0_fixture):
24
+ @db0.memo
25
+ class TestItem:
26
+ pass
27
+
28
+ db0.open("month_store_prefix", "rw")
29
+ month_store = MonthStore(TestItem, prefix="month_store_prefix")
30
+
31
+ assert db0.get_prefix_of(month_store).name == "month_store_prefix"
32
+
33
+
13
34
  def test_month_store_can_be_created(db0_fixture):
14
35
  @db0.memo
15
36
  class TestItem:
@@ -10,6 +10,45 @@ import dbzero as db0
10
10
  from dbzero_modelkit.object_lock import ObjectLock
11
11
 
12
12
 
13
+ def test_object_lock_model_type_id(db0_fixture):
14
+ @db0.memo
15
+ class TestObj:
16
+ pass
17
+
18
+ lock = ObjectLock(TestObj())
19
+
20
+ assert db0.get_type_stats(type(lock))["type_id"] == "/dbzero/dbzero-modelkit/ObjectLock"
21
+
22
+
23
+ def test_object_lock_accepts_keyword_only_prefix(db0_fixture):
24
+ @db0.memo
25
+ class TestObj:
26
+ pass
27
+
28
+ db0.open("lock_prefix", "rw")
29
+ item = TestObj()
30
+ lock = ObjectLock(item, prefix="lock_prefix")
31
+
32
+ assert db0.get_prefix_of(lock).name == "lock_prefix"
33
+ assert db0.find(item, "LOCKED")
34
+
35
+
36
+ def test_object_lock_can_tag_object_from_different_prefix(db0_fixture):
37
+ @db0.memo
38
+ class TestObj:
39
+ pass
40
+
41
+ db0.open("item_prefix", "rw")
42
+ item = TestObj()
43
+ db0.open("lock_prefix", "rw")
44
+
45
+ lock = ObjectLock(item, prefix="lock_prefix")
46
+
47
+ assert db0.get_prefix_of(item).name == "item_prefix"
48
+ assert db0.get_prefix_of(lock).name == "lock_prefix"
49
+ assert db0.find(item, "LOCKED", prefix="item_prefix")
50
+
51
+
13
52
  class DatetimeMock:
14
53
  """Deterministic datetime replacement for object lock tests."""
15
54
 
@@ -4,7 +4,34 @@ from __future__ import annotations
4
4
 
5
5
  import dbzero as db0
6
6
 
7
- from dbzero_modelkit.queues import FiFoQueue
7
+ from dbzero_modelkit.queues import FQ_Item, FiFoQueue
8
+
9
+
10
+ def test_queue_model_type_ids(db0_fixture):
11
+ queue = FiFoQueue()
12
+ item = FQ_Item(0)
13
+
14
+ assert db0.get_type_stats(type(queue))["type_id"] == "/dbzero/dbzero-modelkit/FiFoQueue"
15
+ assert db0.get_type_stats(type(item))["type_id"] == "/dbzero/dbzero-modelkit/FQ_Item"
16
+
17
+
18
+ def test_fifo_queue_accepts_keyword_only_prefix(db0_fixture):
19
+ db0.open("queue_prefix", "rw")
20
+
21
+ queue = FiFoQueue(prefix="queue_prefix")
22
+
23
+ assert db0.get_prefix_of(queue).name == "queue_prefix"
24
+ assert db0.get_prefix_of(queue._FiFoQueue__items).name == "queue_prefix"
25
+
26
+
27
+ def test_fifo_queue_items_use_queue_prefix(db0_fixture):
28
+ db0.open("queue_prefix", "rw")
29
+ queue = FiFoQueue(prefix="queue_prefix")
30
+
31
+ queue.push_back(value=1)
32
+ item = next(iter(queue._FiFoQueue__items.select()))
33
+
34
+ assert db0.get_prefix_of(item).name == "queue_prefix"
8
35
 
9
36
 
10
37
  def test_fifo_queue_can_be_created(db0_fixture):