From 0509487b6293852a412015442fc40c610908b362 Mon Sep 17 00:00:00 2001 From: "Sam G." Date: Thu, 18 Apr 2024 12:31:16 -0700 Subject: [PATCH] loosen type Mapper args, add more Database tests --- co3/accessors/sql.py | 16 +++++++++++----- co3/collector.py | 6 +++--- co3/mapper.py | 34 +++++++++++++++++++++------------- tests/test_co3.py | 8 ++++---- tests/test_database.py | 18 ++++++++++++++++++ tests/test_mapper.py | 8 ++++---- 6 files changed, 61 insertions(+), 29 deletions(-) diff --git a/co3/accessors/sql.py b/co3/accessors/sql.py index 9047ac5..d3ab8be 100644 --- a/co3/accessors/sql.py +++ b/co3/accessors/sql.py @@ -56,7 +56,7 @@ class RelationalAccessor[R: Relation](Accessor[R]): connection, text: str ): - connection.exec + #connection.exec raise NotImplementedError def select( @@ -104,11 +104,17 @@ class SQLAccessor(RelationalAccessor[SQLTable]): mappings=False, include_cols=False, ): - res_method = utils.db.sa_exec_dicts - if mappings: - res_method = utils.db.sa_exec_mappings + res = SQLEngine.execute( + connection, + sql, + bind_params=bind_params, + include_cols=include_cols + ) - return res_method(self.database.engine, sa.text(sql), bind_params=bind_params, include_cols=include_cols) + if mappings: + return res.mappings().all() + else: + return self.result_dicts(res) def select( self, diff --git a/co3/collector.py b/co3/collector.py index 1d473e1..27c66cd 100644 --- a/co3/collector.py +++ b/co3/collector.py @@ -37,13 +37,13 @@ class Collector[C: Component]: def __init__(self, schema: Schema[C]): self.schema = schema - self._inserts = defaultdict(lambda: defaultdict(list)) + self._inserts = {} @property def inserts(self): return self._inserts_from_receipts() - def _inserts_from_receipts(self, receipts: list=None, pop=False): + def _inserts_from_receipts(self, receipts: list[str]|None=None, pop=False): ''' Group up added inserts by Component, often to be used directly for bulk insertion. Optionally provide a list of `receipts` to group up only the corresponding subset of @@ -104,7 +104,7 @@ class Collector[C: Component]: return receipt - def collect_inserts(self, receipts=None): + def collect_inserts(self, receipts:list[str]|None=None): ''' Collect insert-ready dictionaries for the core primitive schema. This method is effectively a light wrapper around the File and Note-based collection logic diff --git a/co3/mapper.py b/co3/mapper.py index f8d06d4..912423b 100644 --- a/co3/mapper.py +++ b/co3/mapper.py @@ -157,17 +157,25 @@ class Mapper[C: Component]: self.attach(_type, attr_comp, coll_groups=coll_groups) - def get_attribute_comp( + def get_attr_comp( self, - type_ref: type[CO3] + co3_ref: CO3 | type[CO3] ) -> C | None: + type_ref = co3_ref + if isinstance(co3_ref, CO3): + type_ref = co3_ref.__class__ + return self.attribute_comps.get(type_ref, None) - def get_collation_comp( + def get_coll_comp( self, - type_ref: type[CO3], + co3_ref: CO3 | type[CO3], group=str | None, ) -> C | None: + type_ref = co3_ref + if isinstance(co3_ref, CO3): + type_ref = co3_ref.__class__ + return self.collation_groups.get(type_ref, {}).get(group, None) def collect( @@ -201,7 +209,7 @@ class Mapper[C: Component]: receipts = [] for _cls in reversed(obj.__class__.__mro__[:-2]): - attribute_component = self.get_attribute_comp(_cls) + attribute_component = self.get_attr_comp(_cls) # require an attribute component for type consideration if attribute_component is None: @@ -222,7 +230,7 @@ class Mapper[C: Component]: _, action_groups = obj.action_registry.get(action_key, (None, [])) for action_group in action_groups: - collation_component = self.get_collation_comp(_cls, group=action_group) + collation_component = self.get_coll_comp(_cls, group=action_group) if collation_component is None: continue @@ -331,7 +339,7 @@ class ComposableMapper[C: ComposableComponent](Mapper[C]): def compose( self, - obj: CO3 | type[CO3], + co3_ref: CO3 | type[CO3], action_groups: list[str] | None = None, *compose_args, **compose_kwargs, @@ -348,13 +356,13 @@ class ComposableMapper[C: ComposableComponent](Mapper[C]): Parameters: obj: either a CO3 instance or a type reference ''' - class_ref = obj - if isinstance(obj, CO3): - class_ref = obj.__class__ + type_ref = co3_ref + if isinstance(co3_ref, CO3): + type_ref = co3_ref.__class__ attr_comp_agg = None - for _cls in reversed(class_ref.__mro__[:-2]): - attr_comp = self.get_attribute_comp(_cls) + for _cls in reversed(type_ref.__mro__[:-2]): + attr_comp = self.get_attr_comp(_cls) # require an attribute component for type consideration if attr_comp is None: @@ -364,7 +372,7 @@ class ComposableMapper[C: ComposableComponent](Mapper[C]): coll_comp_agg = attr_comp if action_groups is not None: for action_group in action_groups: - coll_comp = self.get_collation_comp(_cls, group=action_group) + coll_comp = self.get_coll_comp(_cls, group=action_group) if coll_comp is None: continue diff --git a/tests/test_co3.py b/tests/test_co3.py index 4043b40..65538ff 100644 --- a/tests/test_co3.py +++ b/tests/test_co3.py @@ -7,14 +7,14 @@ def test_mapper_getters(): veg_comp = veg.vegetable_schema.get_component('vegetable') tom_comp = veg.vegetable_schema.get_component('tomato') - assert veg.vegetable_mapper.get_attribute_comp(veg.Vegetable) is veg_comp - assert veg.vegetable_mapper.get_attribute_comp(veg.Tomato) is tom_comp + assert veg.vegetable_mapper.get_attr_comp(veg.Vegetable) is veg_comp + assert veg.vegetable_mapper.get_attr_comp(veg.Tomato) is tom_comp tom_aging = veg.vegetable_schema.get_component('tomato_aging_states') tom_cooking = veg.vegetable_schema.get_component('tomato_cooking_states') - assert veg.vegetable_mapper.get_collation_comp(veg.Tomato, 'aging') is tom_aging - assert veg.vegetable_mapper.get_collation_comp(veg.Tomato, 'cooking') is tom_cooking + assert veg.vegetable_mapper.get_coll_comp(veg.Tomato, 'aging') is tom_aging + assert veg.vegetable_mapper.get_coll_comp(veg.Tomato, 'cooking') is tom_cooking def test_mapper_attach(): assert veg.vegetable_mapper.attach( diff --git a/tests/test_database.py b/tests/test_database.py index c6c33a5..71e8013 100644 --- a/tests/test_database.py +++ b/tests/test_database.py @@ -20,6 +20,24 @@ def test_database_insert(): tomato = veg.Tomato('t1', 5) veg.vegetable_mapper.collect(tomato) + # test instance as arg + tom_comp = veg.vegetable_mapper.get_attr_comp(tomato) + + inserts = veg.vegetable_mapper.collector.collect_inserts() + tom_inserts = inserts.get(tom_comp) + assert tom_inserts is not None + + with db.engine.connect() as connection: + assert db.manager.insert( + connection, + tom_comp, + tom_inserts, + ) is not None + +def test_database_insert_many(): + tomato = veg.Tomato('t2', 5) + veg.vegetable_mapper.collect(tomato) + with db.engine.connect() as connection: assert db.manager.insert_many( connection, diff --git a/tests/test_mapper.py b/tests/test_mapper.py index feb120b..4732de9 100644 --- a/tests/test_mapper.py +++ b/tests/test_mapper.py @@ -7,14 +7,14 @@ def test_mapper_getters(): veg_comp = veg.vegetable_schema.get_component('vegetable') tom_comp = veg.vegetable_schema.get_component('tomato') - assert veg.vegetable_mapper.get_attribute_comp(veg.Vegetable) is veg_comp - assert veg.vegetable_mapper.get_attribute_comp(veg.Tomato) is tom_comp + assert veg.vegetable_mapper.get_attr_comp(veg.Vegetable) is veg_comp + assert veg.vegetable_mapper.get_attr_comp(veg.Tomato) is tom_comp tom_aging = veg.vegetable_schema.get_component('tomato_aging_states') tom_cooking = veg.vegetable_schema.get_component('tomato_cooking_states') - assert veg.vegetable_mapper.get_collation_comp(veg.Tomato, 'aging') is tom_aging - assert veg.vegetable_mapper.get_collation_comp(veg.Tomato, 'cooking') is tom_cooking + assert veg.vegetable_mapper.get_coll_comp(veg.Tomato, 'aging') is tom_aging + assert veg.vegetable_mapper.get_coll_comp(veg.Tomato, 'cooking') is tom_cooking def test_mapper_attach(): assert veg.vegetable_mapper.attach(