perform minor, partial reformatting

This commit is contained in:
2026-01-02 17:26:28 -08:00
parent 090c122c60
commit c566dcf678
25 changed files with 1431 additions and 140 deletions

62
test/co3_api_demo.py Normal file
View File

@@ -0,0 +1,62 @@
'''
content -> converted
state -> transformed
'''
from co3 import CO3, collate
from co3.databases import SQLiteDatabase
from co3 import Mapper
class Tomato(CO3):
def __init__(self, size, ripe):
self.size = size
self.ripe = ripe
@property
def attributes(self):
return vars(self)
@collate('diced')
def dice(self):
return self.size / 2
@collate('roasted')
def roast(self):
return self.size / 2
metadata = sa.MetaData()
tomato_table = sa.Table(
'tomato',
metadata,
sa.Column('id', sa.Integer, primary_key=True),
)
tomato_schema = Schema.from_metadata(metadata)
mapper = Mapper()
mapper.attach(
Tomato,
tomato_table
)
tomato = Tomato(5, False)
mapper.collect(tomato, for='diced')
db = SQLiteDatabse('resource.sqlite')
db.recreate(tomato_schema)
# for non-raw DB ops, consider requiring a verify step first. Keeps up integrity between
# runs
db.verify(tomato_schema)
# then
# not too straightforward, but can also verify mapper compositions if they use a schema
# that's been verified by the database
db.sync(mapper)
dict_results = db.select(
mapper.compose(Tomato),
tomato_table.c.size == 5
)

5
test/co3_medium_demo.py Normal file
View File

@@ -0,0 +1,5 @@
from co3.mediums import Disk
disk = Disk('disk:///')
disk.browse('dir://home')

152
test/setups/vegetables.py Normal file
View File

@@ -0,0 +1,152 @@
'''
just remembered tomatos aren't vegetables. whoops
'''
import random
import sqlalchemy as sa
from co3.schemas import SQLSchema
from co3 import CO3, collate, Mapper, ComposableMapper
from co3 import util
class Vegetable(CO3):
def __init__(self, name, color):
super().__init__()
self.name = name
self.color = color
#@abstractmethod
@collate
def cut(self, method):
raise NotImplementedError
class Tomato(Vegetable):
def __init__(self, name, radius):
super().__init__(name, 'red')
self.radius = radius
@property
def attributes(self):
return vars(self)
def collation_attributes(self, key, group):
return {
'name': self.name,
'state': key,
}
@collate('ripe', groups=['aging'])
def ripen(self):
return {
'age': random.randint(1, 6)
}
@collate('rotten', groups=['aging'])
def rot(self):
return {
'age': random.randint(4, 9)
}
@collate('diced', groups=['cooking'])
def dice(self):
return {
'pieces': random.randint(2, 12)
}
@collate
def cut(self, method):
if method == 'slice':
return {
'pieces': random.randint(2, 5)
}
elif method == 'dice':
return self.dice()
type_list = [Vegetable, Tomato]
'''
VEGETABLE
|
TOMATO -- AGING
|
-- COOKING
Note: foreign keys need to represent values that could be known by objects _without_ first interacting
with a DB. This is slightly non-standard, given how common it is to depend on another table's integer ID
(typically a value assigned by the DB using an autoincrement, for example, and not specified explicitly
within the insertion body). As a result, SQLTable components need to be able to operate by another unique
key when expected to connect to other tables in the hierarchy. Below we use `name` with a UNIQUE constraint
for this purpose. Note that having an integer `id` is still perfectly okay so that a table can manage
uniqueness of its own rows by default.
'''
metadata = sa.MetaData()
vegetable_table = sa.Table(
'vegetable',
metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String, unique=True),
sa.Column('color', sa.String),
)
tomato_table = sa.Table(
'tomato',
metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String, util.db.deferred_cd_fkey('vegetable.name'), unique=True),
sa.Column('radius', sa.Integer),
)
tomato_aging_table = sa.Table(
'tomato_aging_states',
metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String, util.db.deferred_cd_fkey('tomato.name'), unique=True),
sa.Column('state', sa.String),
sa.Column('age', sa.Integer),
)
tomato_cooking_table = sa.Table(
'tomato_cooking_states',
metadata,
sa.Column('id', sa.Integer, primary_key=True),
sa.Column('name', sa.String, util.db.deferred_cd_fkey('tomato.name'), unique=True),
sa.Column('state', sa.String),
sa.Column('pieces', sa.Integer),
)
vegetable_schema = SQLSchema.from_metadata(metadata)
def general_compose_map(c1, c2):
return c1.obj.c.name == c2.obj.c.name
vegetable_mapper = ComposableMapper(
vegetable_schema,
attr_compose_map=general_compose_map,
coll_compose_map=general_compose_map,
)
def attr_name_map(cls):
return f'{cls.__name__.lower()}'
def coll_name_map(cls, group):
return f'{cls.__name__.lower()}_{group}_states'
vegetable_mapper.attach_many(
type_list,
attr_name_map,
coll_name_map,
)
'''
new mapping type for Mapper attachment:
Callable[ [type[CO3], str|None], tuple[str, tuple[str], tuple[str]]]
tail tuples to associate column names from central table to collation
this should complete the auto-compose horizontally
'''

39
test/test_co3.py Normal file
View File

@@ -0,0 +1,39 @@
from collections import defaultdict
from co3.components import Relation
from setups import vegetables as veg
tomato = veg.Tomato('t1', 10)
def test_co3_registry():
keys_to_groups = defaultdict(list)
# collect groups each key is associated
for group, keys in tomato.group_registry.items():
for key in keys:
keys_to_groups[key].append(group)
assert set(tomato.key_registry.get(None,{}).keys()) == set(keys_to_groups.get(None,[]))
# check against `registry`, should map keys to all groups
for key, group_dict in tomato.key_registry.items():
assert keys_to_groups.get(key) == list(group_dict.keys())
def test_co3_attributes():
assert tomato.attributes is not None
def test_co3_components():
assert tomato.components is not None
def test_co3_collation_attributes():
for group, keys in tomato.group_registry.items():
for key in keys:
assert tomato.collation_attributes(key, group) is not None
def test_co3_collate():
for group, keys in tomato.group_registry.items():
for key in keys:
if key is None: continue
assert tomato.collate(key, group=group) is not None

54
test/test_database.py Normal file
View File

@@ -0,0 +1,54 @@
from co3.components import Relation
from co3.databases import SQLDatabase
from setups import vegetables as veg
db = None
def test_database_init():
global db
db = SQLDatabase('sqlite://')
assert True
def test_database_recreate():
db.recreate(veg.vegetable_schema)
assert True
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,
veg.vegetable_mapper.collector.inserts,
) is not None
def test_database_access():
agg_table = veg.vegetable_mapper.compose(veg.Tomato)
with db.engine.connect() as connection:
assert db.accessor.select(
connection,
agg_table,
) is not None

25
test/test_imports.py Normal file
View File

@@ -0,0 +1,25 @@
def test_import():
from co3 import Accessor
from co3 import CO3
from co3 import Collector
from co3 import Database
from co3 import Indexer
from co3 import Manager
from co3 import Mapper
from co3 import Component
from co3.accessors import SQLAccessor
from co3.accessors import FTSAccessor
from co3.accessors import VSSAccessor
from co3.databases import SQLDatabase
from co3.databases import SQLiteDatabase
from co3.managers import SQLManager
from co3.components import ComposableComponent
from co3.components import Relation
from co3.components import SQLTable
assert True

56
test/test_mapper.py Normal file
View File

@@ -0,0 +1,56 @@
from co3.components import Relation
from setups import vegetables as veg
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_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_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(
veg.Tomato,
'tomato',
coll_groups={
'aging': 'tomato_aging_states',
'cooking': 'tomato_cooking_states',
},
) is None
def test_mapper_attach_many():
assert veg.vegetable_mapper.attach_many(
[veg.Vegetable, veg.Tomato],
lambda t: f'{t.__name__.lower()}'
) is None
def test_mapper_collect():
tomato = veg.Tomato('t1', 10)
receipts = veg.vegetable_mapper.collect(tomato)
assert len(receipts) == 2
# attempt to retrieve receipts one at a time
res1 = veg.vegetable_mapper.collector.collect_inserts([receipts[0]])
assert len(res1) == 1 # should be just one match
assert len(res1[next(iter(res1.keys()))]) == 1 # and one dict for matching comp
# try again, check no persistent match
res1 = veg.vegetable_mapper.collector.collect_inserts([receipts[0]])
assert len(res1) == 0 # should be no matches for the same receipt
res2 = veg.vegetable_mapper.collector.collect_inserts([receipts[1]])
assert len(res2) == 1
assert len(res2[next(iter(res2.keys()))]) == 1

26
test/test_schema.py Normal file
View File

@@ -0,0 +1,26 @@
from co3.components import Relation
from setups import vegetables as veg
def test_schema_get():
veg_comp_raw = veg.vegetable_schema._component_map.get('vegetable')
veg_comp = veg.vegetable_schema.get_component('vegetable')
assert veg_comp_raw is veg_comp
def test_schema_contains():
vegetable_comp = veg.vegetable_schema.get_component('vegetable')
tomato_comp = veg.vegetable_schema.get_component('tomato')
tomato_aging_comp = veg.vegetable_schema.get_component('tomato_aging_states')
assert vegetable_comp in veg.vegetable_schema
assert tomato_comp in veg.vegetable_schema
assert tomato_aging_comp in veg.vegetable_schema
def test_schema_add():
veg.vegetable_schema.add_component(Relation[int]('a', 1))
veg.vegetable_schema.add_component(Relation[int]('b', 2))
assert veg.vegetable_schema.get_component('a') is not None
assert veg.vegetable_schema.get_component('b') is not None