cs.context


Namecs.context JSON
Version 20240412 PyPI version JSON
download
home_pageNone
SummaryAssorted context managers.
upload_time2024-04-12 02:33:06
maintainerNone
docs_urlNone
authorNone
requires_pythonNone
licenseGNU General Public License v3 or later (GPLv3+)
keywords python2 python3
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            Assorted context managers.

*Latest release 20240412*:
* contextif: rework to be much easier to use, add new call modes.
* pushkeys, stackkeys: support update dicts whose keys are not identifier strings i.e. a non **kw call mode.
* New withif() function returning a context manager even for objects which do not provide one.
* New withall(iterable-of-context-managers) context manager.

## Function `contextif(cmgr, *cmgr_args, **cmgr_kwargs)`

A context manager to use `cmgr` conditionally,
with a flexible call signature.
This yields the context manager if `cmgr` is used or `None`
if it is not used, allowing the enclosed code to test whether
the context is active.

This is to ease uses where the context object is optional
i.e. `None` if not present. Example from `cs.vt.stream`:

    @contextmanager
    def startup_shutdown(self):
      """ Open/close `self.local_store` if not `None`.
      """
      with super().startup_shutdown():
        with contextif(self.local_store):
          with self._packet_connection(self.recv, self.send) as conn:
            with stackattrs(self, _conn=conn):
              yield

Here `self.local_store` might be `None` if there's no local
store to present. We still want a nice nested `with` statement
during the setup. By using `contextif` we run a context manager
which behaves correctly when `self.local_store=None`.

The signature is flexible, offering 2 basic modes of use.

Flagged use: `contextif(flag,cmgr,*a,**kw)`: if `flag` is a
Boolean then it governs whether the context manager `cmgr`
is used. Historically the driving use case was verbosity
dependent status lines or progress bars. Example:

    from cs.upd import run_task
    with contextif(verbose, run_task, ....) as proxy:
        ... do stuff, updating proxy if not None ...

Unflagged use: `contextif(cmgr,*a,**kw)`: use `cmgr` as the
flag: if false (eg `None`) then `cmgr` is not used.

Additionally, `cmgr` may be a callable, in which case the
context manager itself is obtained by calling
`cmgr,*cmgr_args,**cmgr_kwargs)`. Otherwise `cmgr` is assumed
to be a context manager already, and it is an error to provide
`cmgr_args` or `cmgr_kwargs`.

In the `cs.upd` example above, `run_task` is a context manager
function which pops up an updatable status line, normally
used as:

    with run_task("doing thing") as proxy:
        ... do the thing, setting proxy.text as needed ...

## Class `ContextManagerMixin`

A mixin to provide context manager `__enter__` and `__exit__` methods
running the first and second steps of a single `__enter_exit__` generator method.

*Note*: the `__enter_exit__` method is _not_ a context manager,
but a short generator method.

This makes it easy to use context managers inside `__enter_exit__`
as the setup/teardown process, for example:

    def __enter_exit__(self):
        with open(self.datafile, 'r') as f:
            yield f

Like a context manager created via `@contextmanager`
it performs the setup phase and then `yield`s the value for the `with` statement.
If `None` is `yield`ed (as from a bare `yield`)
then `self` is returned from `__enter__`.
As with `@contextmanager`,
if there was an exception in the managed suite
then that exception is raised on return from the `yield`.

*However*, and _unlike_ a `@contextmanager` method,
the `__enter_exit__` generator _may_ also `yield`
an additional true/false value to use as the result
of the `__exit__` method, to indicate whether the exception was handled.
This extra `yield` is _optional_ and if it is omitted the `__exit__` result
will be `False` indicating that an exception was not handled.

Here is a sketch of a method which can handle a `SomeException` specially:

    class CMgr(ContextManagerMixin):
        def __enter_exit__(self):
            ... do some setup here ...
            # Returning self is common, but might be any relevant value.
            # Note that if you want `self`, you can just use a bare yield
            # and ContextManagerMixin will provide `self` as the default.
            enter_result = self
            exit_result = False
            try:
                yield enter_result
            except SomeException as e:
                ... handle e ...
                exit_result = True
            finally:
                ... do tear down here ...
            yield exit_result

*Method `ContextManagerMixin.__enter__(self)`*:
Run `super().__enter__` (if any)
then the `__enter__` phase of `self.__enter_exit__()`.

*Method `ContextManagerMixin.__exit__(self, exc_type, exc_value, traceback)`*:
Run the `__exit__` step of `self.__enter_exit__()`,
then `super().__exit__` (if any).

*Method `ContextManagerMixin.as_contextmanager(self)`*:
Run the generator from the `cls` class specific `__enter_exit__`
method via `self` as a context manager.

Example from `RunState` which subclasses `HasThreadState`,
both of which are `ContextManagerMixin` subclasses:

    class RunState(HasThreadState):
        .....
        def __enter_exit__(self):
            with HasThreadState.as_contextmanager(self):
                ... RunState context manager stuff ...

This runs the `HasThreadState` context manager
around the main `RunState` context manager.

## Function `pop_cmgr(o, attr)`

Run the `__exit__` phase of a context manager commenced with `push_cmgr`.
Restore `attr` as it was before `push_cmgr`.
Return the result of `__exit__`.

## Function `popattrs(o, attr_names, old_values)`

The "pop" part of `stackattrs`.
Restore previous attributes of `o`
named by `attr_names` with previous state in `old_values`.

This can be useful in hooks/signals/callbacks,
where you cannot inline a context manager.

## Function `popkeys(d, key_names, old_values)`

The "pop" part of `stackkeys`.
Restore previous key values of `d`
named by `key_names` with previous state in `old_values`.

This can be useful in hooks/signals/callbacks,
where you cannot inline a context manager.

## Function `push_cmgr(o, attr, cmgr)`

A convenience wrapper for `twostep(cmgr)`
to run the `__enter__` phase of `cmgr` and save its value as `o.`*attr*`.
Return the result of the `__enter__` phase.

The `__exit__` phase is run by `pop_cmgr(o,attr)`,
returning the return value of the exit phase.

Example use in a unit test:

    class TestThing(unittest.TestCase):
        def setUp(self):
            # save the temp dir path as self.dirpath
            push_cmgr(self, 'dirpath', TemporaryDirectory())
        def tearDown(self):
            # clean up the temporary directory, discard self.dirpath
            pop_cmgr(self, 'dirpath')

The `cs.testutils` `SetupTeardownMixin` class does this
allowing the provision of a single `setupTeardown()` context manager method
for test case setUp/tearDown.

Doc test:

    >>> from os.path import isdir as isdirpath
    >>> from tempfile import TemporaryDirectory
    >>> from types import SimpleNamespace
    >>> obj = SimpleNamespace()
    >>> dirpath = push_cmgr(obj, 'path', TemporaryDirectory())
    >>> assert dirpath == obj.path
    >>> assert isdirpath(dirpath)
    >>> pop_cmgr(obj, 'path')
    >>> assert not hasattr(obj, 'path')
    >>> assert not isdirpath(dirpath)

## Function `pushattrs(o, **attr_values)`

The "push" part of `stackattrs`.
Push `attr_values` onto `o` as attributes,
return the previous attribute values in a `dict`.

This can be useful in hooks/signals/callbacks,
where you cannot inline a context manager.

## Function `pushkeys(d, kv=None, **kw)`

The "push" part of `stackkeys`.
Use the mapping provided as `kv` or `kw` to update `d`.
Return the previous key values in a `dict`.

This can be useful in hooks/signals/callbacks,
where you cannot inline a context manager using `stackkeys`.

## Function `reconfigure_file(f, **kw)`

Context manager flavour of `TextIOBase.reconfigure`.

## Function `setup_cmgr(cmgr)`

Run the set up phase of the context manager `cmgr`
and return a callable which runs the tear down phase.

This is a convenience wrapper for the lower level `twostep()` function
which produces a two iteration generator from a context manager.

Please see the `push_cmgr` function, a superior wrapper for `twostep()`.

*Note*:
this function expects `cmgr` to be an existing context manager.
In particular, if you define some context manager function like this:

    @contextmanager
    def my_cmgr_func(...):
        ...
        yield
        ...

then the correct use of `setup_cmgr()` is:

    teardown = setup_cmgr(my_cmgr_func(...))

and _not_:

    cmgr_iter = setup_cmgr(my_cmgr_func)
    ...

The purpose of `setup_cmgr()` is to split any context manager's operation
across two steps when the set up and teardown phases must operate
in different parts of your code.
A common situation is the `__enter__` and `__exit__` methods
of another context manager class.

The call to `setup_cmgr()` performs the "enter" phase
and returns the tear down callable.
Calling that performs the tear down phase.

Example use in a class:

    class SomeClass:
        def __init__(self, foo)
            self.foo = foo
            self._teardown = None
        def __enter__(self):
            self._teardown = setup_cmgr(stackattrs(o, setting=foo))
        def __exit__(self, *_):
            teardown, self._teardown = self._teardown, None
            teardown()

## Function `stack_signals(signums, handler, additional=False)`

Context manager to apply a handler function to `signums`
using `signal.signal`.
The old handlers are restored on exit from the context manager.

If the optional `additional` argument is true,
apply a handler which calls both the new handler and the old handler.

## Function `stackattrs(o, **attr_values)`

Context manager to push new values for the attributes of `o`
and to restore them afterward.
Returns a `dict` containing a mapping of the previous attribute values.
Attributes not present are not present in the returned mapping.

Restoration includes deleting attributes which were not present
initially.

This makes it easy to adjust temporarily some shared context object
without having to pass it through the call stack.

See `stackkeys` for a flavour of this for mappings.

See `cs.threads.ThreadState` for a convenient wrapper class.

Example of fiddling a programme's "verbose" mode:

    >>> class RunModes:
    ...     def __init__(self, verbose=False):
    ...         self.verbose = verbose
    ...
    >>> runmode = RunModes()
    >>> if runmode.verbose:
    ...     print("suppressed message")
    ...
    >>> with stackattrs(runmode, verbose=True):
    ...     if runmode.verbose:
    ...         print("revealed message")
    ...
    revealed message
    >>> if runmode.verbose:
    ...     print("another suppressed message")
    ...

Example exhibiting restoration of absent attributes:

    >>> class O:
    ...     def __init__(self):
    ...         self.a = 1
    ...
    >>> o = O()
    >>> print(o.a)
    1
    >>> print(o.b)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'O' object has no attribute 'b'
    >>> with stackattrs(o, a=3, b=4):
    ...     print(o.a)
    ...     print(o.b)
    ...     o.b = 5
    ...     print(o.b)
    ...     delattr(o, 'a')
    ...
    3
    4
    5
    >>> print(o.a)
    1
    >>> print(o.b)
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    AttributeError: 'O' object has no attribute 'b'

## Function `stackkeys(d, kv=None, **kw)`

A context manager to push new values for the key values of `d`
and to restore them afterward.
The new values are provided as `kv` or `kw` as convenient.
Returns a `dict` containing a mapping of the previous key values.
Keys not present are not present in the mapping.

Restoration includes deleting key values which were not present
initially.

This makes it easy to adjust temporarily some shared context object
without having to pass it through the call stack.

See `stackattrs` for a flavour of this for object attributes.

Example of making log entries which may reference
some higher level context log entry:

    >>> import time
    >>> global_context = {
    ...     'parent': None,
    ... }
    >>> def log_entry(desc, **kw):
    ...     print("log_entry: global_context =", repr(global_context))
    ...     entry = dict(global_context)
    ...     entry.update(desc=desc, when=time.time())
    ...     entry.update(kw)
    ...     return entry
    ...
    >>> log_entry("stand alone entry")    #doctest: +ELLIPSIS
    log_entry: global_context = {'parent': None}
    {'parent': None, 'desc': 'stand alone entry', 'when': ...}
    >>> context_entry = log_entry("high level entry")
    log_entry: global_context = {'parent': None}
    >>> context_entry                     #doctest: +ELLIPSIS
    {'parent': None, 'desc': 'high level entry', 'when': ...}
    >>> with stackkeys(global_context, parent=context_entry): #doctest: +ELLIPSIS
    ...     print(repr(log_entry("low level event")))
    ...
    log_entry: global_context = {'parent': {'parent': None, 'desc': 'high level entry', 'when': ...}}
    {'parent': {'parent': None, 'desc': 'high level entry', 'when': ...}, 'desc': 'low level event', 'when': ...}
    >>> log_entry("another standalone entry")    #doctest: +ELLIPSIS
    log_entry: global_context = {'parent': None}
    {'parent': None, 'desc': 'another standalone entry', 'when': ...}

## Function `stackset(s, element, lock=None)`

Context manager to add `element` to the set `s` and remove it on return.
The element is neither added nor removed if it is already present.

## Function `twostep(cmgr)`

Return a generator which operates the context manager `cmgr`.

The first iteration performs the "enter" phase and yields the result.
The second iteration performs the "exit" phase and yields `None`.

See also the `push_cmgr(obj,attr,cmgr)` function
and its partner `pop_cmgr(obj,attr)`
which form a convenient wrapper for this low level generator.

The purpose of `twostep()` is to split any context manager's operation
across two steps when the set up and tear down phases must operate
in different parts of your code.
A common situation is the `__enter__` and `__exit__` methods
of another context manager class
or the `setUp` and `tearDown` methods of a unit test case.

*Note*:
this function expects `cmgr` to be an existing context manager
and _not_ the function which returns the context manager.

In particular, if you define some function like this:

    @contextmanager
    def my_cmgr_func(...):
        ...
        yield
        ...

then `my_cmgr_func(...)` returns a context manager instance
and so the correct use of `twostep()` is like this:

    # steps broken out for clarity
    cmgr = my_cmgr_func(...)
    cmgr_iter = twostep(cmgr)
    next(cmgr_iter)   # set up
    next(cmgr_iter)   # tear down

and _not_:

    cmgr_iter = twostep(my_cmgr_func)
    next(cmgr_iter)   # set up
    next(cmgr_iter)   # tear down

Example use in a class (but really you should use
`push_cmgr`/`pop_cmgr` instead):

    class SomeClass:
        def __init__(self, foo)
            self.foo = foo
            self._cmgr_ = None
        def __enter__(self):
            self._cmgr_stepped = twostep(stackattrs(o, setting=foo))
            self._cmgr = next(self._cmgr_stepped)
            return self._cmgr
        def __exit__(self, *_):
            next(self._cmgr_stepped)
            self._cmgr = None

## Function `withall(objs)`

Enter every object `obj` in `obj_list` except those which are `None`
using `with obj:`, then yield.

## Function `withif(obj)`

Return a context manager for `obj`.
If `obj` has an `__enter__` attribute, return `obj`
otherwise return `nullcontext()`.

Example:

    with withif(inner_mapping):
      ... work with inner_mapping ...

# Release Log



*Release 20240412*:
* contextif: rework to be much easier to use, add new call modes.
* pushkeys, stackkeys: support update dicts whose keys are not identifier strings i.e. a non **kw call mode.
* New withif() function returning a context manager even for objects which do not provide one.
* New withall(iterable-of-context-managers) context manager.

*Release 20240316*:
Fixed release upload artifacts.

*Release 20240212.1*:
Minor doc updates.

*Release 20240212*:
New reconfigure_file(f,**kw), a context manager flavour of `TextIOBase.reconfigure`.

*Release 20240201*:
contextif: require the flag to be a bool.

*Release 20230331*:
stackset: accept optional lock to guard modification of the set.

*Release 20230212*:
* BREAKING: drop StackableState, superceded by cs.threads.State.
* New stackset(set,element) to push and then pop an element to a set unless it is already there.

*Release 20230125*:
New ContextManagerMixin.as_contextmanager(cls,self) class method to run the __enter_exit__ from a specific class, useful in subclasses.

*Release 20230109*:
New contextif(flag,cmgr_func,...) context manager to use cmgr_func if flag is true otherwise nullcontext.

*Release 20221118*:
stackattrs: improve docstring.

*Release 20220619*:
twostep: the returned "tear down" phase function needs to ignore StopIteration from the context manager, see PEP 479.

*Release 20220227*:
New stack_signals context manager to push signal handlers.

*Release 20211115.1*:
Docstring grammar/phrasing updates.

*Release 20211115*:
Rename `enter_exit` to `__enter_exit__` - the user doesn't call this overtly and it aligns better with `__enter__` and `__exit__`.

*Release 20211114.1*:
ContextManagerMixin: the default __enter__ return is self, supporting a trivial bare `yield` in the generator.

*Release 20211114*:
New ContextManagerMixin mixin class to implement the __enter__/__exit__ methods using a simple generator function named enter_exit.

*Release 20210727*:
* twostep: iteration 1 now returns the result of __enter__, iteration 2 now returns None.
* New functions push_cmgr(obj,attr,cmgr) and partner pop_cmgr(obj,attr) to run a twostep()ed context manager conveniently, more conveniently than setup_cmgr().

*Release 20210420.1*:
Rerelease after completing stalled merge: docstring updates.

*Release 20210420*:
Docstring corrections and improvements.

*Release 20210306*:
* New twostep() and setup_cmgr() functions to split a context manager into set up and teardown phases for when these must occur in different parts of the code.
* New thread local StackableState class which can be called to push attribute changes with stackattrs - intended for use as shared global state to avoiod passing through deep function call chains.

*Release 20200725.1*:
Docstring improvements.

*Release 20200725*:
New stackkeys and components pushkeys and popkeys doing "stackattrs for dicts/mappings".

*Release 20200517*:
* Add `nullcontext` like the one from recent contextlib.
* stackattrs: expose the push and pop parts as pushattrs() and popattrs().

*Release 20200228.1*:
Initial release with stackattrs context manager.


            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "cs.context",
    "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/0a/42/0722b3b34cb5d455acc6d5ba81b9b02fd260240f25a6cad61aa2d7b24a87/cs.context-20240412.tar.gz",
    "platform": null,
    "description": "Assorted context managers.\n\n*Latest release 20240412*:\n* contextif: rework to be much easier to use, add new call modes.\n* pushkeys, stackkeys: support update dicts whose keys are not identifier strings i.e. a non **kw call mode.\n* New withif() function returning a context manager even for objects which do not provide one.\n* New withall(iterable-of-context-managers) context manager.\n\n## Function `contextif(cmgr, *cmgr_args, **cmgr_kwargs)`\n\nA context manager to use `cmgr` conditionally,\nwith a flexible call signature.\nThis yields the context manager if `cmgr` is used or `None`\nif it is not used, allowing the enclosed code to test whether\nthe context is active.\n\nThis is to ease uses where the context object is optional\ni.e. `None` if not present. Example from `cs.vt.stream`:\n\n    @contextmanager\n    def startup_shutdown(self):\n      \"\"\" Open/close `self.local_store` if not `None`.\n      \"\"\"\n      with super().startup_shutdown():\n        with contextif(self.local_store):\n          with self._packet_connection(self.recv, self.send) as conn:\n            with stackattrs(self, _conn=conn):\n              yield\n\nHere `self.local_store` might be `None` if there's no local\nstore to present. We still want a nice nested `with` statement\nduring the setup. By using `contextif` we run a context manager\nwhich behaves correctly when `self.local_store=None`.\n\nThe signature is flexible, offering 2 basic modes of use.\n\nFlagged use: `contextif(flag,cmgr,*a,**kw)`: if `flag` is a\nBoolean then it governs whether the context manager `cmgr`\nis used. Historically the driving use case was verbosity\ndependent status lines or progress bars. Example:\n\n    from cs.upd import run_task\n    with contextif(verbose, run_task, ....) as proxy:\n        ... do stuff, updating proxy if not None ...\n\nUnflagged use: `contextif(cmgr,*a,**kw)`: use `cmgr` as the\nflag: if false (eg `None`) then `cmgr` is not used.\n\nAdditionally, `cmgr` may be a callable, in which case the\ncontext manager itself is obtained by calling\n`cmgr,*cmgr_args,**cmgr_kwargs)`. Otherwise `cmgr` is assumed\nto be a context manager already, and it is an error to provide\n`cmgr_args` or `cmgr_kwargs`.\n\nIn the `cs.upd` example above, `run_task` is a context manager\nfunction which pops up an updatable status line, normally\nused as:\n\n    with run_task(\"doing thing\") as proxy:\n        ... do the thing, setting proxy.text as needed ...\n\n## Class `ContextManagerMixin`\n\nA mixin to provide context manager `__enter__` and `__exit__` methods\nrunning the first and second steps of a single `__enter_exit__` generator method.\n\n*Note*: the `__enter_exit__` method is _not_ a context manager,\nbut a short generator method.\n\nThis makes it easy to use context managers inside `__enter_exit__`\nas the setup/teardown process, for example:\n\n    def __enter_exit__(self):\n        with open(self.datafile, 'r') as f:\n            yield f\n\nLike a context manager created via `@contextmanager`\nit performs the setup phase and then `yield`s the value for the `with` statement.\nIf `None` is `yield`ed (as from a bare `yield`)\nthen `self` is returned from `__enter__`.\nAs with `@contextmanager`,\nif there was an exception in the managed suite\nthen that exception is raised on return from the `yield`.\n\n*However*, and _unlike_ a `@contextmanager` method,\nthe `__enter_exit__` generator _may_ also `yield`\nan additional true/false value to use as the result\nof the `__exit__` method, to indicate whether the exception was handled.\nThis extra `yield` is _optional_ and if it is omitted the `__exit__` result\nwill be `False` indicating that an exception was not handled.\n\nHere is a sketch of a method which can handle a `SomeException` specially:\n\n    class CMgr(ContextManagerMixin):\n        def __enter_exit__(self):\n            ... do some setup here ...\n            # Returning self is common, but might be any relevant value.\n            # Note that if you want `self`, you can just use a bare yield\n            # and ContextManagerMixin will provide `self` as the default.\n            enter_result = self\n            exit_result = False\n            try:\n                yield enter_result\n            except SomeException as e:\n                ... handle e ...\n                exit_result = True\n            finally:\n                ... do tear down here ...\n            yield exit_result\n\n*Method `ContextManagerMixin.__enter__(self)`*:\nRun `super().__enter__` (if any)\nthen the `__enter__` phase of `self.__enter_exit__()`.\n\n*Method `ContextManagerMixin.__exit__(self, exc_type, exc_value, traceback)`*:\nRun the `__exit__` step of `self.__enter_exit__()`,\nthen `super().__exit__` (if any).\n\n*Method `ContextManagerMixin.as_contextmanager(self)`*:\nRun the generator from the `cls` class specific `__enter_exit__`\nmethod via `self` as a context manager.\n\nExample from `RunState` which subclasses `HasThreadState`,\nboth of which are `ContextManagerMixin` subclasses:\n\n    class RunState(HasThreadState):\n        .....\n        def __enter_exit__(self):\n            with HasThreadState.as_contextmanager(self):\n                ... RunState context manager stuff ...\n\nThis runs the `HasThreadState` context manager\naround the main `RunState` context manager.\n\n## Function `pop_cmgr(o, attr)`\n\nRun the `__exit__` phase of a context manager commenced with `push_cmgr`.\nRestore `attr` as it was before `push_cmgr`.\nReturn the result of `__exit__`.\n\n## Function `popattrs(o, attr_names, old_values)`\n\nThe \"pop\" part of `stackattrs`.\nRestore previous attributes of `o`\nnamed by `attr_names` with previous state in `old_values`.\n\nThis can be useful in hooks/signals/callbacks,\nwhere you cannot inline a context manager.\n\n## Function `popkeys(d, key_names, old_values)`\n\nThe \"pop\" part of `stackkeys`.\nRestore previous key values of `d`\nnamed by `key_names` with previous state in `old_values`.\n\nThis can be useful in hooks/signals/callbacks,\nwhere you cannot inline a context manager.\n\n## Function `push_cmgr(o, attr, cmgr)`\n\nA convenience wrapper for `twostep(cmgr)`\nto run the `__enter__` phase of `cmgr` and save its value as `o.`*attr*`.\nReturn the result of the `__enter__` phase.\n\nThe `__exit__` phase is run by `pop_cmgr(o,attr)`,\nreturning the return value of the exit phase.\n\nExample use in a unit test:\n\n    class TestThing(unittest.TestCase):\n        def setUp(self):\n            # save the temp dir path as self.dirpath\n            push_cmgr(self, 'dirpath', TemporaryDirectory())\n        def tearDown(self):\n            # clean up the temporary directory, discard self.dirpath\n            pop_cmgr(self, 'dirpath')\n\nThe `cs.testutils` `SetupTeardownMixin` class does this\nallowing the provision of a single `setupTeardown()` context manager method\nfor test case setUp/tearDown.\n\nDoc test:\n\n    >>> from os.path import isdir as isdirpath\n    >>> from tempfile import TemporaryDirectory\n    >>> from types import SimpleNamespace\n    >>> obj = SimpleNamespace()\n    >>> dirpath = push_cmgr(obj, 'path', TemporaryDirectory())\n    >>> assert dirpath == obj.path\n    >>> assert isdirpath(dirpath)\n    >>> pop_cmgr(obj, 'path')\n    >>> assert not hasattr(obj, 'path')\n    >>> assert not isdirpath(dirpath)\n\n## Function `pushattrs(o, **attr_values)`\n\nThe \"push\" part of `stackattrs`.\nPush `attr_values` onto `o` as attributes,\nreturn the previous attribute values in a `dict`.\n\nThis can be useful in hooks/signals/callbacks,\nwhere you cannot inline a context manager.\n\n## Function `pushkeys(d, kv=None, **kw)`\n\nThe \"push\" part of `stackkeys`.\nUse the mapping provided as `kv` or `kw` to update `d`.\nReturn the previous key values in a `dict`.\n\nThis can be useful in hooks/signals/callbacks,\nwhere you cannot inline a context manager using `stackkeys`.\n\n## Function `reconfigure_file(f, **kw)`\n\nContext manager flavour of `TextIOBase.reconfigure`.\n\n## Function `setup_cmgr(cmgr)`\n\nRun the set up phase of the context manager `cmgr`\nand return a callable which runs the tear down phase.\n\nThis is a convenience wrapper for the lower level `twostep()` function\nwhich produces a two iteration generator from a context manager.\n\nPlease see the `push_cmgr` function, a superior wrapper for `twostep()`.\n\n*Note*:\nthis function expects `cmgr` to be an existing context manager.\nIn particular, if you define some context manager function like this:\n\n    @contextmanager\n    def my_cmgr_func(...):\n        ...\n        yield\n        ...\n\nthen the correct use of `setup_cmgr()` is:\n\n    teardown = setup_cmgr(my_cmgr_func(...))\n\nand _not_:\n\n    cmgr_iter = setup_cmgr(my_cmgr_func)\n    ...\n\nThe purpose of `setup_cmgr()` is to split any context manager's operation\nacross two steps when the set up and teardown phases must operate\nin different parts of your code.\nA common situation is the `__enter__` and `__exit__` methods\nof another context manager class.\n\nThe call to `setup_cmgr()` performs the \"enter\" phase\nand returns the tear down callable.\nCalling that performs the tear down phase.\n\nExample use in a class:\n\n    class SomeClass:\n        def __init__(self, foo)\n            self.foo = foo\n            self._teardown = None\n        def __enter__(self):\n            self._teardown = setup_cmgr(stackattrs(o, setting=foo))\n        def __exit__(self, *_):\n            teardown, self._teardown = self._teardown, None\n            teardown()\n\n## Function `stack_signals(signums, handler, additional=False)`\n\nContext manager to apply a handler function to `signums`\nusing `signal.signal`.\nThe old handlers are restored on exit from the context manager.\n\nIf the optional `additional` argument is true,\napply a handler which calls both the new handler and the old handler.\n\n## Function `stackattrs(o, **attr_values)`\n\nContext manager to push new values for the attributes of `o`\nand to restore them afterward.\nReturns a `dict` containing a mapping of the previous attribute values.\nAttributes not present are not present in the returned mapping.\n\nRestoration includes deleting attributes which were not present\ninitially.\n\nThis makes it easy to adjust temporarily some shared context object\nwithout having to pass it through the call stack.\n\nSee `stackkeys` for a flavour of this for mappings.\n\nSee `cs.threads.ThreadState` for a convenient wrapper class.\n\nExample of fiddling a programme's \"verbose\" mode:\n\n    >>> class RunModes:\n    ...     def __init__(self, verbose=False):\n    ...         self.verbose = verbose\n    ...\n    >>> runmode = RunModes()\n    >>> if runmode.verbose:\n    ...     print(\"suppressed message\")\n    ...\n    >>> with stackattrs(runmode, verbose=True):\n    ...     if runmode.verbose:\n    ...         print(\"revealed message\")\n    ...\n    revealed message\n    >>> if runmode.verbose:\n    ...     print(\"another suppressed message\")\n    ...\n\nExample exhibiting restoration of absent attributes:\n\n    >>> class O:\n    ...     def __init__(self):\n    ...         self.a = 1\n    ...\n    >>> o = O()\n    >>> print(o.a)\n    1\n    >>> print(o.b)\n    Traceback (most recent call last):\n      File \"<stdin>\", line 1, in <module>\n    AttributeError: 'O' object has no attribute 'b'\n    >>> with stackattrs(o, a=3, b=4):\n    ...     print(o.a)\n    ...     print(o.b)\n    ...     o.b = 5\n    ...     print(o.b)\n    ...     delattr(o, 'a')\n    ...\n    3\n    4\n    5\n    >>> print(o.a)\n    1\n    >>> print(o.b)\n    Traceback (most recent call last):\n      File \"<stdin>\", line 1, in <module>\n    AttributeError: 'O' object has no attribute 'b'\n\n## Function `stackkeys(d, kv=None, **kw)`\n\nA context manager to push new values for the key values of `d`\nand to restore them afterward.\nThe new values are provided as `kv` or `kw` as convenient.\nReturns a `dict` containing a mapping of the previous key values.\nKeys not present are not present in the mapping.\n\nRestoration includes deleting key values which were not present\ninitially.\n\nThis makes it easy to adjust temporarily some shared context object\nwithout having to pass it through the call stack.\n\nSee `stackattrs` for a flavour of this for object attributes.\n\nExample of making log entries which may reference\nsome higher level context log entry:\n\n    >>> import time\n    >>> global_context = {\n    ...     'parent': None,\n    ... }\n    >>> def log_entry(desc, **kw):\n    ...     print(\"log_entry: global_context =\", repr(global_context))\n    ...     entry = dict(global_context)\n    ...     entry.update(desc=desc, when=time.time())\n    ...     entry.update(kw)\n    ...     return entry\n    ...\n    >>> log_entry(\"stand alone entry\")    #doctest: +ELLIPSIS\n    log_entry: global_context = {'parent': None}\n    {'parent': None, 'desc': 'stand alone entry', 'when': ...}\n    >>> context_entry = log_entry(\"high level entry\")\n    log_entry: global_context = {'parent': None}\n    >>> context_entry                     #doctest: +ELLIPSIS\n    {'parent': None, 'desc': 'high level entry', 'when': ...}\n    >>> with stackkeys(global_context, parent=context_entry): #doctest: +ELLIPSIS\n    ...     print(repr(log_entry(\"low level event\")))\n    ...\n    log_entry: global_context = {'parent': {'parent': None, 'desc': 'high level entry', 'when': ...}}\n    {'parent': {'parent': None, 'desc': 'high level entry', 'when': ...}, 'desc': 'low level event', 'when': ...}\n    >>> log_entry(\"another standalone entry\")    #doctest: +ELLIPSIS\n    log_entry: global_context = {'parent': None}\n    {'parent': None, 'desc': 'another standalone entry', 'when': ...}\n\n## Function `stackset(s, element, lock=None)`\n\nContext manager to add `element` to the set `s` and remove it on return.\nThe element is neither added nor removed if it is already present.\n\n## Function `twostep(cmgr)`\n\nReturn a generator which operates the context manager `cmgr`.\n\nThe first iteration performs the \"enter\" phase and yields the result.\nThe second iteration performs the \"exit\" phase and yields `None`.\n\nSee also the `push_cmgr(obj,attr,cmgr)` function\nand its partner `pop_cmgr(obj,attr)`\nwhich form a convenient wrapper for this low level generator.\n\nThe purpose of `twostep()` is to split any context manager's operation\nacross two steps when the set up and tear down phases must operate\nin different parts of your code.\nA common situation is the `__enter__` and `__exit__` methods\nof another context manager class\nor the `setUp` and `tearDown` methods of a unit test case.\n\n*Note*:\nthis function expects `cmgr` to be an existing context manager\nand _not_ the function which returns the context manager.\n\nIn particular, if you define some function like this:\n\n    @contextmanager\n    def my_cmgr_func(...):\n        ...\n        yield\n        ...\n\nthen `my_cmgr_func(...)` returns a context manager instance\nand so the correct use of `twostep()` is like this:\n\n    # steps broken out for clarity\n    cmgr = my_cmgr_func(...)\n    cmgr_iter = twostep(cmgr)\n    next(cmgr_iter)   # set up\n    next(cmgr_iter)   # tear down\n\nand _not_:\n\n    cmgr_iter = twostep(my_cmgr_func)\n    next(cmgr_iter)   # set up\n    next(cmgr_iter)   # tear down\n\nExample use in a class (but really you should use\n`push_cmgr`/`pop_cmgr` instead):\n\n    class SomeClass:\n        def __init__(self, foo)\n            self.foo = foo\n            self._cmgr_ = None\n        def __enter__(self):\n            self._cmgr_stepped = twostep(stackattrs(o, setting=foo))\n            self._cmgr = next(self._cmgr_stepped)\n            return self._cmgr\n        def __exit__(self, *_):\n            next(self._cmgr_stepped)\n            self._cmgr = None\n\n## Function `withall(objs)`\n\nEnter every object `obj` in `obj_list` except those which are `None`\nusing `with obj:`, then yield.\n\n## Function `withif(obj)`\n\nReturn a context manager for `obj`.\nIf `obj` has an `__enter__` attribute, return `obj`\notherwise return `nullcontext()`.\n\nExample:\n\n    with withif(inner_mapping):\n      ... work with inner_mapping ...\n\n# Release Log\n\n\n\n*Release 20240412*:\n* contextif: rework to be much easier to use, add new call modes.\n* pushkeys, stackkeys: support update dicts whose keys are not identifier strings i.e. a non **kw call mode.\n* New withif() function returning a context manager even for objects which do not provide one.\n* New withall(iterable-of-context-managers) context manager.\n\n*Release 20240316*:\nFixed release upload artifacts.\n\n*Release 20240212.1*:\nMinor doc updates.\n\n*Release 20240212*:\nNew reconfigure_file(f,**kw), a context manager flavour of `TextIOBase.reconfigure`.\n\n*Release 20240201*:\ncontextif: require the flag to be a bool.\n\n*Release 20230331*:\nstackset: accept optional lock to guard modification of the set.\n\n*Release 20230212*:\n* BREAKING: drop StackableState, superceded by cs.threads.State.\n* New stackset(set,element) to push and then pop an element to a set unless it is already there.\n\n*Release 20230125*:\nNew ContextManagerMixin.as_contextmanager(cls,self) class method to run the __enter_exit__ from a specific class, useful in subclasses.\n\n*Release 20230109*:\nNew contextif(flag,cmgr_func,...) context manager to use cmgr_func if flag is true otherwise nullcontext.\n\n*Release 20221118*:\nstackattrs: improve docstring.\n\n*Release 20220619*:\ntwostep: the returned \"tear down\" phase function needs to ignore StopIteration from the context manager, see PEP 479.\n\n*Release 20220227*:\nNew stack_signals context manager to push signal handlers.\n\n*Release 20211115.1*:\nDocstring grammar/phrasing updates.\n\n*Release 20211115*:\nRename `enter_exit` to `__enter_exit__` - the user doesn't call this overtly and it aligns better with `__enter__` and `__exit__`.\n\n*Release 20211114.1*:\nContextManagerMixin: the default __enter__ return is self, supporting a trivial bare `yield` in the generator.\n\n*Release 20211114*:\nNew ContextManagerMixin mixin class to implement the __enter__/__exit__ methods using a simple generator function named enter_exit.\n\n*Release 20210727*:\n* twostep: iteration 1 now returns the result of __enter__, iteration 2 now returns None.\n* New functions push_cmgr(obj,attr,cmgr) and partner pop_cmgr(obj,attr) to run a twostep()ed context manager conveniently, more conveniently than setup_cmgr().\n\n*Release 20210420.1*:\nRerelease after completing stalled merge: docstring updates.\n\n*Release 20210420*:\nDocstring corrections and improvements.\n\n*Release 20210306*:\n* New twostep() and setup_cmgr() functions to split a context manager into set up and teardown phases for when these must occur in different parts of the code.\n* New thread local StackableState class which can be called to push attribute changes with stackattrs - intended for use as shared global state to avoiod passing through deep function call chains.\n\n*Release 20200725.1*:\nDocstring improvements.\n\n*Release 20200725*:\nNew stackkeys and components pushkeys and popkeys doing \"stackattrs for dicts/mappings\".\n\n*Release 20200517*:\n* Add `nullcontext` like the one from recent contextlib.\n* stackattrs: expose the push and pop parts as pushattrs() and popattrs().\n\n*Release 20200228.1*:\nInitial release with stackattrs context manager.\n\n",
    "bugtrack_url": null,
    "license": "GNU General Public License v3 or later (GPLv3+)",
    "summary": "Assorted context managers.",
    "version": "20240412",
    "project_urls": {
        "URL": "https://bitbucket.org/cameron_simpson/css/commits/all"
    },
    "split_keywords": [
        "python2",
        " python3"
    ],
    "urls": [
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "d795b36e2f142f63e0a3c959d7de342fa09a937f5d73721642a20580599c63b1",
                "md5": "ee7692a894ef1bd054d130f57a531680",
                "sha256": "7f7ccc2f6bba332e0c05e39497564807d2c460095103c0fa898a6eac209841e9"
            },
            "downloads": -1,
            "filename": "cs.context-20240412-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "ee7692a894ef1bd054d130f57a531680",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": null,
            "size": 13383,
            "upload_time": "2024-04-12T02:33:04",
            "upload_time_iso_8601": "2024-04-12T02:33:04.819493Z",
            "url": "https://files.pythonhosted.org/packages/d7/95/b36e2f142f63e0a3c959d7de342fa09a937f5d73721642a20580599c63b1/cs.context-20240412-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": "",
            "digests": {
                "blake2b_256": "0a420722b3b34cb5d455acc6d5ba81b9b02fd260240f25a6cad61aa2d7b24a87",
                "md5": "35fc042133ef5462b2f6a7aa5a6f1d57",
                "sha256": "e9a2edbf6c76adba4b1e00686b1a2ff4e2d29fabef6fca73ac74d021f4bbcc2d"
            },
            "downloads": -1,
            "filename": "cs.context-20240412.tar.gz",
            "has_sig": false,
            "md5_digest": "35fc042133ef5462b2f6a7aa5a6f1d57",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 14544,
            "upload_time": "2024-04-12T02:33:06",
            "upload_time_iso_8601": "2024-04-12T02:33:06.917384Z",
            "url": "https://files.pythonhosted.org/packages/0a/42/0722b3b34cb5d455acc6d5ba81b9b02fd260240f25a6cad61aa2d7b24a87/cs.context-20240412.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2024-04-12 02:33:06",
    "github": false,
    "gitlab": false,
    "bitbucket": true,
    "codeberg": false,
    "bitbucket_user": "cameron_simpson",
    "bitbucket_project": "css",
    "lcname": "cs.context"
}
        
Elapsed time: 0.24346s