LayeredContext¶
-
class
privex.helpers.common.LayeredContext(wrapped_class: K, max_layers: Optional[int] = 1, fail: bool = False)[source]¶ A wrapper class for context manager classes / functions which allows you to control how many
withlayers that a context manager can have - and allow for the previous layer’s context manager__enter__/yieldresult to be passed down whenmax_layersis hit.(context managers are classes/functions with the methods
__enter__/__exit__/__aenter__/__aexit__etc.)Works with context manager classes, asyncio context manager classes, and
contextlib.contextmanager()functions.By default,
LayeredContextsetsmax_layersto1, meaning after 1 layer ofwithorasync withstatements, all additional layers will simply get given the same context result as the 1st layer, plus both__enter__and__exit__will only be called once (at the start and end of the first layer).Using with class-based context managers:
>>> class Hello: ... def __enter__(self): ... print('entering Hello') ... return self ... def __exit__(self, exc_type, exc_val, exc_tb): ... print('exiting Hello') >>> ctx_a = LayeredContext(Hello()) >>> with ctx_a as a: ... print('class manager layer 1') ... with ctx_a as b: ... print('class manager layer 2') ... print('back to class layer 1') entering Hello class manager layer 1 class manager layer 2 back to class layer 1 exiting Hello
We can see that
entering Helloandexiting Hellowere only outputted at the end of the first context blockwith ctx_a as a, showing thatHellowas only entered/exited as a context manager for the firstwithblock.Using with function-based :func:`contextlib.contextmanager` context managers:
>>> from contextlib import contextmanager >>> @contextmanager >>> def lorem(): ... print('entering lorem contextmanager') ... yield 'hello world' ... print('exiting lorem contextmanager') >>> ctx_b = LayeredContext(lorem()) >>> with ctx_b as c: ... print('function manager layer 1 - context is:', c) ... with ctx_b as d: ... print('function manager layer 2 - context is:', d) ... print('back to function layer 1') entering lorem contextmanager function manager layer 1 - context is: hello world function manager layer 2 - context is: hello world back to function layer 1 exiting lorem contextmanager
We can see the default
max_layersof1was respected, as the 2nd layerwith ctx_b as donly printedfunction manager layer 2(thuslorem’s enter/exit methods were not called), and it shows the context is stillhello world(the context yielded byloremin layer 1).Example usage
First we need an example class which can be used as a context manager, so we create
Examplewith a very simple__enter__and__exit__method, which simply adds and subtracts fromself.ctx_layerrespectively:>>> class Example: ... def __init__(self): ... self.ctx_layer = 0 ... def __enter__(self): ... self.ctx_layer += 1 ... return self ... def __exit__(self, exc_type, exc_val, exc_tb): ... if self.ctx_layer <= 0: raise ValueError('ctx_layer <= 0 !!!') ... self.ctx_layer -= 1 ... return None
If we then create an instance of
Example, and use it as a context manager in a 2 layer nestedwith exp, we can seectx_layergets increased each time we use it as a context manager, and decreases after the context manager block:>>> exp = Example() >>> with exp as x: ... print(x.ctx_layer) # prints: 1 ... with exp as y: ... print(y.ctx_layer) # prints: 2 ... print(x.ctx_layer) # prints: 1 >>> exp.ctx_layer 0
Now, lets wrap it with
LayeredContext, and set the maximum amount of layers to1. If we start usingctxas a context manager, it works as if we used the example instanceexpas a context manager. But, unlike the real instance,__enter__is only really called for the firstwithblock, and__exit__is only really called once we finish the first layerwith ctx as x>>> ctx = LayeredContext(exp, max_layers=1) >>> with ctx as x: ... print(x.ctx_layer) # prints: 1 ... with ctx as y: ... print(y.ctx_layer) # prints: 1 ... print(ctx.virtual_layer) # prints: 2 ... print(x.ctx_layer) # prints: 1 ... print(ctx.virtual_layer) # prints: 1 >>> exp.ctx_layer 0 >>> print(ctx.layer, ctx.virtual_layer) 0 0
-
__init__(wrapped_class: K, max_layers: Optional[int] = 1, fail: bool = False)[source] Construct a
LayeredContextinstance, wrapping the context manager class instance or func:contextlib.contextmanager manager functionwrapped_class.- Parameters
wrapped_class (K|object) – A context manager class or
contextlib.contextmanager()manager function to wrapmax_layers (int) – Maximum layers of
(async) withblocks before silently consuming further attempts to enter/exit the context manager forwrapped_classfail (bool) – (default:
False) WhenTrue, will raiseNestedContextExceptionwhen anenter()call is going to cause more thanmax_layerscontext manager layers to be active.
-