Package: cs.resources
Resource management classes and functions.Latest release 20240201:
MultiOpenMixin: new .is_open() method to test for opens > 0.ClassClosedError(builtins.Exception, builtins.BaseException)Exception for operations invalid when something is closed.ClassMultiOpen(MultiOpenMixin, cs.context.ContextManagerMixin)Context manager class that manages a single open/close object
using a MultiOpenMixin.MethodMultiOpen.__init__(self, openable, finalise_later=False):
Initialise: save theopenableand call the MultiOpenMixin initialiser.ClassMultiOpenMixin(cs.context.ContextManagerMixin)A multithread safe mixin to count open and close calls,
doing a startup on the first.openand shutdown on the last.close.If used as a context manager this mixin callsopen()/close()from__enter__()and__exit__().It is recommended subclass implementations do as little as possible
during__init__, and do almost all setup during startup so
that the class may perform multiple startup/shutdown iterations.Classes using this mixin should define a context manager
method.startup_shutdownwhich does the startup actions
before yielding and then does the shutdown actions.Example:class DatabaseThing(MultiOpenMixin):
    @contextmanager
    def startup_shutdown(self):
        self._db = open_the_database()
        yield
        self._db.close()
...
with DatabaseThing(...) as db_thing:
    ... use db_thing ...If course, often something like a database open will itself
be a context manager and thestartup_shutdownmethod more
usually looks like this:@contextmanager
    def startup_shutdown(self):
        with open_the_database() as db:
            self._db = db
            yieldWhy not just write a plain context manager class? Because in
multithreaded or async code one wants to keep the instance
"open" while any thread is still using it.
This mixin lets threads use an instance in overlapping fashion:db_thing = DatabaseThing(...)
with db_thing:
    ... kick off threads with access to the db ...
...
thread 1:
with db_thing:
   ... use db_thing ...
thread 2:
with db_thing:
   ... use db_thing ...TODO:subopens: if true (default false) then.openwill return
a proxy object with its own.closedattribute set by the
proxy's.close.Functionnot_closed(func)Decorator to wrap methods of objects with a .closed property
which should raise when self.closed.Functionopenif(obj)Context manager to openobjif it has a.openmethod
and also to close it via its.closemethod.
This yieldsobj.open()if defined, orobjotherwise.ClassPoolA generic pool of objects on the premise that reuse is cheaper than recreation.All the pool objects must be suitable for use, so thenew_objectcallable will typically be a closure.
For example, here is theinitfor a per-thread AWS Bucket using a
distinct Session:def __init__(self, bucket_name):
    Pool.__init__(self, lambda: boto3.session.Session().resource('s3').Bucket(bucket_name)MethodPool.__init__(self, new_object, max_size=None, lock=None):
Initialise the Pool with creatornew_objectand maximum sizemax_size.Parameters:new_objectis a callable which returns a new object for the Pool.max_size: The maximum size of the pool of available objects saved for reuse.
If omitted orNone, defaults to 4.
If 0, no upper limit is applied.lock: optional shared Lock; if omitted orNonea new Lock is allocatedClassRunState(cs.threads.HasThreadState, cs.context.ContextManagerMixin)A class to track a running task whose cancellation may be requested.Its purpose is twofold, to provide easily queriable state
around tasks which can start and stop, and to provide control
methods to pronounce that a task has started (.start),
should stop (.cancel)
and has stopped (.stop).ARunStatecan be used as a context manager, with the enter
and exit methods calling.startand.stoprespectively.
Note that if the suite raises an exception
then the exit method also calls.cancelbefore the call to.stop.Monitor or daemon processes can poll theRunStateto see when
they should terminate, and may also manage the overall state
easily using a context manager.
Example:def monitor(self):
    with self.runstate:
        while not self.runstate.cancelled:
            ... main loop body here ...ARunStatehas three main methods:.start(): set.runningand clear.cancelled.cancel(): set.cancelled.stop(): clear.runningARunStatehas the following properties:cancelled: true if.cancelhas been called.running: true if the task is running.
Further, assigning a true value to it sets.start_timeto now.
Assigning a false value to it sets.stop_timeto now.start_time: the time.runningwas last set to true.stop_time: the time.runningwas last set to false.run_time:max(0,.stop_time-.start_time)stopped: true if the task is not running.stopping: true if the task is running but has been cancelled.notify_start: a set of callables called with theRunStateinstance
to be called whenever.runningbecomes true.notify_end: a set of callables called with theRunStateinstance
to be called whenever.runningbecomes false.notify_cancel: a set of callables called with theRunStateinstance
to be called whenever.cancelis called.ClassRunStateMixinMixin to provide convenient access to aRunState.Provides:.runstate,.cancelled,.running,.stopping,.stopped.MethodRunStateMixin.__init__(self, runstate: Union[cs.resources.RunState, str, NoneType]):
Initialise theRunStateMixin; sets the.runstateattribute.Parameters:runstate: optionalRunStateinstance or name.
If astr, a newRunStatewith that name is allocated.
If omitted, the defaultRunStateis used.Release LogRelease 20240201:
MultiOpenMixin: new .is_open() method to test for opens > 0.Release 20231221:
RunState: new raiseif() method to raise CancellationError if the RunState is cancelled.Release 20231129:RunStateMixin: runstate parameter may be None, str, RunState.MultiOpenMixin.enter_exit: do not pass caller frame to self.close(), uninformative.Release 20230503:
RunState: new optional poll_cancel Callable parameter, make .cancelled a property.Release 20230331:@uses_runstate: use the prevailing RunState or create one.MultiOpenMixin: move all the open/close counting logic to the _mom_state class, make several attributes public, drop separate finalise() method and associated Condition.bugfix: _mom_state.open: only set self._teardown when opens==1.Release 20230217:
MultiOpenMixin:reprfor the state object.Release 20230212:
RunState: if already running, do not adjust state or catch signals; if not in the main thread do not adjust signals.Release 20230125:
RunState: subclass HasThreadState, adjust @uses_runstate.Release 20221228:Get error,warning from cs.gimmicks.RunState: get store verbose as self.verbose, drop from catch_signals.Release 20221118:New RunState.current thread local stackable class attribute.New @uses_runstate decorator for functions using a RunState, defaulting to RunState.current.runstate.Release 20220918:MultiOpenMixin.close: report caller of underflow close.RunState: new optional handle_signal parameter to override the default method.New openif() context manager to open/close an object if it has a .open method.MultiOpenMixin.startup_shutdown: be silent for missing (obsolete) .startup, require .shutdown if .startup.Release 20220429:
RunState: new catch_signal(sig,verbose=False) context manager method to cancel the RunState on receipt of a signal.Release 20211208:MultiOpenMixin.startup_shutdown: since this is the fallback for obsolete uses of MultiOpenMixin, warn if there is no .startup/.shutdown method.MultiOpenMixin.startup_shutdown: fix up shutdown logic, was not using a finally clause.MultiOpenMixin: use ContextManagerMixinenter_exitgenerator method instead ofenterandexit.Release 20210906:
MultiOpenMixin: make startup and shutdown optional.Release 20210731:
RunState: tune the sanity checks around whether the state is "running".Release 20210420:
MultiOpenMixin: run startup/shutdown entirely via the new default method @contextmanager(startup_shutdown), paving the way for subclasses to just define their own startup_shutdown context manager methods instead of distinct startup/shutdown methods.Release 20201025:
MultiOpenMixin.__mo_getstate: dereference self.dictbecause using AttributeError was pulling a state object from another instance, utterly weird.Release 20200718:
MultiOpenMixin: as a hack to avoid having aninit, move state into an on demand object accesses by a private method.Release 20200521:
Sweeping removal of cs.obj.O, universally supplanted by types.SimpleNamespace.Release 20190812:MultiOpenMixin: no longer subclass cs.obj.O.MultiOpenMixin: removelockparam support, the mixin has its own lock.MultiOpen: droplockparam support, no longer used by MultiOpenMixin.MultiOpenMixin: do finalise inside the lock for the same reason as shutdown (competition with open/startup).MultiOpenMixin.close: newunopened_ok=Falseparameter intended for callback closes which might fire even if the initial open does not occur.Release 20190617:
RunState.exit: if an exception was raised call .canel() before calling .stop().Release 20190103:Bugfixes for context managers.MultiOpenMixin fixes and changes.RunState improvements.Release 20171024:bugfix MultiOpenMixin finalise logic and other small logic fixes and checsnew class RunState for tracking or controlling a running taskRelease 20160828:
Use "install_requires" instead of "requires" in DISTINFO.Release 20160827:BREAKING CHANGE: rename NestingOpenCloseMixin to MultiOpenMixin.New Pool class for generic object reuse.Assorted minor improvements.Release 20150115:
First PyPI release.