Skip to content

Commit 5b61ca6

Browse files
committed
MAJOR NEW FEATURE: wired in the Type_Safe__Fast_Create feature (which uses Type_Safe__Config) to the main Type_Safe class (with no side effect)
1 parent f4a7841 commit 5b61ca6

File tree

4 files changed

+860
-1
lines changed

4 files changed

+860
-1
lines changed

osbot_utils/type_safe/Type_Safe.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# todo: find a way to add these documentations strings to a separate location so that
22
# the data is available in IDE's code complete
3+
from osbot_utils.type_safe.type_safe_core.config.Type_Safe__Config import get_active_config
4+
from osbot_utils.type_safe.type_safe_core.fast_create.Type_Safe__Fast_Create import type_safe_fast_create
5+
from osbot_utils.type_safe.type_safe_core.fast_create.Type_Safe__Fast_Create__Cache import type_safe_fast_create_cache
36
from osbot_utils.type_safe.type_safe_core.shared.Type_Safe__Validation import type_safe_validation
47
from osbot_utils.type_safe.type_safe_core.steps.Type_Safe__Step__Class_Kwargs import type_safe_step_class_kwargs
58
from osbot_utils.type_safe.type_safe_core.steps.Type_Safe__Step__Default_Kwargs import type_safe_step_default_kwargs
@@ -11,10 +14,17 @@
1114
class Type_Safe:
1215

1316
def __init__(self, **kwargs):
17+
config = get_active_config()
18+
if config and config.fast_create:
19+
if not type_safe_fast_create_cache.is_generating(type(self)):
20+
type_safe_fast_create.create(self, **kwargs)
21+
return
22+
1423
class_kwargs = self.__cls_kwargs__(provided_kwargs=kwargs)
1524
type_safe_step_init.init(self, class_kwargs, **kwargs)
1625

1726

27+
1828
def __enter__(self): return self
1929
def __exit__(self, exc_type, exc_val, exc_tb): pass
2030

osbot_utils/type_safe/type_safe_core/fast_create/Type_Safe__Fast_Create__Cache.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
# ═══════════════════════════════════════════════════════════════════════════════
1212

1313
from typing import Any, Dict, Set, Type
14-
from osbot_utils.type_safe.Type_Safe import Type_Safe
1514
from osbot_utils.type_safe.Type_Safe__Primitive import Type_Safe__Primitive
1615
from osbot_utils.type_safe.type_safe_core.collections.Type_Safe__Dict import Type_Safe__Dict
1716
from osbot_utils.type_safe.type_safe_core.collections.Type_Safe__List import Type_Safe__List
@@ -133,6 +132,7 @@ def is_type_safe_collection(self, value: Any) -> bool: #
133132
return isinstance(value, (Type_Safe__List, Type_Safe__Dict, Type_Safe__Set, list, dict, set))
134133

135134
def is_nested_type_safe(self, value: Any) -> bool: # Check if value is nested Type_Safe
135+
from osbot_utils.type_safe.Type_Safe import Type_Safe # needs to be done here due to circular dependency
136136
if not isinstance(value, Type_Safe):
137137
return False
138138
if isinstance(value, Type_Safe__Primitive): # Primitives are not nested
Lines changed: 303 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,303 @@
1+
# ═══════════════════════════════════════════════════════════════════════════════
2+
# Tests: Type_Safe Fast Create - Created and Cached
3+
# These tests verify MECHANISM - they FAIL without Type_Safe wiring in place
4+
# ═══════════════════════════════════════════════════════════════════════════════
5+
#
6+
# PURPOSE: Verify that fast_create is ACTUALLY BEING USED by checking:
7+
# - Schema cache is populated after creation
8+
# - Schema cache is NOT populated when fast_create=False
9+
# - Cache warming works via creation
10+
# - Nested class schemas are cached recursively
11+
#
12+
# REQUIRES: Type_Safe wiring in place:
13+
# - Type_Safe.__init__ checks config.fast_create and delegates to fast_create
14+
# - Type_Safe.__setattr__ checks config.skip_validation
15+
#
16+
# WITHOUT WIRING: All tests in this file should FAIL (cache never populated)
17+
#
18+
# ═══════════════════════════════════════════════════════════════════════════════
19+
20+
from typing import Dict, List
21+
from unittest import TestCase
22+
from osbot_utils.type_safe.Type_Safe import Type_Safe
23+
from osbot_utils.type_safe.type_safe_core.config.Type_Safe__Config import Type_Safe__Config
24+
from osbot_utils.type_safe.type_safe_core.fast_create.Type_Safe__Fast_Create__Cache import type_safe_fast_create_cache
25+
26+
27+
# ═══════════════════════════════════════════════════════════════════════════════
28+
# Test Classes
29+
# ═══════════════════════════════════════════════════════════════════════════════
30+
31+
class TS__Simple(Type_Safe):
32+
name : str = ''
33+
count : int = 0
34+
active : bool = False
35+
36+
37+
class TS__With_Collections(Type_Safe):
38+
name : str = ''
39+
items : List[str]
40+
data : Dict[str, int]
41+
42+
43+
class TS__Inner(Type_Safe):
44+
value : str = ''
45+
count : int = 0
46+
47+
48+
class TS__With_Nested(Type_Safe):
49+
inner : TS__Inner
50+
name : str = ''
51+
52+
53+
class TS__Deep_Level3(Type_Safe):
54+
data : str = ''
55+
56+
57+
class TS__Deep_Level2(Type_Safe):
58+
level3 : TS__Deep_Level3
59+
value : int = 0
60+
61+
62+
class TS__Deep_Level1(Type_Safe):
63+
level2 : TS__Deep_Level2
64+
name : str = ''
65+
66+
67+
class TS__Deep(Type_Safe):
68+
level1 : TS__Deep_Level1
69+
count : int = 0
70+
71+
72+
# ═══════════════════════════════════════════════════════════════════════════════
73+
# Test: Created and Cached (Mechanism Verification)
74+
# ═══════════════════════════════════════════════════════════════════════════════
75+
76+
class test_Type_Safe__Fast_Create__created_and_cached(TestCase):
77+
"""
78+
These tests verify that fast_create mechanism is actually being invoked.
79+
They check cache state which only changes when fast_create path is used.
80+
81+
WITHOUT WIRING: All these tests FAIL because cache is never populated.
82+
WITH WIRING: All these tests PASS.
83+
"""
84+
85+
def setUp(self):
86+
type_safe_fast_create_cache.clear_cache()
87+
88+
# ───────────────────────────────────────────────────────────────────────────
89+
# Basic Cache Population
90+
# ───────────────────────────────────────────────────────────────────────────
91+
92+
def test__fast_create__populates_cache_for_simple_class(self): # Schema cached on first use
93+
assert TS__Simple not in type_safe_fast_create_cache.schema_cache # Empty before
94+
95+
with Type_Safe__Config(fast_create=True):
96+
_ = TS__Simple()
97+
98+
assert TS__Simple in type_safe_fast_create_cache.schema_cache # Cached after
99+
100+
def test__fast_create__populates_cache_for_collections_class(self): # Collections class cached
101+
assert TS__With_Collections not in type_safe_fast_create_cache.schema_cache
102+
103+
with Type_Safe__Config(fast_create=True):
104+
_ = TS__With_Collections()
105+
106+
assert TS__With_Collections in type_safe_fast_create_cache.schema_cache
107+
108+
def test__fast_create__populates_cache_for_nested_class(self): # Parent class cached
109+
assert TS__With_Nested not in type_safe_fast_create_cache.schema_cache
110+
111+
with Type_Safe__Config(fast_create=True):
112+
_ = TS__With_Nested()
113+
114+
assert TS__With_Nested in type_safe_fast_create_cache.schema_cache
115+
116+
# ───────────────────────────────────────────────────────────────────────────
117+
# Recursive Cache Population (Nested Classes)
118+
# ───────────────────────────────────────────────────────────────────────────
119+
120+
def test__fast_create__caches_nested_class_recursively(self): # Inner class also cached
121+
assert TS__With_Nested not in type_safe_fast_create_cache.schema_cache
122+
assert TS__Inner not in type_safe_fast_create_cache.schema_cache
123+
124+
with Type_Safe__Config(fast_create=True):
125+
_ = TS__With_Nested()
126+
127+
assert TS__With_Nested in type_safe_fast_create_cache.schema_cache
128+
assert TS__Inner in type_safe_fast_create_cache.schema_cache # Also cached!
129+
130+
def test__fast_create__caches_deep_nested_classes_recursively(self): # All levels cached
131+
assert TS__Deep not in type_safe_fast_create_cache.schema_cache
132+
assert TS__Deep_Level1 not in type_safe_fast_create_cache.schema_cache
133+
assert TS__Deep_Level2 not in type_safe_fast_create_cache.schema_cache
134+
assert TS__Deep_Level3 not in type_safe_fast_create_cache.schema_cache
135+
136+
with Type_Safe__Config(fast_create=True):
137+
_ = TS__Deep()
138+
139+
assert TS__Deep in type_safe_fast_create_cache.schema_cache # Level 0
140+
assert TS__Deep_Level1 in type_safe_fast_create_cache.schema_cache # Level 1
141+
assert TS__Deep_Level2 in type_safe_fast_create_cache.schema_cache # Level 2
142+
assert TS__Deep_Level3 in type_safe_fast_create_cache.schema_cache # Level 3
143+
144+
# ───────────────────────────────────────────────────────────────────────────
145+
# Cache NOT Populated Without fast_create
146+
# ───────────────────────────────────────────────────────────────────────────
147+
148+
def test__normal_mode__does_not_populate_cache(self): # Normal creation skips cache
149+
assert TS__Simple not in type_safe_fast_create_cache.schema_cache
150+
151+
_ = TS__Simple() # Normal creation (no config)
152+
153+
assert TS__Simple not in type_safe_fast_create_cache.schema_cache # Still empty!
154+
155+
def test__fast_create_false__does_not_populate_cache(self): # Explicit False skips cache
156+
assert TS__Simple not in type_safe_fast_create_cache.schema_cache
157+
158+
with Type_Safe__Config(fast_create=False):
159+
_ = TS__Simple()
160+
161+
assert TS__Simple not in type_safe_fast_create_cache.schema_cache # Still empty!
162+
163+
# ───────────────────────────────────────────────────────────────────────────
164+
# Multiple Creations Use Same Cached Schema
165+
# ───────────────────────────────────────────────────────────────────────────
166+
167+
def test__fast_create__second_creation_uses_cached_schema(self): # Cache hit on second create
168+
with Type_Safe__Config(fast_create=True):
169+
_ = TS__Simple()
170+
171+
schema_after_first = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
172+
assert schema_after_first is not None
173+
174+
with Type_Safe__Config(fast_create=True):
175+
_ = TS__Simple()
176+
177+
schema_after_second = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
178+
assert schema_after_second is schema_after_first # Same instance!
179+
180+
def test__fast_create__batch_creation_uses_same_schema(self): # All batch objects use same schema
181+
with Type_Safe__Config(fast_create=True):
182+
_ = TS__Simple()
183+
184+
schema = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
185+
assert schema is not None
186+
187+
cache_size_after_one = len(type_safe_fast_create_cache.schema_cache)
188+
189+
with Type_Safe__Config(fast_create=True):
190+
for _ in range(100):
191+
_ = TS__Simple()
192+
193+
# Cache size unchanged - all used same schema
194+
assert len(type_safe_fast_create_cache.schema_cache) == cache_size_after_one
195+
assert type_safe_fast_create_cache.schema_cache.get(TS__Simple) is schema
196+
197+
# ───────────────────────────────────────────────────────────────────────────
198+
# Schema Content Verification
199+
# ───────────────────────────────────────────────────────────────────────────
200+
201+
def test__fast_create__cached_schema_has_correct_target_class(self): # Schema points to right class
202+
with Type_Safe__Config(fast_create=True):
203+
_ = TS__Simple()
204+
205+
schema = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
206+
assert schema.target_class is TS__Simple
207+
208+
def test__fast_create__cached_schema_has_correct_fields(self): # Schema has all fields
209+
with Type_Safe__Config(fast_create=True):
210+
_ = TS__Simple()
211+
212+
schema = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
213+
field_names = {f.name for f in schema.fields}
214+
215+
assert 'name' in field_names
216+
assert 'count' in field_names
217+
assert 'active' in field_names
218+
219+
def test__fast_create__cached_schema_has_static_dict(self): # Schema has pre-built static_dict
220+
with Type_Safe__Config(fast_create=True):
221+
_ = TS__Simple()
222+
223+
schema = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
224+
225+
assert 'name' in schema.static_dict
226+
assert 'count' in schema.static_dict
227+
assert 'active' in schema.static_dict
228+
assert schema.static_dict['name'] == ''
229+
assert schema.static_dict['count'] == 0
230+
assert schema.static_dict['active'] is False
231+
232+
def test__fast_create__cached_schema_has_factory_fields(self): # Collections have factory funcs
233+
with Type_Safe__Config(fast_create=True):
234+
_ = TS__With_Collections()
235+
236+
schema = type_safe_fast_create_cache.schema_cache.get(TS__With_Collections)
237+
238+
factory_names = {f.name for f in schema.factory_fields}
239+
assert 'items' in factory_names
240+
assert 'data' in factory_names
241+
242+
def test__fast_create__cached_schema_has_nested_fields(self): # Nested fields identified
243+
with Type_Safe__Config(fast_create=True):
244+
_ = TS__With_Nested()
245+
246+
schema = type_safe_fast_create_cache.schema_cache.get(TS__With_Nested)
247+
248+
assert len(schema.nested_fields) == 1
249+
assert schema.nested_fields[0].name == 'inner'
250+
assert schema.nested_fields[0].nested_class is TS__Inner
251+
252+
# ───────────────────────────────────────────────────────────────────────────
253+
# Cache State After Context Exit
254+
# ───────────────────────────────────────────────────────────────────────────
255+
256+
def test__fast_create__cache_persists_after_context_exit(self): # Cache survives context
257+
with Type_Safe__Config(fast_create=True):
258+
_ = TS__Simple()
259+
260+
# Outside context - cache still has schema
261+
assert TS__Simple in type_safe_fast_create_cache.schema_cache
262+
263+
def test__fast_create__cache_persists_across_multiple_contexts(self): # Cache shared across contexts
264+
with Type_Safe__Config(fast_create=True):
265+
_ = TS__Simple()
266+
267+
schema_from_first = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
268+
269+
with Type_Safe__Config(fast_create=True):
270+
_ = TS__Simple()
271+
272+
schema_from_second = type_safe_fast_create_cache.schema_cache.get(TS__Simple)
273+
274+
assert schema_from_first is schema_from_second # Same schema object
275+
276+
# ───────────────────────────────────────────────────────────────────────────
277+
# Mixed Usage: Some With fast_create, Some Without
278+
# ───────────────────────────────────────────────────────────────────────────
279+
280+
def test__mixed_usage__fast_create_populates_normal_does_not(self): # Only fast path caches
281+
# Normal creation - no cache
282+
_ = TS__Simple()
283+
assert TS__Simple not in type_safe_fast_create_cache.schema_cache
284+
285+
# Fast creation - populates cache
286+
with Type_Safe__Config(fast_create=True):
287+
_ = TS__With_Collections()
288+
289+
assert TS__Simple not in type_safe_fast_create_cache.schema_cache # Still not cached
290+
assert TS__With_Collections in type_safe_fast_create_cache.schema_cache # Now cached
291+
292+
def test__mixed_usage__different_classes_cached_independently(self): # Each class cached separately
293+
with Type_Safe__Config(fast_create=True):
294+
_ = TS__Simple()
295+
296+
assert TS__Simple in type_safe_fast_create_cache.schema_cache
297+
assert TS__With_Collections not in type_safe_fast_create_cache.schema_cache
298+
299+
with Type_Safe__Config(fast_create=True):
300+
_ = TS__With_Collections()
301+
302+
assert TS__Simple in type_safe_fast_create_cache.schema_cache
303+
assert TS__With_Collections in type_safe_fast_create_cache.schema_cache

0 commit comments

Comments
 (0)