intermediate Medium/Resource refactoring
This commit is contained in:
		
							parent
							
								
									b726f495b6
								
							
						
					
					
						commit
						090c122c60
					
				
							
								
								
									
										179
									
								
								co3/co3.py
									
									
									
									
									
								
							
							
						
						
									
										179
									
								
								co3/co3.py
									
									
									
									
									
								
							| @ -1,8 +1,9 @@ | |||||||
| ''' | ''' | ||||||
| CO3 is an abstract base class for scaffolding object hierarchies and managing operations | CO3 is an abstract base class for scaffolding object hierarchies and managing | ||||||
| with associated database schemas. It facilitates something like a "lightweight ORM" for | operations with associated database schemas. It facilitates something like a | ||||||
| classes/tables/states with fixed transformations of interest. The canonical use case is | "lightweight ORM" for classes/tables/states with fixed transformations of | ||||||
| managing hierarchical document relations, format conversions, and syntactical components. | interest. The canonical use case is managing hierarchical document relations, | ||||||
|  | format conversions, and syntactical components. | ||||||
| 
 | 
 | ||||||
| Generic collation syntax: | Generic collation syntax: | ||||||
| 
 | 
 | ||||||
| @ -22,12 +23,13 @@ Generic collation syntax: | |||||||
| 
 | 
 | ||||||
| .. admonition:: On multi-key attachment | .. admonition:: On multi-key attachment | ||||||
| 
 | 
 | ||||||
|     One possible quirk of the current collation registry scheme is the rather black and |     One possible quirk of the current collation registry scheme is the rather | ||||||
|     white nature of key attachment. You either specify a single key, possibly to several |     black and white nature of key attachment. You either specify a single key, | ||||||
|     groups, or allow any key via passthrough under an implicit group. There's no explicit |     possibly to several groups, or allow any key via passthrough under an | ||||||
|     "multi-key" pattern to make use of here, be it through "restricted passthrough" |     implicit group. There's no explicit "multi-key" pattern to make use of | ||||||
|     (method still parameterized by the key, but only allows keys from a provided list) or |     here, be it through "restricted passthrough" (method still parameterized by | ||||||
|     just simple duplicated attachment. To demonstrate via the above example: |     the key, but only allows keys from a provided list) or just simple | ||||||
|  |     duplicated attachment. To demonstrate via the above example: | ||||||
| 
 | 
 | ||||||
|     .. code-block:: python |     .. code-block:: python | ||||||
| 
 | 
 | ||||||
| @ -54,8 +56,8 @@ Generic collation syntax: | |||||||
| 
 | 
 | ||||||
|                 ... |                 ... | ||||||
| 
 | 
 | ||||||
|     or with a central handler and separate collation points (at least when the key list is |     or with a central handler and separate collation points (at least when the | ||||||
|     small): |     key list is small): | ||||||
| 
 | 
 | ||||||
|     .. code-block:: python |     .. code-block:: python | ||||||
| 
 | 
 | ||||||
| @ -71,38 +73,41 @@ Generic collation syntax: | |||||||
|             def key2(self): |             def key2(self): | ||||||
|                 self._handle_supported_keys('key2') |                 self._handle_supported_keys('key2') | ||||||
| 
 | 
 | ||||||
|     The former scales better and allows general key rejection patterns if needed, while |     The former scales better and allows general key rejection patterns if | ||||||
|     the latter integrates a bit better with the formal collation process, e.g., will |     needed, while the latter integrates a bit better with the formal collation | ||||||
|     throw ``ValueErrors`` based on key mismatches automatically. |     process, e.g., will throw ``ValueErrors`` based on key mismatches | ||||||
|  |     automatically. | ||||||
| ''' | ''' | ||||||
| import inspect | import inspect | ||||||
| import logging | import logging | ||||||
| from collections import defaultdict | from collections import defaultdict | ||||||
| from functools import wraps, partial |  | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| def collate(key, groups=None): | def collate(key, groups=None): | ||||||
|     ''' |     ''' | ||||||
|     Collation decorator for CO3 subtype action registry. |     Collation decorator for CO3 subtype action registry. | ||||||
| 
 | 
 | ||||||
|     Dynamic decorator; can be used as ``collate`` without any arguments, or with all. In |     Dynamic decorator; can be used as ``collate`` without any arguments, or | ||||||
|     the former case, ``key`` will be a function, so we check for this. |     with all. In the former case, ``key`` will be a function, so we check for | ||||||
|  |     this. | ||||||
| 
 | 
 | ||||||
|     .. admonition:: Usage |     .. admonition:: Usage | ||||||
| 
 | 
 | ||||||
|         Collation registration is the process of exposing various actions for use in |         Collation registration is the process of exposing various actions for | ||||||
|         **hierarchical collection** (see ``Mapper.collect``). Collation *keys* are unique |         use in **hierarchical collection** (see ``Mapper.collect``). Collation | ||||||
|         identifiers of a particular action that emits data. Keys can belong to an arbitrary |         *keys* are unique identifiers of a particular action that emits data. | ||||||
|         number of *groups*, which serve as semantically meaningful collections of similar |         Keys can belong to an arbitrary number of *groups*, which serve as | ||||||
|         actions. Group assignment also determines the associated *collation component* |         semantically meaningful collections of similar actions. Group | ||||||
|         to be used as a storage target; the results of actions $K_G$ belonging to group |         assignment also determines the associated *collation component* to be | ||||||
|         $G$ will all be stored in the attached $G$-component. Specification of key-group |         used as a storage target; the results of actions $K_G$ belonging to | ||||||
|         relations can be done in a few ways: |         group $G$ will all be stored in the attached $G$-component. | ||||||
|  |         Specification of key-group relations can be done in a few ways: | ||||||
| 
 | 
 | ||||||
|         - Explicit key-group specification: a specific key and associated groups can be |         - Explicit key-group specification: a specific key and associated | ||||||
|           provided as arguments to the decorator: |           groups can be provided as arguments to the decorator: | ||||||
| 
 | 
 | ||||||
|           .. code-block:: python |           .. code-block:: python | ||||||
| 
 | 
 | ||||||
| @ -127,14 +132,14 @@ def collate(key, groups=None): | |||||||
|                   ... |                   ... | ||||||
|               } |               } | ||||||
| 
 | 
 | ||||||
|           If ``groups`` is left unspecified, the key will be attached to the default |           If ``groups`` is left unspecified, the key will be attached to the | ||||||
|           ``None`` group. |           default ``None`` group. | ||||||
| 
 | 
 | ||||||
|         - Implicit key-group association: in some cases, you may want to support an entire |         - Implicit key-group association: in some cases, you may want to | ||||||
|           "action class," and associate any operations under the class to the same storage |           support an entire "action class," and associate any operations under | ||||||
|           component. Here we still use the notion of connecting groups to components, but |           the class to the same storage component. Here we still use the notion | ||||||
|           allow the key to be dynamically specified and passed through to the collation |           of connecting groups to components, but allow the key to be | ||||||
|           method: |           dynamically specified and passed through to the collation method: | ||||||
| 
 | 
 | ||||||
|           .. code-block:: python |           .. code-block:: python | ||||||
| 
 | 
 | ||||||
| @ -160,27 +165,31 @@ def collate(key, groups=None): | |||||||
| 
 | 
 | ||||||
|           A few important notes: |           A few important notes: | ||||||
| 
 | 
 | ||||||
|           - Implicit key-group specifications attach the *group* to a single method, |           - Implicit key-group specifications attach the *group* to a single | ||||||
|             whereas in the explicit case, groups can be affiliated with many keys. When |             method, whereas in the explicit case, groups can be affiliated with | ||||||
|             explicitly provided, only those exact key values are supported. But in the |             many keys. When explicitly provided, only those exact key values | ||||||
|             implicit case, *any* key is allowed; the group still remains a proxy for the |             are supported. But in the implicit case, *any* key is allowed; the | ||||||
|             entire action class, but without needing to map from specifically stored key |             group still remains a proxy for the entire action class, but | ||||||
|             values. That is, the utility of the group remains consistent across implicit |             without needing to map from specifically stored key values. That | ||||||
|  |             is, the utility of the group remains consistent across implicit | ||||||
|             and explicit cases, but stores the associations differently. |             and explicit cases, but stores the associations differently. | ||||||
|           - The ``None`` key, rather than point to a ``(<method>, <group-list>)`` tuple, |           - The ``None`` key, rather than point to a ``(<method>, | ||||||
|             instead points to a dictionary of ``group``-``method`` pairs. When attempting |             <group-list>)`` tuple, instead points to a dictionary of | ||||||
|             execute a key under a particular group, the group registry indicates |             ``group``-``method`` pairs. When attempting execute a key under a | ||||||
|             whether the key is explicitly supported. If ``None`` is present for the group, |             particular group, the group registry indicates whether the key is | ||||||
|             then ``key_registry[None][<group-name>]`` can be used to recover the method |             explicitly supported. If ``None`` is present for the group, then | ||||||
|             implicitly affiliated with the key (along with any other key under the group). |             ``key_registry[None][<group-name>]`` can be used to recover the | ||||||
|           - When any method has been implicitly registered, *any* key (even when |             method implicitly affiliated with the key (along with any other key | ||||||
|             attempting to specify an explicit key) will match that group. This can |             under the group). | ||||||
|             effectively mean keys are not unique when an implicit group has been |           - When any method has been implicitly registered, *any* key (even | ||||||
|             registered. There is a protection in place here, however; in methods like |             when attempting to specify an explicit key) will match that group. | ||||||
|             ``CO3.collate`` and ``Mapper.collect``, an implicit group must be directly |             This can effectively mean keys are not unique when an implicit | ||||||
|             named in order for a given key to be considered. That is, when attempting |             group has been registered. There is a protection in place here, | ||||||
|             collation outside specific group context, provided keys will only be |             however; in methods like ``CO3.collate`` and ``Mapper.collect``, an | ||||||
|             considered against explicitly registered keys. |             implicit group must be directly named in order for a given key to | ||||||
|  |             be considered. That is, when attempting collation outside specific | ||||||
|  |             group context, provided keys will only be considered against | ||||||
|  |             explicitly registered keys. | ||||||
|     ''' |     ''' | ||||||
|     func = None |     func = None | ||||||
|     if inspect.isfunction(key): |     if inspect.isfunction(key): | ||||||
| @ -200,6 +209,7 @@ def collate(key, groups=None): | |||||||
| 
 | 
 | ||||||
|     return decorator |     return decorator | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class FormatRegistryMeta(type): | class FormatRegistryMeta(type): | ||||||
|     ''' |     ''' | ||||||
|     Metaclass handling collation registry at the class level. |     Metaclass handling collation registry at the class level. | ||||||
| @ -225,8 +235,8 @@ class FormatRegistryMeta(type): | |||||||
|                 for _, method in methods: |                 for _, method in methods: | ||||||
|                     register_action(method) |                     register_action(method) | ||||||
| 
 | 
 | ||||||
|         # add final registered formats for the current class, overwriting any found in |         # add final registered formats for the current class, overwriting any | ||||||
|         # superclass chain |         # found in superclass chain | ||||||
|         for attr_name, attr_value in attrs.items(): |         for attr_name, attr_value in attrs.items(): | ||||||
|             register_action(attr_value) |             register_action(attr_value) | ||||||
| 
 | 
 | ||||||
| @ -235,41 +245,49 @@ class FormatRegistryMeta(type): | |||||||
| 
 | 
 | ||||||
|         return super().__new__(cls, name, bases, attrs) |         return super().__new__(cls, name, bases, attrs) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class CO3(metaclass=FormatRegistryMeta): | class CO3(metaclass=FormatRegistryMeta): | ||||||
|     ''' |     ''' | ||||||
|     Base class supporting the central "COllate, COllect, COmpose" paradigm. |     Base class supporting the central "COllate, COllect, COmpose" paradigm. | ||||||
| 
 | 
 | ||||||
|     - Collate: organize and transform conversion outputs, possibly across class components |     - Collate: organize and transform conversion outputs, possibly across class | ||||||
|     - Collect: gather core attributes, conversion data, and subcomponents for DB insertion |       components | ||||||
|     - Compose: construct object-associated DB table references through the class hierarchy |     - Collect: gather core attributes, conversion data, and subcomponents for | ||||||
|  |       DB insertion | ||||||
|  |     - Compose: construct object-associated DB table references through the | ||||||
|  |       class hierarchy | ||||||
| 
 | 
 | ||||||
|     .. admonition:: on action groups |     .. admonition:: on action groups | ||||||
| 
 | 
 | ||||||
|         Group keys are simply named collections to make it easy for storage components to |         Group keys are simply named collections to make it easy for storage | ||||||
|         be attached to action subsets. They do _not_ augment the action registration |         components to be attached to action subsets. They do _not_ augment the | ||||||
|         namespace, meaning the action key should still be unique; the group key is purely |         action registration namespace, meaning the action key should still be | ||||||
|         auxiliary. |         unique; the group key is purely auxiliary. | ||||||
| 
 | 
 | ||||||
|         Action methods can also be attached to several groups, in case there is |         Action methods can also be attached to several groups, in case there is | ||||||
|         overlapping utility within or across schemas or storage media. In this case, it |         overlapping utility within or across schemas or storage media. In this | ||||||
|         becomes particularly critical to ensure registered ``collate`` methods really are |         case, it becomes particularly critical to ensure registered ``collate`` | ||||||
|         just "gathering results" from possibly heavy-duty operations, rather than |         methods really are just "gathering results" from possibly heavy-duty | ||||||
|         performing them when called, so as to reduce wasted computation. |         operations, rather than performing them when called, so as to reduce | ||||||
|  |         wasted computation. | ||||||
| 
 | 
 | ||||||
|     .. admonition:: New: collation caching |     .. admonition:: New: collation caching | ||||||
| 
 | 
 | ||||||
|         To help facilitate the common pattern of storing collation results, a |         To help facilitate the common pattern of storing collation results, a | ||||||
|         ``collate_cache`` parameter has been added to store key-group indexed collation |         ``collate_cache`` parameter has been added to store key-group indexed | ||||||
|         results. (Note: now requires explicit superclass instantiation.) |         collation results. (Note: now requires explicit superclass | ||||||
|  |         instantiation.) | ||||||
|     ''' |     ''' | ||||||
|  | 
 | ||||||
|     def __init__(self): |     def __init__(self): | ||||||
|         self._collate_cache = {} |         self._collate_cache = {} | ||||||
| 
 | 
 | ||||||
|     @property |     @property | ||||||
|     def attributes(self): |     def attributes(self): | ||||||
|         ''' |         ''' | ||||||
|         Method to define how a subtype's inserts should be handled under ``collect`` for |         Method to define how a subtype's inserts should be handled under | ||||||
|         canonical attributes, i.e., inserts to the type's table. |         ``collect`` for canonical attributes, i.e., inserts to the type's | ||||||
|  |         table. | ||||||
|         ''' |         ''' | ||||||
|         return vars(self) |         return vars(self) | ||||||
| 
 | 
 | ||||||
| @ -284,14 +302,15 @@ class CO3(metaclass=FormatRegistryMeta): | |||||||
|     def collation_attributes(self, key, group): |     def collation_attributes(self, key, group): | ||||||
|         ''' |         ''' | ||||||
|         Return "connective" collation component data, possibly dependent on |         Return "connective" collation component data, possibly dependent on | ||||||
|         instance-specific attributes and the action arguments. This is typically the |         instance-specific attributes and the action arguments. This is | ||||||
|         auxiliary structure that may be needed to attach to responses from registered |         typically the auxiliary structure that may be needed to attach to | ||||||
|         ``collate`` calls to complete inserts. |         responses from registered ``collate`` calls to complete inserts. | ||||||
| 
 | 
 | ||||||
|         Note: this method is primarily used by ``Mapper.collect()``, and is called just |         Note: this method is primarily used by ``Mapper.collect()``, and is | ||||||
|         prior to collector send-off for collation inserts and injected alongside collation |         called just prior to collector send-off for collation inserts and | ||||||
|         data. Common structure in collation components can make this function easy to |         injected alongside collation data. Common structure in collation | ||||||
|         define, independent of action group for instance. |         components can make this function easy to define, independent of action | ||||||
|  |         group for instance. | ||||||
|         ''' |         ''' | ||||||
|         return {} |         return {} | ||||||
| 
 | 
 | ||||||
| @ -350,5 +369,3 @@ class CO3(metaclass=FormatRegistryMeta): | |||||||
|             self._collate_cache[(key, group)] = result |             self._collate_cache[(key, group)] = result | ||||||
| 
 | 
 | ||||||
|         return result |         return result | ||||||
| 
 |  | ||||||
| 
 |  | ||||||
|  | |||||||
| @ -11,12 +11,10 @@ logger = logging.getLogger(__name__) | |||||||
| 
 | 
 | ||||||
| class Indexer: | class Indexer: | ||||||
|     ''' |     ''' | ||||||
|     Indexer class |     Indexer base class | ||||||
| 
 | 
 | ||||||
|     Provides restricted access to an underlying Accessor to enable more efficient, superficial |     Provides restricted access to an underlying Accessor to enable more efficient, superficial | ||||||
|     caching. |     caching. Note that cache clearing is to be handled by a wrapper class, like the Database. | ||||||
| 
 |  | ||||||
|     Cache clearing is to be handled by a wrapper class, like the Database. |  | ||||||
| 
 | 
 | ||||||
|     Caching occurs at the class level, with indexes prefixed by table's origin Composer. |     Caching occurs at the class level, with indexes prefixed by table's origin Composer. | ||||||
|     This means that cached selects/group-bys will be available regardless of the provided |     This means that cached selects/group-bys will be available regardless of the provided | ||||||
| @ -261,12 +259,12 @@ class Indexer: | |||||||
|             agg_on = agg_on_names |             agg_on = agg_on_names | ||||||
|             index_on = index_on_names |             index_on = index_on_names | ||||||
| 
 | 
 | ||||||
|         #print(f'rows_are_mappings: {rows_are_mappings}') |         # print(f'rows_are_mappings: {rows_are_mappings}') | ||||||
|         #print(f'group_by: {group_by}') |         # print(f'group_by: {group_by}') | ||||||
|         #print(f'agg_on: {agg_on}') |         # print(f'agg_on: {agg_on}') | ||||||
|         #print(f'agg_on_names: {agg_on_names}') |         # print(f'agg_on_names: {agg_on_names}') | ||||||
|         #print(f'index_on: {index_on}') |         # print(f'index_on: {index_on}') | ||||||
|         #print(f'index_on_names: {index_on_names}') |         # print(f'index_on_names: {index_on_names}') | ||||||
| 
 | 
 | ||||||
|         # "group by" block ID and wrangle the links into a list |         # "group by" block ID and wrangle the links into a list | ||||||
|         group_by_idx = {} |         group_by_idx = {} | ||||||
| @ -317,6 +315,7 @@ class Indexer: | |||||||
| 
 | 
 | ||||||
|         return list(group_by_idx.values()) |         return list(group_by_idx.values()) | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| class CacheBlock: | class CacheBlock: | ||||||
|     ''' |     ''' | ||||||
|     Wraps up a set of query parameters for a specific entity, and provides cached access |     Wraps up a set of query parameters for a specific entity, and provides cached access | ||||||
|  | |||||||
| @ -1,6 +1,8 @@ | |||||||
| import logging | import logging | ||||||
| from contextlib import contextmanager | from contextlib import contextmanager | ||||||
| 
 | 
 | ||||||
|  | from co3.engine import Engine, Connection, Resource, Group | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| logger = logging.getLogger(__name__) | logger = logging.getLogger(__name__) | ||||||
| 
 | 
 | ||||||
| @ -10,11 +12,14 @@ class Medium[R: Resource]: | |||||||
| 
 | 
 | ||||||
|     A Resource space |     A Resource space | ||||||
|     ''' |     ''' | ||||||
|     def __init__(self, scope): |     _engine_cls: type[Engine] = Engine | ||||||
|  |   | ||||||
|  |     def __init__(self, *scope_args, **scope_kwargs): | ||||||
|  |         self.engine = self._engine_cls(*engine_args, **engine_kwargs) | ||||||
|         pass |         pass | ||||||
|          |          | ||||||
|     @contextmanager |     @contextmanager | ||||||
|     def connect(self, timeout=None): |     def connect(self, timeout=None) -> Connection: | ||||||
|         ''' |         ''' | ||||||
|         Open a connection to the database specified by the resource. Exactly what the |         Open a connection to the database specified by the resource. Exactly what the | ||||||
|         returned connection looks like remains relatively unconstrained given the wide |         returned connection looks like remains relatively unconstrained given the wide | ||||||
| @ -22,21 +27,59 @@ class Medium[R: Resource]: | |||||||
|         with-statement contexts, constituting an "interaction session" with the database |         with-statement contexts, constituting an "interaction session" with the database | ||||||
|         (i.e., allowing several actions to be performed using the same connection). |         (i.e., allowing several actions to be performed using the same connection). | ||||||
|         ''' |         ''' | ||||||
|         raise NotImplementedError |         return self.engine.connect(timeout=timeout) | ||||||
| 
 | 
 | ||||||
|     def execute(self, query: Query[QL]): |     def execute(self, query: Query[QL]): | ||||||
|         pass |         pass | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class BrowsableMedium[R: Resource](Medium[R]): | class ReadableMedium[R: Resource](Medium[R]): | ||||||
|     def browse(self, uri: URI[R]): |     def _resolve_relative_uri(self, protocol, value): | ||||||
|  |         ''' | ||||||
|  |         Subclass to implement: fetch child object for supported protocol, i.e., single | ||||||
|  |         component subpath | ||||||
|  |         ''' | ||||||
|  |         ... | ||||||
|  | 
 | ||||||
|  |     def resolve_uri(self, uri: URI) -> ResourceCollection: | ||||||
|  |         assert uri.protocols[0] in self.supported_protocols | ||||||
|  | 
 | ||||||
|  |         obj = self._resolve_relative_uri(uri.protocols[0], uri.components[0]) | ||||||
|  | 
 | ||||||
|  |         # core the uri and recurse | ||||||
|  |         cored_uri = uri.core(): | ||||||
|  | 
 | ||||||
|  |         if cored_uri: | ||||||
|  |             rc = obj.resolve_uri(cored_uri) | ||||||
|  |         else: | ||||||
|  |             assert type(obj) is Resource | ||||||
|  |             rc = ResourceCollection([obj]) | ||||||
|  | 
 | ||||||
|  |         return rc | ||||||
|  | 
 | ||||||
|  |     def _to_uri_list(self, uri_like): | ||||||
|  |         if type(uri) is not list: | ||||||
|  |             uri = [uri] | ||||||
|  |         ... | ||||||
|  | 
 | ||||||
|  |     def browse( | ||||||
|  |         self, | ||||||
|  |         connection: Connection, | ||||||
|  |         uri: str | URI | list[str] | list[URI] | ||||||
|  |     ) -> ResourceCollection: | ||||||
|         ''' |         ''' | ||||||
|         Analog for Read (CRUD), SELECT (SQL), GET (REST) |         Analog for Read (CRUD), SELECT (SQL), GET (REST) | ||||||
|         ''' |         ''' | ||||||
|         pass |         uris = self._to_uri_list(uri) | ||||||
|  | 
 | ||||||
|  |         rc = ResourceCollection() | ||||||
|  |         for uri in uris: | ||||||
|  |             rc.extend(self._resolve_uri(uri)) | ||||||
|  | 
 | ||||||
|  |         return rc | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class ABCDMedium[R: Resource](BrowsableMedium[R]): | class WritableMedium[R: Resource](ReadableMedium[R]): | ||||||
|     def append(self, uri: URI[R], resource: R): |     def append(self, uri: URI[R], resource: R): | ||||||
|         ''' |         ''' | ||||||
|         Analog for Create (CRUD), INSERT (SQL), POST/PUT (REST) |         Analog for Create (CRUD), INSERT (SQL), POST/PUT (REST) | ||||||
|  | |||||||
							
								
								
									
										0
									
								
								co3/mediums/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								co3/mediums/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										6
									
								
								co3/mediums/disk.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								co3/mediums/disk.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,6 @@ | |||||||
|  | from co3 import Medium | ||||||
|  | from co3.resources import INode | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class Disk[INode](Medium): | ||||||
|  |     pass | ||||||
| @ -1,10 +1,15 @@ | |||||||
| from typing import Protocol | from typing import BinaryIO | ||||||
|  | 
 | ||||||
|  | from co3 import URI, Medium | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class Resource: | class Resource: | ||||||
|     def content(self) -> BinaryIO: |     def __init__( | ||||||
|         pass |         self, | ||||||
|  |         context: Medium | list[Medium], | ||||||
|  |         uri: URI | ||||||
|  |     ): | ||||||
|  |         self.uri = uri | ||||||
| 
 | 
 | ||||||
| class SelectableResource(Protocol): |     def open(self) -> BinaryIO: | ||||||
|     def select(self, component, *args, **kwargs): |         pass | ||||||
|         raise NotImplementedError |  | ||||||
|  | |||||||
							
								
								
									
										5
									
								
								co3/resources/inode.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								co3/resources/inode.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | from co3 import Resource | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | class INode(Resource): | ||||||
|  |     pass | ||||||
							
								
								
									
										21
									
								
								co3/uri.py
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								co3/uri.py
									
									
									
									
									
								
							| @ -1,4 +1,4 @@ | |||||||
| from urllib import parse | from urllib.parse import urlparse | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| class URI: | class URI: | ||||||
| @ -13,3 +13,22 @@ class URN(URI): | |||||||
|     def __init__(self, url_str: str): |     def __init__(self, url_str: str): | ||||||
|         self.url_str = url_str |         self.url_str = url_str | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | class CompositeURI(URI): | ||||||
|  |     def __init__(self, url_str: str): | ||||||
|  |         url_obj = urlparse(url_str) | ||||||
|  | 
 | ||||||
|  |         self.protocols = url_obj.scheme.split('+')[::-1] | ||||||
|  |         self.components = url_obj.scheme.split('+')[::-1] | ||||||
|  | 
 | ||||||
|  |     def core(self, layers=1) -> 'CompositeURI': | ||||||
|  |         ''' | ||||||
|  |         "Core" the innermost ``layers`` layers of the composite URI. | ||||||
|  |         ''' | ||||||
|  |         pass | ||||||
|  | 
 | ||||||
|  |     def shed(self, layers=1) -> 'CompositeURI': | ||||||
|  |         ''' | ||||||
|  |         "Shed" the outermost ``layers`` layers of the composite URI. | ||||||
|  |         ''' | ||||||
|  |         pass | ||||||
|  | |||||||
							
								
								
									
										11
									
								
								docs/reference/uri.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								docs/reference/uri.md
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,11 @@ | |||||||
|  | Quick thoughts and ideals: | ||||||
|  | 
 | ||||||
|  | - Schemes are compositional, "wrapping" super contexts: `c+b+a://a/b/c` | ||||||
|  | - The scheme communicates the target type (above is `c`) | ||||||
|  | - URIs can be arbitrarily relative so long as they're resolved in the right contexts. | ||||||
|  |   Above, `c+b://b/c` can be resolved in the context of `a://a` | ||||||
|  | - URIs are resolved by unwrapping schemes and resolving in to out | ||||||
|  | - URL params can apply only to the target type (this is the most consistent and probably | ||||||
|  |   not too restrictive) | ||||||
|  | - Trajectories from one scheme to another can be inferred from the type hierarchy; there | ||||||
|  |   may be many | ||||||
							
								
								
									
										5
									
								
								tests/co3_medium_demo.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/co3_medium_demo.py
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,5 @@ | |||||||
|  | from co3.mediums import Disk | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | disk = Disk('disk:///') | ||||||
|  | disk.browse('dir://home') | ||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user