Thread related convenience classes and functions.
*Latest release 20241005*:
Remove some debug noise.
## <a name="AdjustableSemaphore"></a>Class `AdjustableSemaphore`
A semaphore whose value may be tuned after instantiation.
*`AdjustableSemaphore.acquire(self, blocking=True)`*:
The acquire() method calls the base acquire() method if not blocking.
If blocking is true, the base acquire() is called inside a lock to
avoid competing with a reducing adjust().
*`AdjustableSemaphore.adjust(self, newvalue)`*:
Set capacity to `newvalue`
by calling release() or acquire() an appropriate number of times.
If `newvalue` lowers the semaphore capacity then adjust()
may block until the overcapacity is released.
*`AdjustableSemaphore.adjust_delta(self, delta)`*:
Adjust capacity by `delta` by calling release() or acquire()
an appropriate number of times.
If `delta` lowers the semaphore capacity then adjust() may block
until the overcapacity is released.
*`AdjustableSemaphore.release(self)`*:
Release the semaphore.
## <a name="bg"></a>`bg(func, *, daemon=None, name=None, no_start=False, no_logexc=False, args=None, kwargs=None, thread_factory=None, pre_enter_objects=None, **tfkw)`
Dispatch the callable `func` in its own `Thread`;
return the `Thread`.
Parameters:
* `func`: a callable for the `Thread` target.
* `args`, `kwargs`: passed to the `Thread` constructor
* `kwargs`, `kwargs`: passed to the `Thread` constructor
* `daemon`: optional argument specifying the `.daemon` attribute.
* `name`: optional argument specifying the `Thread` name,
default: the name of `func`.
* `no_logexc`: if false (default `False`), wrap `func` in `@logexc`.
* `no_start`: optional argument, default `False`.
If true, do not start the `Thread`.
* `pre_enter_objects`: an optional iterable of objects which
should be entered using `with`
If `pre_enter_objects` is supplied, these objects will be
entered before the `Thread` is started and exited when the
`Thread` target function ends.
If the `Thread` is _not_ started (`no_start=True`, very
unusual) then it will be the caller's responsibility to manage
to entered objects.
## <a name="DeadlockError"></a>Class `DeadlockError(builtins.RuntimeError)`
Raised by `NRLock` when a lock is attempted from the `Thread` currently holding the lock.
## <a name="HasThreadState"></a>Class `HasThreadState(cs.context.ContextManagerMixin)`
A mixin for classes with a `cs.threads.ThreadState` instance as `.state`
providing a context manager which pushes `current=self` onto that state
and a `default()` class method returning `cls.perthread_state.current`
as the default instance of that class.
*NOTE*: the documentation here refers to `cls.perthread_state`, but in
fact we honour the `cls.THREAD_STATE_ATTR` attribute to name
the state attribute which allows perclass state attributes,
and also use with classes which already use `.perthread_state` for
another purpose.
*NOTE*: `HasThreadState.Thread` is a _class_ method whose default
is to push state for all active `HasThreadState` subclasses.
Contrast with `HasThreadState.bg` which is an _instance_method
whose default is to push state for just that instance.
The top level `cs.threads.bg` function calls `HasThreadState.Thread`
to obtain its `Thread`.
*`HasThreadState.Thread(*, name=None, target, enter_objects=None, **Thread_kw)`*:
Factory for a `Thread` to push the `.current` state for the
currently active classes.
The optional parameter `enter_objects` may be used to pass
an iterable of objects whose contexts should be entered
using `with obj:`.
If this is set to `True` that indicates that every "current"
`HasThreadStates` instance should be entered.
The default does not enter any object contexts.
The `HasThreadStates.bg` method defaults to passing
`enter_objects=(self,)` to enter the context for `self`.
*`HasThreadState.__enter_exit__(self)`*:
Push `self.perthread_state.current=self` as the `Thread` local current instance.
Include `self.__class__` in the set of currently active classes for the duration.
*`HasThreadState.bg(self, func, *, enter_objects=None, **bg_kw)`*:
Get a `Thread` using `type(self).Thread` and start it.
Return the `Thread`.
The `HasThreadState.Thread` factory duplicates the current `Thread`'s
`HasThreadState` current objects as current in the new `Thread`.
Additionally it enters the contexts of various objects using
`with obj` according to the `enter_objects` parameter.
The value of the optional parameter `enter_objects` governs
which objects have their context entered using `with obj`
in the child `Thread` while running `func` as follows:
- `None`: the default, meaning `(self,)`
- `False`: no object contexts are entered
- `True`: all current `HasThreadState` object contexts will be entered
- an iterable of objects whose contexts will be entered;
pass `()` to enter no objects
*`HasThreadState.default(*, factory=None, raise_on_None=False)`*:
The default instance of this class from `cls.perthread_state.current`.
Parameters:
* `factory`: optional callable to create an instance of `cls`
if `cls.perthread_state.current` is `None` or missing;
if `factory` is `True` then `cls` is used as the factory
* `raise_on_None`: if `cls.perthread_state.current` is `None` or missing
and `factory` is false and `raise_on_None` is true,
raise a `RuntimeError`;
this is primarily a debugging aid
*`HasThreadState.get_thread_states(all_classes=None)`*:
Return a mapping of `class`->*current_instance*`
for use with `HasThreadState.with_thread_states`
or `HasThreadState.Thread` or `HasThreadState.bg`.
The default behaviour returns just a mapping for this class,
expecting the default instance to be responsible for what
other resources it holds.
There is also a legacy mode for `all_classes=True`
where the mapping is for all active classes,
probably best used for `Thread`s spawned outside
a `HasThreadState` context.
Parameters:
* `all_classes`: optional flag, default `False`;
if true, return a mapping of class to current instance
for all `HasThreadState` subclasses with an open instance,
otherwise just a mapping from this class to its current instance
## <a name="joinif"></a>`joinif(T: threading.Thread)`
Call `T.join()` if `T` is not the current `Thread`.
Unlike `threading.Thread.join`, this function is a no-op if
`T` is the current `Thread.
The use case is situations such as the shutdown phase of the
`MultiOpenMixin.startup_shutdown` context manager. Because
the "initial open" startup phase is not necessarily run in
the same thread as the "final close" shutdown phase, it is
possible for example for a worker `Thread` to execute the
shutdown phase and try to join itself. Using this function
supports that scenario.
## <a name="LockableMixin"></a>Class `LockableMixin`
Trite mixin to control access to an object via its `._lock` attribute.
Exposes the `._lock` as the property `.lock`.
Presents a context manager interface for obtaining an object's lock.
*`LockableMixin.__exit__(self, exc_type, exc_value, traceback)`*:
pylint: disable=unused-argument
*`LockableMixin.lock`*:
The internal lock object.
## <a name="locked"></a>`locked(*da, **dkw)`
A decorator for instance methods that must run within a lock.
Decorator keyword arguments:
* `initial_timeout`:
the initial lock attempt timeout;
if this is `>0` and exceeded a warning is issued
and then an indefinite attempt is made.
Default: `2.0`s
* `lockattr`:
the name of the attribute of `self`
which references the lock object.
Default `'_lock'`
## <a name="locked_property"></a>`locked_property(*da, **dkw)`
A thread safe property whose value is cached.
The lock is taken if the value needs to computed.
The default lock attribute is `._lock`.
The default attribute for the cached value is `._`*funcname*
where *funcname* is `func.__name__`.
The default "unset" value for the cache is `None`.
## <a name="monitor"></a>`monitor(*da, **dkw)`
Turn a class into a monitor, all of whose public methods are `@locked`.
This is a simple approach which requires class instances to have a
`._lock` which is an `RLock` or compatible
because methods may naively call each other.
Parameters:
* `attrs`: optional iterable of attribute names to wrap in `@locked`.
If omitted, all names commencing with a letter are chosen.
* `initial_timeout`: optional initial lock timeout, default `10.0`s.
* `lockattr`: optional lock attribute name, default `'_lock'`.
Only attributes satifying `inspect.ismethod` are wrapped
because `@locked` requires access to the instance `._lock` attribute.
## <a name="NRLock"></a>Class `NRLock`
A nonrecursive lock.
Attempting to take this lock when it is already held by the current `Thread`
will raise `DeadlockError`.
Otherwise this behaves like `threading.Lock`.
*`NRLock.acquire(self, *a, caller_frame=None, **kw)`*:
Acquire the lock as for `threading.Lock`.
Raises `DeadlockError` is the lock is already held by the current `Thread`.
*`NRLock.locked(self)`*:
Return the lock status.
*`NRLock.release(self)`*:
Release the lock as for `threading.Lock`.
## <a name="PriorityLock"></a>Class `PriorityLock`
A priority based mutex which is acquired by and released to waiters
in priority order.
The initialiser sets a default priority, itself defaulting to `0`.
The `acquire()` method accepts an optional `priority` value
which specifies the priority of the acquire request;
lower values have higher priorities.
`acquire` returns a new `PriorityLockSubLock`.
Note that internally this allocates a `threading.Lock` per acquirer.
When `acquire` is called, if the `PriorityLock` is taken
then the acquirer blocks on their personal `Lock`.
When `release()` is called the highest priority `Lock` is released.
Within a priority level `acquire`s are served in FIFO order.
Used as a context manager, the mutex is obtained at the default priority.
The `priority()` method offers a context manager
with a specified priority.
Both context managers return the `PriorityLockSubLock`
allocated by the `acquire`.
*`PriorityLock.__init__(self, default_priority=0, name=None)`*:
Initialise the `PriorityLock`.
Parameters:
* `default_priority`: the default `acquire` priority,
default `0`.
* `name`: optional identifying name
*`PriorityLock.__enter__(self)`*:
Enter the mutex as a context manager at the default priority.
Returns the new `Lock`.
*`PriorityLock.__exit__(self, *_)`*:
Exit the context manager.
*`PriorityLock.acquire(self, priority=None)`*:
Acquire the mutex with `priority` (default from `default_priority`).
Return the new `PriorityLockSubLock`.
This blocks behind any higher priority `acquire`s
or any earlier `acquire`s of the same priority.
*`PriorityLock.priority(self, this_priority)`*:
A context manager with the specified `this_priority`.
Returns the new `Lock`.
*`PriorityLock.release(self)`*:
Release the mutex.
Internally, this releases the highest priority `Lock`,
allowing that `acquire`r to go forward.
## <a name="PriorityLockSubLock"></a>Class `PriorityLockSubLock(PriorityLockSubLock)`
The record for the per-`acquire`r `Lock` held by `PriorityLock.acquire`.
## <a name="State"></a>Class `State(_thread._local)`
A `Thread` local object with attributes
which can be used as a context manager to stack attribute values.
Example:
from cs.threads import ThreadState
S = ThreadState(verbose=False)
with S(verbose=True) as prev_attrs:
if S.verbose:
print("verbose! (formerly verbose=%s)" % prev_attrs['verbose'])
*`State.__init__(self, **kw)`*:
Initiate the `ThreadState`, providing the per-Thread initial values.
*`State.__call__(self, **kw)`*:
Calling a `ThreadState` returns a context manager which stacks some state.
The context manager yields the previous values
for the attributes which were stacked.
## <a name="ThreadState"></a>Class `ThreadState(_thread._local)`
A `Thread` local object with attributes
which can be used as a context manager to stack attribute values.
Example:
from cs.threads import ThreadState
S = ThreadState(verbose=False)
with S(verbose=True) as prev_attrs:
if S.verbose:
print("verbose! (formerly verbose=%s)" % prev_attrs['verbose'])
*`ThreadState.__init__(self, **kw)`*:
Initiate the `ThreadState`, providing the per-Thread initial values.
*`ThreadState.__call__(self, **kw)`*:
Calling a `ThreadState` returns a context manager which stacks some state.
The context manager yields the previous values
for the attributes which were stacked.
## <a name="via"></a>`via(cmanager, func, *a, **kw)`
Return a callable that calls the supplied `func` inside a
`with` statement using the context manager `cmanager`.
This intended use case is aimed at deferred function calls.
# Release Log
*Release 20241005*:
Remove some debug noise.
*Release 20240630*:
* bg: use closeall instead of twostep/withall.
* HasThreadState.bg: drop pre_enter_objects (unused), gets plumbed by the **bg_kw.
*Release 20240422*:
HasThreadState.default: make factory and raise_on keyword only.
*Release 20240412*:
* New NRLock, an nonrecursive Lock and associated exception DeadlockError.
* bg: rename thread_class to thread_factory for clarity.
* HasThreadState: big refactor to separate the mapping of default instances from the previously automatic opening of a context for each.
* HasThreadState.bg: new optional pre_enter_objects to supply objects which should be opened before the Thread starts (before bg returns) and closed when the Thread exits.
*Release 20240316*:
Fixed release upload artifacts.
*Release 20240303*:
* HasThreadState: rename thread_states() to get_thread_states().
* HasThreadState.get_thread_states: some logic fixes.
*Release 20231129*:
* HasThreadState.thread_states: *policy change*: the default now makes a mapping only for this class, not for all HasThreadState subclasses, on the premise that this class can manage use of other classes if required.
* HasThreadState: new bg() class method like Thread() but also starting the Thread.
*Release 20230331*:
* HasThreadState: new thread_states() method to snapshot the current states.
* HasThreadState: new with_thread_states() context manager to apply a set of states.
* HasThreadState: rename the default state from .state to .perthread_state.
* HasThreadState.__enter_exit__: pass cls._HasThreadState_lock to stackset as the modification guard lock, prevents race in thread_states.
* Rename State to ThreadState, which how I always use it anyway, and leave a compatibility name behind.
* New joinif(Thread) method to join a Thread unless we are that Thread - this is because MultiOpenMixin.startup_shutdown stuff may run the shutdown in a differ Thread from that which ran the startup.
* @uses_runstate: use the prevailing RunState or create one.
* Drop Python 2 support.
*Release 20230212*:
* HasThreadState: maintain a set of the HasThreadState classes in use.
* New HasThreadState.Thread class factory method to create a new Thread with the current threads states at time of call instantiated in the new Thread.
* bg: new no_context=False parameter to suppress use of HasThreadState.Thread to create the new Thread.
*Release 20230125*:
New HasThreadState mixin for classes with a state=State() attribute to provide a cls.default() class method for the default instance and a context manager to push/pop self.state.current=self.
*Release 20221228*:
* Get error and warning from cs.gimmicks, breaks circular import with cs.logutils.
* Late import of cs.logutils.LogTime to avoid circular import.
*Release 20221207*:
Small bug fix.
*Release 20221118*:
REMOVE WorkerThreadPool, pulls in too many other things and was never used.
*Release 20211208*:
bg: do not pass the current Pfx prefix into the new Thread, seems to leak and grow.
*Release 20210306*:
bg: include the current Pfx prefix in the thread name and thread body Pfx, obsoletes cs.pfx.PfxThread.
*Release 20210123*:
New @monitor class decorator for simple RLock based reentrance protection.
*Release 20201025*:
* @locked: bump the default warning timeout to 10s, was firing too often.
* New State class for thread local state objects with default attribute values and a stacking __call__ context manager.
*Release 20200718*:
@locked: apply the interior __doc__ to the wrapper.
*Release 20200521*:
@locked_property: decorate with @cs.deco.decorator to support keyword arguments.
*Release 20191102*:
@locked: report slow-to-acquire locks, add initial_timeout and lockattr decorator keyword parameters.
*Release 20190923.2*:
Fix annoying docstring typo.
*Release 20190923.1*:
Docstring updates.
*Release 20190923*:
Remove dependence on cs.obj.
*Release 20190921*:
New PriorityLock class for a mutex which releases in (priority,fifo) order.
*Release 20190812*:
bg: compute default name before wrapping `func` in @logexc.
*Release 20190729*:
bg: provide default `name`, run callable inside Pfx, add optional no_logexc=False param preventing @logec wrapper if true.
*Release 20190422*:
bg(): new optional `no_start=False` keyword argument, preventing Thread.start if true
*Release 20190102*:
* Drop some unused classes.
* New LockableMixin, presenting a context manager and a .lock property.
*Release 20160828*:
Use "install_requires" instead of "requires" in DISTINFO.
*Release 20160827*:
* Replace bare "excepts" with "except BaseException".
* Doc updates. Other minor improvements.
*Release 20150115*:
First PyPI release.
Raw data
{
"_id": null,
"home_page": null,
"name": "cs.threads",
"maintainer": null,
"docs_url": null,
"requires_python": null,
"maintainer_email": null,
"keywords": "python2, python3",
"author": null,
"author_email": "Cameron Simpson <cs@cskk.id.au>",
"download_url": "https://files.pythonhosted.org/packages/97/b1/6f71d749c3353833de401f02c51fd8bfd67ba6affe853478b318e074a0d9/cs_threads-20241005.tar.gz",
"platform": null,
"description": "Thread related convenience classes and functions.\n\n*Latest release 20241005*:\nRemove some debug noise.\n\n## <a name=\"AdjustableSemaphore\"></a>Class `AdjustableSemaphore`\n\nA semaphore whose value may be tuned after instantiation.\n\n*`AdjustableSemaphore.acquire(self, blocking=True)`*:\nThe acquire() method calls the base acquire() method if not blocking.\nIf blocking is true, the base acquire() is called inside a lock to\navoid competing with a reducing adjust().\n\n*`AdjustableSemaphore.adjust(self, newvalue)`*:\nSet capacity to `newvalue`\nby calling release() or acquire() an appropriate number of times.\n\nIf `newvalue` lowers the semaphore capacity then adjust()\nmay block until the overcapacity is released.\n\n*`AdjustableSemaphore.adjust_delta(self, delta)`*:\nAdjust capacity by `delta` by calling release() or acquire()\nan appropriate number of times.\n\nIf `delta` lowers the semaphore capacity then adjust() may block\nuntil the overcapacity is released.\n\n*`AdjustableSemaphore.release(self)`*:\nRelease the semaphore.\n\n## <a name=\"bg\"></a>`bg(func, *, daemon=None, name=None, no_start=False, no_logexc=False, args=None, kwargs=None, thread_factory=None, pre_enter_objects=None, **tfkw)`\n\nDispatch the callable `func` in its own `Thread`;\nreturn the `Thread`.\n\nParameters:\n* `func`: a callable for the `Thread` target.\n* `args`, `kwargs`: passed to the `Thread` constructor\n* `kwargs`, `kwargs`: passed to the `Thread` constructor\n* `daemon`: optional argument specifying the `.daemon` attribute.\n* `name`: optional argument specifying the `Thread` name,\n default: the name of `func`.\n* `no_logexc`: if false (default `False`), wrap `func` in `@logexc`.\n* `no_start`: optional argument, default `False`.\n If true, do not start the `Thread`.\n* `pre_enter_objects`: an optional iterable of objects which\n should be entered using `with`\n\nIf `pre_enter_objects` is supplied, these objects will be\nentered before the `Thread` is started and exited when the\n`Thread` target function ends.\nIf the `Thread` is _not_ started (`no_start=True`, very\nunusual) then it will be the caller's responsibility to manage\nto entered objects.\n\n## <a name=\"DeadlockError\"></a>Class `DeadlockError(builtins.RuntimeError)`\n\nRaised by `NRLock` when a lock is attempted from the `Thread` currently holding the lock.\n\n## <a name=\"HasThreadState\"></a>Class `HasThreadState(cs.context.ContextManagerMixin)`\n\nA mixin for classes with a `cs.threads.ThreadState` instance as `.state`\nproviding a context manager which pushes `current=self` onto that state\nand a `default()` class method returning `cls.perthread_state.current`\nas the default instance of that class.\n\n*NOTE*: the documentation here refers to `cls.perthread_state`, but in\nfact we honour the `cls.THREAD_STATE_ATTR` attribute to name\nthe state attribute which allows perclass state attributes,\nand also use with classes which already use `.perthread_state` for\nanother purpose.\n\n*NOTE*: `HasThreadState.Thread` is a _class_ method whose default\nis to push state for all active `HasThreadState` subclasses.\nContrast with `HasThreadState.bg` which is an _instance_method\nwhose default is to push state for just that instance.\nThe top level `cs.threads.bg` function calls `HasThreadState.Thread`\nto obtain its `Thread`.\n\n*`HasThreadState.Thread(*, name=None, target, enter_objects=None, **Thread_kw)`*:\nFactory for a `Thread` to push the `.current` state for the\ncurrently active classes.\n\nThe optional parameter `enter_objects` may be used to pass\nan iterable of objects whose contexts should be entered\nusing `with obj:`.\nIf this is set to `True` that indicates that every \"current\"\n`HasThreadStates` instance should be entered.\nThe default does not enter any object contexts.\nThe `HasThreadStates.bg` method defaults to passing\n`enter_objects=(self,)` to enter the context for `self`.\n\n*`HasThreadState.__enter_exit__(self)`*:\nPush `self.perthread_state.current=self` as the `Thread` local current instance.\n\nInclude `self.__class__` in the set of currently active classes for the duration.\n\n*`HasThreadState.bg(self, func, *, enter_objects=None, **bg_kw)`*:\nGet a `Thread` using `type(self).Thread` and start it.\nReturn the `Thread`.\n\nThe `HasThreadState.Thread` factory duplicates the current `Thread`'s\n`HasThreadState` current objects as current in the new `Thread`.\nAdditionally it enters the contexts of various objects using\n`with obj` according to the `enter_objects` parameter.\n\nThe value of the optional parameter `enter_objects` governs\nwhich objects have their context entered using `with obj`\nin the child `Thread` while running `func` as follows:\n- `None`: the default, meaning `(self,)`\n- `False`: no object contexts are entered\n- `True`: all current `HasThreadState` object contexts will be entered\n- an iterable of objects whose contexts will be entered;\n pass `()` to enter no objects\n\n*`HasThreadState.default(*, factory=None, raise_on_None=False)`*:\nThe default instance of this class from `cls.perthread_state.current`.\n\nParameters:\n* `factory`: optional callable to create an instance of `cls`\n if `cls.perthread_state.current` is `None` or missing;\n if `factory` is `True` then `cls` is used as the factory\n* `raise_on_None`: if `cls.perthread_state.current` is `None` or missing\n and `factory` is false and `raise_on_None` is true,\n raise a `RuntimeError`;\n this is primarily a debugging aid\n\n*`HasThreadState.get_thread_states(all_classes=None)`*:\nReturn a mapping of `class`->*current_instance*`\nfor use with `HasThreadState.with_thread_states`\nor `HasThreadState.Thread` or `HasThreadState.bg`.\n\nThe default behaviour returns just a mapping for this class,\nexpecting the default instance to be responsible for what\nother resources it holds.\n\nThere is also a legacy mode for `all_classes=True`\nwhere the mapping is for all active classes,\nprobably best used for `Thread`s spawned outside\na `HasThreadState` context.\n\nParameters:\n* `all_classes`: optional flag, default `False`;\n if true, return a mapping of class to current instance\n for all `HasThreadState` subclasses with an open instance,\n otherwise just a mapping from this class to its current instance\n\n## <a name=\"joinif\"></a>`joinif(T: threading.Thread)`\n\nCall `T.join()` if `T` is not the current `Thread`.\n\nUnlike `threading.Thread.join`, this function is a no-op if\n`T` is the current `Thread.\n\nThe use case is situations such as the shutdown phase of the\n`MultiOpenMixin.startup_shutdown` context manager. Because\nthe \"initial open\" startup phase is not necessarily run in\nthe same thread as the \"final close\" shutdown phase, it is\npossible for example for a worker `Thread` to execute the\nshutdown phase and try to join itself. Using this function\nsupports that scenario.\n\n## <a name=\"LockableMixin\"></a>Class `LockableMixin`\n\nTrite mixin to control access to an object via its `._lock` attribute.\nExposes the `._lock` as the property `.lock`.\nPresents a context manager interface for obtaining an object's lock.\n\n*`LockableMixin.__exit__(self, exc_type, exc_value, traceback)`*:\npylint: disable=unused-argument\n\n*`LockableMixin.lock`*:\nThe internal lock object.\n\n## <a name=\"locked\"></a>`locked(*da, **dkw)`\n\nA decorator for instance methods that must run within a lock.\n\nDecorator keyword arguments:\n* `initial_timeout`:\n the initial lock attempt timeout;\n if this is `>0` and exceeded a warning is issued\n and then an indefinite attempt is made.\n Default: `2.0`s\n* `lockattr`:\n the name of the attribute of `self`\n which references the lock object.\n Default `'_lock'`\n\n## <a name=\"locked_property\"></a>`locked_property(*da, **dkw)`\n\nA thread safe property whose value is cached.\nThe lock is taken if the value needs to computed.\n\nThe default lock attribute is `._lock`.\nThe default attribute for the cached value is `._`*funcname*\nwhere *funcname* is `func.__name__`.\nThe default \"unset\" value for the cache is `None`.\n\n## <a name=\"monitor\"></a>`monitor(*da, **dkw)`\n\nTurn a class into a monitor, all of whose public methods are `@locked`.\n\nThis is a simple approach which requires class instances to have a\n`._lock` which is an `RLock` or compatible\nbecause methods may naively call each other.\n\nParameters:\n* `attrs`: optional iterable of attribute names to wrap in `@locked`.\n If omitted, all names commencing with a letter are chosen.\n* `initial_timeout`: optional initial lock timeout, default `10.0`s.\n* `lockattr`: optional lock attribute name, default `'_lock'`.\n\nOnly attributes satifying `inspect.ismethod` are wrapped\nbecause `@locked` requires access to the instance `._lock` attribute.\n\n## <a name=\"NRLock\"></a>Class `NRLock`\n\nA nonrecursive lock.\nAttempting to take this lock when it is already held by the current `Thread`\nwill raise `DeadlockError`.\nOtherwise this behaves like `threading.Lock`.\n\n*`NRLock.acquire(self, *a, caller_frame=None, **kw)`*:\nAcquire the lock as for `threading.Lock`.\nRaises `DeadlockError` is the lock is already held by the current `Thread`.\n\n*`NRLock.locked(self)`*:\nReturn the lock status.\n\n*`NRLock.release(self)`*:\nRelease the lock as for `threading.Lock`.\n\n## <a name=\"PriorityLock\"></a>Class `PriorityLock`\n\nA priority based mutex which is acquired by and released to waiters\nin priority order.\n\nThe initialiser sets a default priority, itself defaulting to `0`.\n\nThe `acquire()` method accepts an optional `priority` value\nwhich specifies the priority of the acquire request;\nlower values have higher priorities.\n`acquire` returns a new `PriorityLockSubLock`.\n\nNote that internally this allocates a `threading.Lock` per acquirer.\n\nWhen `acquire` is called, if the `PriorityLock` is taken\nthen the acquirer blocks on their personal `Lock`.\n\nWhen `release()` is called the highest priority `Lock` is released.\n\nWithin a priority level `acquire`s are served in FIFO order.\n\nUsed as a context manager, the mutex is obtained at the default priority.\nThe `priority()` method offers a context manager\nwith a specified priority.\nBoth context managers return the `PriorityLockSubLock`\nallocated by the `acquire`.\n\n*`PriorityLock.__init__(self, default_priority=0, name=None)`*:\nInitialise the `PriorityLock`.\n\nParameters:\n* `default_priority`: the default `acquire` priority,\n default `0`.\n* `name`: optional identifying name\n\n*`PriorityLock.__enter__(self)`*:\nEnter the mutex as a context manager at the default priority.\nReturns the new `Lock`.\n\n*`PriorityLock.__exit__(self, *_)`*:\nExit the context manager.\n\n*`PriorityLock.acquire(self, priority=None)`*:\nAcquire the mutex with `priority` (default from `default_priority`).\nReturn the new `PriorityLockSubLock`.\n\nThis blocks behind any higher priority `acquire`s\nor any earlier `acquire`s of the same priority.\n\n*`PriorityLock.priority(self, this_priority)`*:\nA context manager with the specified `this_priority`.\nReturns the new `Lock`.\n\n*`PriorityLock.release(self)`*:\nRelease the mutex.\n\nInternally, this releases the highest priority `Lock`,\nallowing that `acquire`r to go forward.\n\n## <a name=\"PriorityLockSubLock\"></a>Class `PriorityLockSubLock(PriorityLockSubLock)`\n\nThe record for the per-`acquire`r `Lock` held by `PriorityLock.acquire`.\n\n## <a name=\"State\"></a>Class `State(_thread._local)`\n\nA `Thread` local object with attributes\nwhich can be used as a context manager to stack attribute values.\n\nExample:\n\n from cs.threads import ThreadState\n\n S = ThreadState(verbose=False)\n\n with S(verbose=True) as prev_attrs:\n if S.verbose:\n print(\"verbose! (formerly verbose=%s)\" % prev_attrs['verbose'])\n\n*`State.__init__(self, **kw)`*:\nInitiate the `ThreadState`, providing the per-Thread initial values.\n\n*`State.__call__(self, **kw)`*:\nCalling a `ThreadState` returns a context manager which stacks some state.\nThe context manager yields the previous values\nfor the attributes which were stacked.\n\n## <a name=\"ThreadState\"></a>Class `ThreadState(_thread._local)`\n\nA `Thread` local object with attributes\nwhich can be used as a context manager to stack attribute values.\n\nExample:\n\n from cs.threads import ThreadState\n\n S = ThreadState(verbose=False)\n\n with S(verbose=True) as prev_attrs:\n if S.verbose:\n print(\"verbose! (formerly verbose=%s)\" % prev_attrs['verbose'])\n\n*`ThreadState.__init__(self, **kw)`*:\nInitiate the `ThreadState`, providing the per-Thread initial values.\n\n*`ThreadState.__call__(self, **kw)`*:\nCalling a `ThreadState` returns a context manager which stacks some state.\nThe context manager yields the previous values\nfor the attributes which were stacked.\n\n## <a name=\"via\"></a>`via(cmanager, func, *a, **kw)`\n\nReturn a callable that calls the supplied `func` inside a\n`with` statement using the context manager `cmanager`.\nThis intended use case is aimed at deferred function calls.\n\n# Release Log\n\n\n\n*Release 20241005*:\nRemove some debug noise.\n\n*Release 20240630*:\n* bg: use closeall instead of twostep/withall.\n* HasThreadState.bg: drop pre_enter_objects (unused), gets plumbed by the **bg_kw.\n\n*Release 20240422*:\nHasThreadState.default: make factory and raise_on keyword only.\n\n*Release 20240412*:\n* New NRLock, an nonrecursive Lock and associated exception DeadlockError.\n* bg: rename thread_class to thread_factory for clarity.\n* HasThreadState: big refactor to separate the mapping of default instances from the previously automatic opening of a context for each.\n* HasThreadState.bg: new optional pre_enter_objects to supply objects which should be opened before the Thread starts (before bg returns) and closed when the Thread exits.\n\n*Release 20240316*:\nFixed release upload artifacts.\n\n*Release 20240303*:\n* HasThreadState: rename thread_states() to get_thread_states().\n* HasThreadState.get_thread_states: some logic fixes.\n\n*Release 20231129*:\n* HasThreadState.thread_states: *policy change*: the default now makes a mapping only for this class, not for all HasThreadState subclasses, on the premise that this class can manage use of other classes if required.\n* HasThreadState: new bg() class method like Thread() but also starting the Thread.\n\n*Release 20230331*:\n* HasThreadState: new thread_states() method to snapshot the current states.\n* HasThreadState: new with_thread_states() context manager to apply a set of states.\n* HasThreadState: rename the default state from .state to .perthread_state.\n* HasThreadState.__enter_exit__: pass cls._HasThreadState_lock to stackset as the modification guard lock, prevents race in thread_states.\n* Rename State to ThreadState, which how I always use it anyway, and leave a compatibility name behind.\n* New joinif(Thread) method to join a Thread unless we are that Thread - this is because MultiOpenMixin.startup_shutdown stuff may run the shutdown in a differ Thread from that which ran the startup.\n* @uses_runstate: use the prevailing RunState or create one.\n* Drop Python 2 support.\n\n*Release 20230212*:\n* HasThreadState: maintain a set of the HasThreadState classes in use.\n* New HasThreadState.Thread class factory method to create a new Thread with the current threads states at time of call instantiated in the new Thread.\n* bg: new no_context=False parameter to suppress use of HasThreadState.Thread to create the new Thread.\n\n*Release 20230125*:\nNew HasThreadState mixin for classes with a state=State() attribute to provide a cls.default() class method for the default instance and a context manager to push/pop self.state.current=self.\n\n*Release 20221228*:\n* Get error and warning from cs.gimmicks, breaks circular import with cs.logutils.\n* Late import of cs.logutils.LogTime to avoid circular import.\n\n*Release 20221207*:\nSmall bug fix.\n\n*Release 20221118*:\nREMOVE WorkerThreadPool, pulls in too many other things and was never used.\n\n*Release 20211208*:\nbg: do not pass the current Pfx prefix into the new Thread, seems to leak and grow.\n\n*Release 20210306*:\nbg: include the current Pfx prefix in the thread name and thread body Pfx, obsoletes cs.pfx.PfxThread.\n\n*Release 20210123*:\nNew @monitor class decorator for simple RLock based reentrance protection.\n\n*Release 20201025*:\n* @locked: bump the default warning timeout to 10s, was firing too often.\n* New State class for thread local state objects with default attribute values and a stacking __call__ context manager.\n\n*Release 20200718*:\n@locked: apply the interior __doc__ to the wrapper.\n\n*Release 20200521*:\n@locked_property: decorate with @cs.deco.decorator to support keyword arguments.\n\n*Release 20191102*:\n@locked: report slow-to-acquire locks, add initial_timeout and lockattr decorator keyword parameters.\n\n*Release 20190923.2*:\nFix annoying docstring typo.\n\n*Release 20190923.1*:\nDocstring updates.\n\n*Release 20190923*:\nRemove dependence on cs.obj.\n\n*Release 20190921*:\nNew PriorityLock class for a mutex which releases in (priority,fifo) order.\n\n*Release 20190812*:\nbg: compute default name before wrapping `func` in @logexc.\n\n*Release 20190729*:\nbg: provide default `name`, run callable inside Pfx, add optional no_logexc=False param preventing @logec wrapper if true.\n\n*Release 20190422*:\nbg(): new optional `no_start=False` keyword argument, preventing Thread.start if true\n\n*Release 20190102*:\n* Drop some unused classes.\n* New LockableMixin, presenting a context manager and a .lock property.\n\n*Release 20160828*:\nUse \"install_requires\" instead of \"requires\" in DISTINFO.\n\n*Release 20160827*:\n* Replace bare \"excepts\" with \"except BaseException\".\n* Doc updates. Other minor improvements.\n\n*Release 20150115*:\nFirst PyPI release.\n",
"bugtrack_url": null,
"license": "GNU General Public License v3 or later (GPLv3+)",
"summary": "threading and communication/synchronisation conveniences",
"version": "20241005",
"project_urls": {
"MonoRepo Commits": "https://bitbucket.org/cameron_simpson/css/commits/branch/main",
"Monorepo Git Mirror": "https://github.com/cameron-simpson/css",
"Monorepo Hg/Mercurial Mirror": "https://hg.sr.ht/~cameron-simpson/css",
"Source": "https://github.com/cameron-simpson/css/blob/main/lib/python/cs/threads.py"
},
"split_keywords": [
"python2",
" python3"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "66cb46309522f83fa4264e90c8e67de9fe956659763481add03ddcfc16257466",
"md5": "9b6f3bd38c6cc7dd5d50ccda0c6ef782",
"sha256": "7cc8f38acda02bb589542d62950ace5076224fa24143c5185b826f75ebcfb8e5"
},
"downloads": -1,
"filename": "cs.threads-20241005-py3-none-any.whl",
"has_sig": false,
"md5_digest": "9b6f3bd38c6cc7dd5d50ccda0c6ef782",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": null,
"size": 15355,
"upload_time": "2024-10-05T08:18:56",
"upload_time_iso_8601": "2024-10-05T08:18:56.138052Z",
"url": "https://files.pythonhosted.org/packages/66/cb/46309522f83fa4264e90c8e67de9fe956659763481add03ddcfc16257466/cs.threads-20241005-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "97b16f71d749c3353833de401f02c51fd8bfd67ba6affe853478b318e074a0d9",
"md5": "4b613a197a0d375c0c8c2fa346869f36",
"sha256": "b86c01e1fac7df3a69a1bc8ee5c6484b9535df895069593871ef390d62dd79da"
},
"downloads": -1,
"filename": "cs_threads-20241005.tar.gz",
"has_sig": false,
"md5_digest": "4b613a197a0d375c0c8c2fa346869f36",
"packagetype": "sdist",
"python_version": "source",
"requires_python": null,
"size": 18010,
"upload_time": "2024-10-05T08:18:58",
"upload_time_iso_8601": "2024-10-05T08:18:58.166303Z",
"url": "https://files.pythonhosted.org/packages/97/b1/6f71d749c3353833de401f02c51fd8bfd67ba6affe853478b318e074a0d9/cs_threads-20241005.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-10-05 08:18:58",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "cameron-simpson",
"github_project": "css",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "cs.threads"
}