diff --git a/README.md b/README.md index 480168f..8e7e2af 100644 --- a/README.md +++ b/README.md @@ -5,8 +5,8 @@ etc. Objects inheriting from the `CO3` base class can then define data transform that connect to database components, and can be automatically collected for coordinated database insertion. -`co3` attempts to provide a general interface for interacting with a storage media (e.g., -database, pickled objects, VSS framework, in-memory key-value stores, etc). The following +`co3` attempts to provide a general interface for interacting with storage media (e.g., +databases, pickled objects, VSS framework, in-memory key-value stores, etc). The following top-level classes capture the bulk of the operational model: - **Database**: reference to a storage medium, with an `Accessor` for accessing data, diff --git a/co3/engine.py b/_deprecated/engine.py similarity index 97% rename from co3/engine.py rename to _deprecated/engine.py index ff27a49..2d25ef4 100644 --- a/co3/engine.py +++ b/_deprecated/engine.py @@ -13,7 +13,7 @@ class Engine: derivative Engines, like SQLEngine, mostly just wrap another engine-like object, this is not the rule. That is, inheriting Engine subtypes shouldn't necessarily expect to rely on another object per se, and if such an object is required, _this_ is the class - is meant to be skeleton to supports its creation (and not merely a wrapper for some + meant to be the skeleton that supports its creation (and not merely a wrapper for some other type, although it may appear that way when such a type is in fact readily available). diff --git a/co3/component.py b/co3/component.py index a75aba0..8ddcbb8 100644 --- a/co3/component.py +++ b/co3/component.py @@ -1,12 +1,11 @@ -''' -Component - -General wrapper for storage components to be used in various database contexts. Relations -can be thought of generally as named data containers/entities serving as a fundamental -abstractions within particular storage protocols. -''' - class Component[T]: + ''' + Component + + General wrapper for storage components to be used in various database contexts. Relations + can be thought of generally as named data containers/entities serving as a fundamental + abstractions within particular storage protocols. + ''' def __init__(self, name, obj: T): self.name = name self.obj = obj @@ -19,4 +18,3 @@ class Component[T]: def get_attributes(self): raise NotImplementedError - diff --git a/co3/domain.py b/co3/domain.py new file mode 100644 index 0000000..4caeb41 --- /dev/null +++ b/co3/domain.py @@ -0,0 +1,24 @@ +from contextlib import contextmanager + +from co3 import Resource + + +class Domain[R: Resource]: + ''' + General Domain class + + + ''' + def __init__(self, content): + pass + + def get_resource(self, url: URL) -> Resource: + pass + + @contextmanager + def connect(self, timeout=None): + raise NotImplementedError + +class SelectableDomain(Domain): + def select(self, component, *args, **kwargs): + raise NotImplementedError diff --git a/co3/resources/disk.py b/co3/domains/__init__.py similarity index 85% rename from co3/resources/disk.py rename to co3/domains/__init__.py index 6fc1f15..33dde73 100644 --- a/co3/resources/disk.py +++ b/co3/domains/__init__.py @@ -1,10 +1,12 @@ +#from co3.resources.disk import DiskResource + from pathlib import Path from co3.util import paths -from co3.resource import SelectableResource +from co3.resource import SelectableDomain -class DiskResource(SelectableResource): +class DiskDomain(SelectableDomain): def select( self, path_list: str | Path | list[str | Path], diff --git a/co3/domains/disk.py b/co3/domains/disk.py new file mode 100644 index 0000000..e69de29 diff --git a/co3/managers/sql.py b/co3/managers/sql.py index 8fd4519..86b85c7 100644 --- a/co3/managers/sql.py +++ b/co3/managers/sql.py @@ -75,7 +75,7 @@ class SQLManager(RelationalManager[SQLTable]): ''' def __init__(self, *args, **kwargs): ''' - The insert lock is a *reentrant lock*, meaning the same thread can acquire the + The insert lock is a *re-entrant lock*, meaning the same thread can acquire the lock again without deadlocking (simplifying across methods of this class that need it). ''' @@ -112,11 +112,13 @@ class SQLManager(RelationalManager[SQLTable]): def insert( self, connection, - component, + component: SQLTable, inserts: list[dict], commit=True ): ''' + Insert a group of + Parameters: ''' with self._insert_lock: @@ -135,6 +137,13 @@ class SQLManager(RelationalManager[SQLTable]): Perform provided table inserts, aligning the insert format of ``Collector.collect_inserts()``. + Note that the regular ``insert`` actually supports the usual notion of a "bulk + insert," inserting many entries under a single table. This method simply supports + the same but across multiple tables. It does so by just making calls to + ``insert()`` after grouping entries for each component in the provided ``inserts`` + dict, only committing the transaction after all components inserts have been + staged. + Parameters: inserts: component-indexed dictionary of insert lists ''' diff --git a/co3/medium.py b/co3/medium.py new file mode 100644 index 0000000..5932500 --- /dev/null +++ b/co3/medium.py @@ -0,0 +1,58 @@ +import logging +from contextlib import contextmanager + + +logger = logging.getLogger(__name__) + +class Medium[R: Resource]: + ''' + Medium base class. + + A Resource space + ''' + def __init__(self, scope): + pass + + @contextmanager + def connect(self, timeout=None): + ''' + Open a connection to the database specified by the resource. Exactly what the + returned connection looks like remains relatively unconstrained given the wide + variety of possible database interactions. This function should be invoked in + with-statement contexts, constituting an "interaction session" with the database + (i.e., allowing several actions to be performed using the same connection). + ''' + raise NotImplementedError + + def execute(self, query: Query[QL]): + pass + + +class BrowsableMedium[R: Resource](Medium[R]): + def browse(self, uri: URI[R]): + ''' + Analog for Read (CRUD), SELECT (SQL), GET (REST) + ''' + pass + + +class ABCDMedium[R: Resource](BrowsableMedium[R]): + def append(self, uri: URI[R], resource: R): + ''' + Analog for Create (CRUD), INSERT (SQL), POST/PUT (REST) + ''' + pass + + def change(self, uri: URI[R], resource: R): + ''' + Analog for Update (CRUD), UPDATE (SQL), PUT/PATCH (REST) + + Can a URI be another object? Component for ex; could inherit from URI I guess + ''' + pass + + def delete(self, uri: URI[R]): + ''' + Analog for Delete (CRUD), DELETE (SQL), DELETE (REST) + ''' + pass diff --git a/co3/resource.py b/co3/resource.py index 290aff0..abb6bb7 100644 --- a/co3/resource.py +++ b/co3/resource.py @@ -2,7 +2,8 @@ from typing import Protocol class Resource: - pass + def content(self) -> BinaryIO: + pass class SelectableResource(Protocol): def select(self, component, *args, **kwargs): diff --git a/co3/resources/__init__.py b/co3/resources/__init__.py index b7ebad3..e69de29 100644 --- a/co3/resources/__init__.py +++ b/co3/resources/__init__.py @@ -1 +0,0 @@ -from co3.resources.disk import DiskResource diff --git a/co3/uri.py b/co3/uri.py new file mode 100644 index 0000000..e908149 --- /dev/null +++ b/co3/uri.py @@ -0,0 +1,15 @@ +from urllib import parse + + +class URI: + def __init__(self, url_str: str): + self.url_str = url_str + +class URL(URI): + def __init__(self, url_str: str): + self.url_str = url_str + +class URN(URI): + def __init__(self, url_str: str): + self.url_str = url_str + diff --git a/pyproject.toml b/pyproject.toml index 780bb05..c011c35 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -40,6 +40,7 @@ docs = [ "furo", "myst-parser", ] +jupyter = ["ipykernel"] [project.urls] Homepage = "https://doc.olog.io/co3"