Implements thread-local stack using ContextVar
(:pep:567
).
This is a reimplementation of the local stack as used by Flask, Werkzeug,
Celery, and other libraries to keep a thread-local stack of objects.
-
Supports typing:
request_stack: LocalStack[Request] = LocalStack()
LocalStack
Bases: Generic[T]
LocalStack.
Manage state per coroutine (also thread safe).
Most famously used probably in Flask to keep track of the current
request object.
Source code in mode/utils/locals.py
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100 | class LocalStack(Generic[T]):
"""LocalStack.
Manage state per coroutine (also thread safe).
Most famously used probably in Flask to keep track of the current
request object.
"""
_stack: ContextVar[Optional[List[T]]]
def __init__(self) -> None:
self._stack = ContextVar("_stack")
# XXX mypy bug; when fixed type Generator, should be ContextManager.
@contextmanager
def push(self, obj: T) -> Generator[None, None, None]:
"""Push a new item to the stack."""
self.push_without_automatic_cleanup(obj)
try:
yield
finally:
self.pop()
def push_without_automatic_cleanup(self, obj: T) -> None:
stack = self._stack.get(None)
if stack is None:
stack = []
self._stack.set(stack)
stack.append(obj)
def pop(self) -> Optional[T]:
"""Remove the topmost item from the stack.
Note:
Will return the old value or `None` if the stack was already empty.
"""
stack = self._stack.get(None)
if stack is None:
return None
else:
size = len(stack)
if not size:
self._stack.set(None)
return None
elif size == 1:
item = stack[-1]
self._stack.set(None)
else:
item = stack.pop()
return item
def __len__(self) -> int:
stack = self._stack.get(None)
return len(stack) if stack else 0
@property
def stack(self) -> Sequence[T]:
# read-only version, do not modify
return self._stack.get(None) or []
@property
def top(self) -> Optional[T]:
"""Return the topmost item on the stack.
Does not remove it from the stack.
Note:
If the stack is empty, :const:`None` is returned.
"""
stack = self._stack.get(None)
return stack[-1] if stack else None
|
top: Optional[T]
property
Return the topmost item on the stack.
Does not remove it from the stack.
Note
If the stack is empty, :const:None
is returned.
pop()
Remove the topmost item from the stack.
Note
Will return the old value or None
if the stack was already empty.
Source code in mode/utils/locals.py
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79 | def pop(self) -> Optional[T]:
"""Remove the topmost item from the stack.
Note:
Will return the old value or `None` if the stack was already empty.
"""
stack = self._stack.get(None)
if stack is None:
return None
else:
size = len(stack)
if not size:
self._stack.set(None)
return None
elif size == 1:
item = stack[-1]
self._stack.set(None)
else:
item = stack.pop()
return item
|
push(obj)
Push a new item to the stack.
Source code in mode/utils/locals.py
| @contextmanager
def push(self, obj: T) -> Generator[None, None, None]:
"""Push a new item to the stack."""
self.push_without_automatic_cleanup(obj)
try:
yield
finally:
self.pop()
|