co3/_deprecated/engine.py

83 lines
4.0 KiB
Python
Raw Permalink Normal View History

import logging
from contextlib import contextmanager
logger = logging.getLogger(__name__)
class Engine:
'''
Engine base class. Used primarily as a Database connection manager, with general
methods that can be defined for several kinds of value stores.
Note that this is where the connection hierarchy is supposed to stop. While some
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
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).
.. admonition:: why is this object necessary?
More specifically, why not just have all the functionality here packed into the
Database by default? The answer is that, realistically, it could be. The type
separation between the Engine and Database is perhaps the least substantiated in
CO3. That being said, it still serves a purpose: to make composition of subtypes
easier. The Engine is a very lightweight abstraction, but some Engine subtypes
(e.g., FileEngines) may be used across several sibling Database types. In this
case, we'd have to repeat the Engine-related functionality for such sibling types.
Depending instead on a singular, outside object keeps things DRY. If Databases and
Engines were uniquely attached type-wise 1-to-1 (i.e., unique Engine type per
unique Database type), a separate object here would indeed be a waste, as is the
case for any compositional typing scheme.
.. admonition:: dev note
This class is now non-generic. It was originally conceived as a generic, depending
on a "resource spec type" to be help define expected types on initialization.
This simply proved too messy, required generic type propagation to the Database
definition, and muddied the otherwise simple args and kwargs forwarding for
internal manager creation.
'''
def __init__(self, *manager_args, **manager_kwargs):
self._manager = None
self._manager_args = manager_args
self._manager_kwargs = manager_kwargs
@property
def manager(self):
'''
Return Engine's singleton manager, initializing when the first call is made.
'''
if self._manager is None:
self._manager = self._create_manager()
return self._manager
def _create_manager(self):
'''
Create the session manager needed for connection contexts. This method is called
once by the ``.manager`` property function when it is first accessed. This method is
separated to isolate the creation logic in inheriting types.
Note that this method takes no explicit arguments. This is primarily because the
standard means of invocation (the manager property) is meant to remain generally
useful here in the base class, and can't be aware of any specific properties that
might be extracted in subtype initialization. As a result, we don't even try to
pass args, although it would just look like a forwarding of the readily manager
args and kwargs anyhow. As such, this method should make direct use of these
instance variables as needed.
'''
raise NotImplementedError
@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