Skip to content

Commit dc4ee5e

Browse files
committed
-
1 parent e01e036 commit dc4ee5e

File tree

10 files changed

+130
-137
lines changed

10 files changed

+130
-137
lines changed

source_py3/python_toolbox/context_management/__init__.py

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,5 @@ def do_stuff():
131131
from .self_hook import SelfHook
132132

133133
from .blank_context_manager import BlankContextManager
134-
from .reentrant_context_manager import ReentrantContextManager
135134
from .delegating_context_manager import DelegatingContextManager
136-
from .functions import nested, idempotentify
135+
from .functions import nested, as_idempotent, as_reentrant

source_py3/python_toolbox/context_management/functions.py

Lines changed: 80 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99

1010
import sys
1111

12+
from python_toolbox import caching
13+
1214
from .context_manager_type import ContextManagerType
1315
from .context_manager import ContextManager
1416

@@ -42,8 +44,9 @@ def nested(*managers):
4244
# have been raised and caught by an exit method
4345
raise exc[1].with_traceback(exc[2])
4446

45-
46-
def idempotentify(context_manager):
47+
48+
49+
def as_idempotent(context_manager):
4750
'''
4851
Wrap a context manager so repeated calls to enter and exit will be ignored.
4952
@@ -59,33 +62,84 @@ def idempotentify(context_manager):
5962
`ExitStack`, but you also possibly want to exit it manually before the
6063
`ExitStack` closes. This way you don't risk an exception by having the
6164
context manager exit twice.
65+
66+
Note: The value returned by `reentrant_enter` will be returned by all the
67+
no-op `__enter__` actions contained in the outermost suite.
68+
'''
69+
return _IdempotentContextManager(context_manager)
70+
71+
72+
def as_reentrant(context_manager):
6273
'''
63-
class _IdempotentContextManager:
64-
# Not inheriting from `ContextManager` because this class is internal
65-
# and not used beyond `idempotentify`, so no need for the extra
66-
# baggage.
67-
_entered = False
68-
_enter_value = None
74+
Wrap a context manager to make it reentant.
75+
76+
A context manager wrapped with `as_reentrant` could be entered multiple
77+
times, and only after it's been exited the same number of times that it has
78+
been entered will the original `__exit__` method be called.
79+
80+
Note: The value returned by `reentrant_enter` will be returned by all the
81+
no-op `__enter__` actions contained in the outermost suite.
82+
'''
83+
return _ReentrantContextManager(context_manager)
84+
85+
86+
class _IdempotentContextManager(ContextManager):
87+
_entered = False
88+
_enter_value = None
89+
90+
def __init__(self, wrapped_context_manager):
91+
self.__wrapped__ = wrapped_context_manager
6992

70-
def __init__(self, wrapped_context_manager):
71-
self.__wrapped__ = wrapped_context_manager
93+
94+
def __enter__(self):
95+
if not self._entered:
96+
self._enter_value = self.__wrapped__.__enter__()
97+
self._entered = True
98+
return self._enter_value
7299

73100

74-
def __enter__(self):
75-
if not self._entered:
76-
self._enter_value = self.__wrapped__.__enter__()
77-
self._entered = True
78-
return self._enter_value
79-
101+
def __exit__(self, exc_type=None, exc_value=None, exc_traceback=None):
102+
if self._entered:
103+
exit_value = self.__wrapped__.__exit__(exc_type, exc_value,
104+
exc_traceback)
105+
self._entered = False
106+
self._enter_value = None
107+
return exit_value
108+
109+
class _ReentrantContextManager(ContextManager):
110+
111+
def __init__(self, wrapped_context_manager):
112+
self.__wrapped__ = wrapped_context_manager
113+
114+
depth = caching.CachedProperty(
115+
0,
116+
doc='''
117+
The number of nested suites that entered this context manager.
80118
81-
def __exit__(self, exc_type=None, exc_value=None,
82-
exc_traceback=None):
83-
if self._entered:
84-
exit_value = self.__wrapped__.__exit__(exc_type, exc_value,
85-
exc_traceback)
86-
self._entered = False
87-
return exit_value
88-
89-
return _IdempotentContextManager(context_manager)
119+
When the context manager is completely unused, it's `0`. When
120+
it's first used, it becomes `1`. When its entered again, it
121+
becomes `2`. If it is then exited, it returns to `1`, etc.
122+
'''
123+
)
124+
125+
_enter_value = None
126+
127+
def __enter__(self):
128+
if self.depth == 0:
129+
self._enter_value = self.__wrapped__.__enter__()
130+
self.depth += 1
131+
return self._enter_value
132+
90133

91-
134+
def __exit__(self, exc_type=None, exc_value=None, exc_traceback=None):
135+
assert self.depth >= 1
136+
if self.depth == 1:
137+
exit_value = self.__exit__(exc_type, exc_value, exc_traceback)
138+
self._enter_value = None
139+
else:
140+
exit_value = None
141+
self.depth -= 1
142+
return exit_value
143+
144+
145+

source_py3/python_toolbox/context_management/reentrant_context_manager.py

Lines changed: 0 additions & 70 deletions
This file was deleted.

source_py3/python_toolbox/emitting/emitter_system/emitter_system.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212

1313
from python_toolbox import freezing
1414
from python_toolbox import cute_iter_tools
15-
from python_toolbox.context_management import ReentrantContextManager
1615

1716
from .emitter import Emitter
1817

source_py3/python_toolbox/freezing/delegatee_context_manager.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
from python_toolbox import context_management
55

66

7-
class DelegateeContextManager(context_management.ReentrantContextManager):
7+
class DelegateeContextManager(context_management.ContextManager):
88
'''Inner context manager used internally by `Freezer`.'''
99

1010
def __init__(self, freezer):

source_py3/python_toolbox/freezing/freezer.py

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,15 @@ class Freezer(context_management.DelegatingContextManager):
3131
in the logic of the parent object.
3232
'''
3333

34-
delegatee_context_manager = caching.CachedProperty(DelegateeContextManager)
34+
delegatee_context_manager = caching.CachedProperty(
35+
lambda self: context_management.as_reentrant(DelegateeContextManager)
36+
)
3537
'''The context manager which implements our `__enter__` and `__exit__`.'''
3638

3739

38-
frozen = misc_tools.ProxyProperty('.delegatee_context_manager.depth')
40+
frozen = misc_tools.ProxyProperty(
41+
'.delegatee_context_manager.__wrapped__.depth'
42+
)
3943
'''
4044
An integer specifying the freezer's level of frozenness.
4145

source_py3/test_python_toolbox/test_caching/test_cached_property.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
99
from python_toolbox import misc_tools
1010

1111
from python_toolbox.caching import cache, CachedType, CachedProperty
12+
from python_toolbox.context_management import as_idempotent, ContextManager
13+
14+
get_depth_counting_context_manager = lambda: as_reentrant(ContextManager())
1215

1316

1417
@misc_tools.set_attributes(i=0)
@@ -180,7 +183,7 @@ def test_decorating():
180183

181184
class A:
182185
reentrant_context_manager = CachedProperty(
183-
lambda self: context_management.ReentrantContextManager()
186+
lambda self: get_depth_counting_context_manager()
184187
)
185188

186189
@reentrant_context_manager

source_py3/test_python_toolbox/test_context_management/test_idempotentify.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
import queue as queue_module
55

6-
from python_toolbox.context_management import idempotentify, ContextManager
6+
from python_toolbox.context_management import as_idempotent, ContextManager
77
from python_toolbox import cute_testing
88

99

@@ -20,7 +20,7 @@ def manage_context(self):
2020

2121

2222

23-
def test_idempotentify():
23+
def test_as_idempotent():
2424
some_context_manager = SomeContextManager()
2525
assert some_context_manager.x == 0
2626
with some_context_manager as enter_result:
@@ -58,7 +58,7 @@ def test_idempotentify():
5858

5959

6060
another_context_manager = SomeContextManager()
61-
idempotent_context_manager = idempotentify(another_context_manager)
61+
idempotent_context_manager = as_idempotent(another_context_manager)
6262

6363
assert another_context_manager is idempotent_context_manager.__wrapped__
6464

source_py3/test_python_toolbox/test_context_management/test_nested.py

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,16 +6,18 @@
66
from python_toolbox import freezing
77
from python_toolbox import cute_testing
88

9-
from python_toolbox.context_management import ReentrantContextManager, nested
9+
from python_toolbox.context_management import (ContextManager, nested,
10+
as_reentrant)
1011

12+
get_depth_counting_context_manager = lambda: as_reentrant(ContextManager())
1113

1214

1315
def test_nested():
1416
'''Test the basic workings of `nested`.'''
1517

16-
a = ReentrantContextManager()
17-
b = ReentrantContextManager()
18-
c = ReentrantContextManager()
18+
a = get_depth_counting_context_manager()
19+
b = get_depth_counting_context_manager()
20+
c = get_depth_counting_context_manager()
1921

2022
with nested(a):
2123
assert (a.depth, b.depth, c.depth) == (1, 0, 0)

0 commit comments

Comments
 (0)