mit neuen venv und exe-Files
This commit is contained in:
348
venv3_12/Lib/site-packages/gevent/contextvars.py
Normal file
348
venv3_12/Lib/site-packages/gevent/contextvars.py
Normal file
@@ -0,0 +1,348 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Cooperative ``contextvars`` module.
|
||||
|
||||
This module was added to Python 3.7. The gevent version is available
|
||||
on all supported versions of Python. However, see an important note
|
||||
about gevent 20.9.
|
||||
|
||||
Context variables are like greenlet-local variables, just more
|
||||
inconvenient to use. They were designed to work around limitations in
|
||||
:mod:`asyncio` and are rarely needed by greenlet-based code.
|
||||
|
||||
The primary difference is that snapshots of the state of all context
|
||||
variables in a given greenlet can be taken, and later restored for
|
||||
execution; modifications to context variables are "scoped" to the
|
||||
duration that a particular context is active. (This state-restoration
|
||||
support is rarely useful for greenlets because instead of always
|
||||
running "tasks" sequentially within a single thread like `asyncio`
|
||||
does, greenlet-based code usually spawns new greenlets to handle each
|
||||
task.)
|
||||
|
||||
The gevent implementation is based on the Python reference implementation
|
||||
from :pep:`567` and doesn't have much optimization. In particular, setting
|
||||
context values isn't constant time.
|
||||
|
||||
.. versionadded:: 1.5a3
|
||||
.. versionchanged:: 20.9.0
|
||||
On Python 3.7 and above, this module is no longer monkey-patched
|
||||
in place of the standard library version.
|
||||
gevent depends on greenlet 0.4.17 which includes support for context variables.
|
||||
This means that any number of greenlets can be running any number of asyncio tasks
|
||||
each with their own context variables. This module is only greenlet aware, not
|
||||
asyncio task aware, so its use is not recommended on Python 3.7 and above.
|
||||
|
||||
On previous versions of Python, this module continues to be a solution for
|
||||
backporting code. It is also available if you wish to use the contextvar API
|
||||
in a strictly greenlet-local manner.
|
||||
"""
|
||||
from __future__ import absolute_import
|
||||
from __future__ import division
|
||||
from __future__ import print_function
|
||||
|
||||
|
||||
__all__ = [
|
||||
'ContextVar',
|
||||
'Context',
|
||||
'copy_context',
|
||||
'Token',
|
||||
]
|
||||
|
||||
try:
|
||||
from collections.abc import Mapping
|
||||
except ImportError:
|
||||
from collections import Mapping # pylint:disable=deprecated-class
|
||||
|
||||
|
||||
from gevent._util import _NONE
|
||||
from gevent.local import local
|
||||
|
||||
__stdlib_expected__ = __all__
|
||||
__implements__ = __stdlib_expected__
|
||||
|
||||
# In the reference implementation, the interpreter level OS thread state
|
||||
# is modified to contain a pointer to the current context. Obviously we can't
|
||||
# touch that here because we're not tied to CPython's internals; plus, of course,
|
||||
# we want to operate with greenlets, not OS threads. So we use a greenlet-local object
|
||||
# to store the active context.
|
||||
class _ContextState(local):
|
||||
|
||||
def __init__(self):
|
||||
self.context = Context()
|
||||
|
||||
|
||||
def _not_base_type(cls):
|
||||
# This is not given in the PEP but is tested in test_context.
|
||||
# Assign this method to __init_subclass__ in each type that can't
|
||||
# be subclassed. (This only works in 3.6+, but context vars are only in
|
||||
# 3.7+)
|
||||
raise TypeError("not an acceptable base type")
|
||||
|
||||
class _ContextData(object):
|
||||
"""
|
||||
A copy-on-write immutable mapping from ContextVar
|
||||
keys to arbitrary values. Setting values requires a
|
||||
copy, making it O(n), not O(1).
|
||||
"""
|
||||
|
||||
# In theory, the HAMT used by the stdlib contextvars module could
|
||||
# be used: It's often available at _testcapi.hamt() (see
|
||||
# test_context). We'd need to be sure to add a correct __hash__
|
||||
# method to ContextVar to make that work well. (See
|
||||
# Python/context.c:contextvar_generate_hash.)
|
||||
|
||||
__slots__ = (
|
||||
'_mapping',
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
self._mapping = {}
|
||||
|
||||
def __getitem__(self, key):
|
||||
return self._mapping[key]
|
||||
|
||||
def __contains__(self, key):
|
||||
return key in self._mapping
|
||||
|
||||
def __len__(self):
|
||||
return len(self._mapping)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._mapping)
|
||||
|
||||
def set(self, key, value):
|
||||
copy = _ContextData()
|
||||
copy._mapping = self._mapping.copy()
|
||||
copy._mapping[key] = value
|
||||
return copy
|
||||
|
||||
def delete(self, key):
|
||||
copy = _ContextData()
|
||||
copy._mapping = self._mapping.copy()
|
||||
del copy._mapping[key]
|
||||
return copy
|
||||
|
||||
|
||||
class ContextVar(object):
|
||||
"""
|
||||
Implementation of :class:`contextvars.ContextVar`.
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
'_name',
|
||||
'_default',
|
||||
)
|
||||
|
||||
def __init__(self, name, default=_NONE):
|
||||
self._name = name
|
||||
self._default = default
|
||||
|
||||
__init_subclass__ = classmethod(_not_base_type)
|
||||
|
||||
@classmethod
|
||||
def __class_getitem__(cls, _):
|
||||
# For typing support: ContextVar[str].
|
||||
# Not in the PEP.
|
||||
# sigh.
|
||||
return cls
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
def get(self, default=_NONE):
|
||||
context = _context_state.context
|
||||
try:
|
||||
return context[self]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if default is not _NONE:
|
||||
return default
|
||||
|
||||
if self._default is not _NONE:
|
||||
return self._default
|
||||
|
||||
raise LookupError
|
||||
|
||||
def set(self, value):
|
||||
context = _context_state.context
|
||||
return context._set_value(self, value)
|
||||
|
||||
def reset(self, token):
|
||||
token._reset(self)
|
||||
|
||||
def __repr__(self):
|
||||
# This is not captured in the PEP but is tested by test_context
|
||||
return '<%s.%s name=%r default=%r at 0x%x>' % (
|
||||
type(self).__module__,
|
||||
type(self).__name__,
|
||||
self._name,
|
||||
self._default,
|
||||
id(self)
|
||||
)
|
||||
|
||||
|
||||
class Token(object):
|
||||
"""
|
||||
Opaque implementation of :class:`contextvars.Token`.
|
||||
"""
|
||||
|
||||
MISSING = _NONE
|
||||
|
||||
__slots__ = (
|
||||
'_context',
|
||||
'_var',
|
||||
'_old_value',
|
||||
'_used',
|
||||
)
|
||||
|
||||
def __init__(self, context, var, old_value):
|
||||
self._context = context
|
||||
self._var = var
|
||||
self._old_value = old_value
|
||||
self._used = False
|
||||
|
||||
__init_subclass__ = classmethod(_not_base_type)
|
||||
|
||||
@property
|
||||
def var(self):
|
||||
"""
|
||||
A read-only attribute pointing to the variable that created the token
|
||||
"""
|
||||
return self._var
|
||||
|
||||
@property
|
||||
def old_value(self):
|
||||
"""
|
||||
A read-only attribute set to the value the variable had before
|
||||
the ``set()`` call, or to :attr:`MISSING` if the variable wasn't set
|
||||
before.
|
||||
"""
|
||||
return self._old_value
|
||||
|
||||
def _reset(self, var):
|
||||
if self._used:
|
||||
raise RuntimeError("Taken has already been used once")
|
||||
|
||||
if self._var is not var:
|
||||
raise ValueError("Token was created by a different ContextVar")
|
||||
|
||||
if self._context is not _context_state.context:
|
||||
raise ValueError("Token was created in a different Context")
|
||||
|
||||
self._used = True
|
||||
if self._old_value is self.MISSING:
|
||||
self._context._delete(var)
|
||||
else:
|
||||
self._context._reset_value(var, self._old_value)
|
||||
|
||||
def __repr__(self):
|
||||
# This is not captured in the PEP but is tested by test_context
|
||||
return '<%s.%s%s var=%r at 0x%x>' % (
|
||||
type(self).__module__,
|
||||
type(self).__name__,
|
||||
' used' if self._used else '',
|
||||
self._var,
|
||||
id(self),
|
||||
)
|
||||
|
||||
class Context(Mapping):
|
||||
"""
|
||||
Implementation of :class:`contextvars.Context`
|
||||
"""
|
||||
|
||||
__slots__ = (
|
||||
'_data',
|
||||
'_prev_context',
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
"""
|
||||
Creates an empty context.
|
||||
"""
|
||||
self._data = _ContextData()
|
||||
self._prev_context = None
|
||||
|
||||
__init_subclass__ = classmethod(_not_base_type)
|
||||
|
||||
def run(self, function, *args, **kwargs):
|
||||
if self._prev_context is not None:
|
||||
raise RuntimeError(
|
||||
"Cannot enter context; %s is already entered" % (self,)
|
||||
)
|
||||
|
||||
self._prev_context = _context_state.context
|
||||
try:
|
||||
_context_state.context = self
|
||||
return function(*args, **kwargs)
|
||||
finally:
|
||||
_context_state.context = self._prev_context
|
||||
self._prev_context = None
|
||||
|
||||
def copy(self):
|
||||
"""
|
||||
Return a shallow copy.
|
||||
"""
|
||||
result = Context()
|
||||
result._data = self._data
|
||||
return result
|
||||
|
||||
###
|
||||
# Operations used by ContextVar and Token
|
||||
###
|
||||
|
||||
def _set_value(self, var, value):
|
||||
try:
|
||||
old_value = self._data[var]
|
||||
except KeyError:
|
||||
old_value = Token.MISSING
|
||||
|
||||
self._data = self._data.set(var, value)
|
||||
return Token(self, var, old_value)
|
||||
|
||||
def _delete(self, var):
|
||||
self._data = self._data.delete(var)
|
||||
|
||||
def _reset_value(self, var, old_value):
|
||||
self._data = self._data.set(var, old_value)
|
||||
|
||||
# Note that all Mapping methods, including Context.__getitem__ and
|
||||
# Context.get, ignore default values for context variables (i.e.
|
||||
# ContextVar.default). This means that for a variable var that was
|
||||
# created with a default value and was not set in the context:
|
||||
#
|
||||
# - context[var] raises a KeyError,
|
||||
# - var in context returns False,
|
||||
# - the variable isn't included in context.items(), etc.
|
||||
|
||||
# Checking the type of key isn't part of the PEP but is tested by
|
||||
# test_context.py.
|
||||
@staticmethod
|
||||
def __check_key(key):
|
||||
if type(key) is not ContextVar: # pylint:disable=unidiomatic-typecheck
|
||||
raise TypeError("ContextVar key was expected")
|
||||
|
||||
def __getitem__(self, key):
|
||||
self.__check_key(key)
|
||||
return self._data[key]
|
||||
|
||||
def __contains__(self, key):
|
||||
self.__check_key(key)
|
||||
return key in self._data
|
||||
|
||||
def __len__(self):
|
||||
return len(self._data)
|
||||
|
||||
def __iter__(self):
|
||||
return iter(self._data)
|
||||
|
||||
|
||||
def copy_context():
|
||||
"""
|
||||
Return a shallow copy of the current context.
|
||||
"""
|
||||
return _context_state.context.copy()
|
||||
|
||||
|
||||
_context_state = _ContextState()
|
||||
Reference in New Issue
Block a user