mit neuen venv und exe-Files
This commit is contained in:
610
venv3_12/Lib/site-packages/gevent/local.py
Normal file
610
venv3_12/Lib/site-packages/gevent/local.py
Normal file
@@ -0,0 +1,610 @@
|
||||
# cython: auto_pickle=False,embedsignature=True,always_allow_keywords=False
|
||||
"""
|
||||
Greenlet-local objects.
|
||||
|
||||
This module is based on `_threading_local.py`__ from the standard
|
||||
library of Python 3.4.
|
||||
|
||||
__ https://github.com/python/cpython/blob/3.4/Lib/_threading_local.py
|
||||
|
||||
Greenlet-local objects support the management of greenlet-local data.
|
||||
If you have data that you want to be local to a greenlet, simply create
|
||||
a greenlet-local object and use its attributes:
|
||||
|
||||
>>> import gevent
|
||||
>>> from gevent.local import local
|
||||
>>> mydata = local()
|
||||
>>> mydata.number = 42
|
||||
>>> mydata.number
|
||||
42
|
||||
|
||||
You can also access the local-object's dictionary:
|
||||
|
||||
>>> mydata.__dict__
|
||||
{'number': 42}
|
||||
>>> mydata.__dict__.setdefault('widgets', [])
|
||||
[]
|
||||
>>> mydata.widgets
|
||||
[]
|
||||
|
||||
What's important about greenlet-local objects is that their data are
|
||||
local to a greenlet. If we access the data in a different greenlet:
|
||||
|
||||
>>> log = []
|
||||
>>> def f():
|
||||
... items = list(mydata.__dict__.items())
|
||||
... items.sort()
|
||||
... log.append(items)
|
||||
... mydata.number = 11
|
||||
... log.append(mydata.number)
|
||||
>>> greenlet = gevent.spawn(f)
|
||||
>>> greenlet.join()
|
||||
>>> log
|
||||
[[], 11]
|
||||
|
||||
we get different data. Furthermore, changes made in the other greenlet
|
||||
don't affect data seen in this greenlet:
|
||||
|
||||
>>> mydata.number
|
||||
42
|
||||
|
||||
Of course, values you get from a local object, including a __dict__
|
||||
attribute, are for whatever greenlet was current at the time the
|
||||
attribute was read. For that reason, you generally don't want to save
|
||||
these values across greenlets, as they apply only to the greenlet they
|
||||
came from.
|
||||
|
||||
You can create custom local objects by subclassing the local class:
|
||||
|
||||
>>> class MyLocal(local):
|
||||
... number = 2
|
||||
... initialized = False
|
||||
... def __init__(self, **kw):
|
||||
... if self.initialized:
|
||||
... raise SystemError('__init__ called too many times')
|
||||
... self.initialized = True
|
||||
... self.__dict__.update(kw)
|
||||
... def squared(self):
|
||||
... return self.number ** 2
|
||||
|
||||
This can be useful to support default values, methods and
|
||||
initialization. Note that if you define an __init__ method, it will be
|
||||
called each time the local object is used in a separate greenlet. This
|
||||
is necessary to initialize each greenlet's dictionary.
|
||||
|
||||
Now if we create a local object:
|
||||
|
||||
>>> mydata = MyLocal(color='red')
|
||||
|
||||
Now we have a default number:
|
||||
|
||||
>>> mydata.number
|
||||
2
|
||||
|
||||
an initial color:
|
||||
|
||||
>>> mydata.color
|
||||
'red'
|
||||
>>> del mydata.color
|
||||
|
||||
And a method that operates on the data:
|
||||
|
||||
>>> mydata.squared()
|
||||
4
|
||||
|
||||
As before, we can access the data in a separate greenlet:
|
||||
|
||||
>>> log = []
|
||||
>>> greenlet = gevent.spawn(f)
|
||||
>>> greenlet.join()
|
||||
>>> log
|
||||
[[('color', 'red'), ('initialized', True)], 11]
|
||||
|
||||
without affecting this greenlet's data:
|
||||
|
||||
>>> mydata.number
|
||||
2
|
||||
>>> mydata.color
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
AttributeError: 'MyLocal' object has no attribute 'color'
|
||||
|
||||
Note that subclasses can define slots, but they are not greenlet
|
||||
local. They are shared across greenlets::
|
||||
|
||||
>>> class MyLocal(local):
|
||||
... __slots__ = 'number'
|
||||
|
||||
>>> mydata = MyLocal()
|
||||
>>> mydata.number = 42
|
||||
>>> mydata.color = 'red'
|
||||
|
||||
So, the separate greenlet:
|
||||
|
||||
>>> greenlet = gevent.spawn(f)
|
||||
>>> greenlet.join()
|
||||
|
||||
affects what we see:
|
||||
|
||||
>>> mydata.number
|
||||
11
|
||||
|
||||
>>> del mydata
|
||||
|
||||
.. versionchanged:: 1.1a2
|
||||
Update the implementation to match Python 3.4 instead of Python 2.5.
|
||||
This results in locals being eligible for garbage collection as soon
|
||||
as their greenlet exits.
|
||||
|
||||
.. versionchanged:: 1.2.3
|
||||
Use a weak-reference to clear the greenlet link we establish in case
|
||||
the local object dies before the greenlet does.
|
||||
|
||||
.. versionchanged:: 1.3a1
|
||||
Implement the methods for attribute access directly, handling
|
||||
descriptors directly here. This allows removing the use of a lock
|
||||
and facilitates greatly improved performance.
|
||||
|
||||
.. versionchanged:: 1.3a1
|
||||
The ``__init__`` method of subclasses of ``local`` is no longer
|
||||
called with a lock held. CPython does not use such a lock in its
|
||||
native implementation. This could potentially show as a difference
|
||||
if code that uses multiple dependent attributes in ``__slots__``
|
||||
(which are shared across all greenlets) switches during ``__init__``.
|
||||
|
||||
"""
|
||||
from __future__ import print_function
|
||||
|
||||
from copy import copy
|
||||
from weakref import ref
|
||||
|
||||
|
||||
locals()['getcurrent'] = __import__('greenlet').getcurrent
|
||||
locals()['greenlet_init'] = lambda: None
|
||||
|
||||
__all__ = [
|
||||
"local",
|
||||
]
|
||||
|
||||
# The key used in the Thread objects' attribute dicts.
|
||||
# We keep it a string for speed but make it unlikely to clash with
|
||||
# a "real" attribute.
|
||||
key_prefix = '_gevent_local_localimpl_'
|
||||
|
||||
# The overall structure is as follows:
|
||||
# For each local() object:
|
||||
# greenlet.__dict__[key_prefix + str(id(local))]
|
||||
# => _localimpl.dicts[id(greenlet)] => (ref(greenlet), {})
|
||||
|
||||
# That final tuple is actually a localimpl_dict_entry object.
|
||||
|
||||
def all_local_dicts_for_greenlet(greenlet):
|
||||
"""
|
||||
Internal debug helper for getting the local values associated
|
||||
with a greenlet. This is subject to change or removal at any time.
|
||||
|
||||
:return: A list of ((type, id), {}) pairs, where the first element
|
||||
is the type and id of the local object and the second object is its
|
||||
instance dictionary, as seen from this greenlet.
|
||||
|
||||
.. versionadded:: 1.3a2
|
||||
"""
|
||||
|
||||
result = []
|
||||
id_greenlet = id(greenlet)
|
||||
greenlet_dict = greenlet.__dict__
|
||||
for k, v in greenlet_dict.items():
|
||||
if not k.startswith(key_prefix):
|
||||
continue
|
||||
local_impl = v()
|
||||
if local_impl is None:
|
||||
continue
|
||||
entry = local_impl.dicts.get(id_greenlet)
|
||||
if entry is None:
|
||||
# Not yet used in this greenlet.
|
||||
continue
|
||||
assert entry.wrgreenlet() is greenlet
|
||||
result.append((local_impl.localtypeid, entry.localdict))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class _wrefdict(dict):
|
||||
"""A dict that can be weak referenced"""
|
||||
|
||||
class _greenlet_deleted(object):
|
||||
"""
|
||||
A weakref callback for when the greenlet
|
||||
is deleted.
|
||||
|
||||
If the greenlet is a `gevent.greenlet.Greenlet` and
|
||||
supplies ``rawlink``, that will be used instead of a
|
||||
weakref.
|
||||
"""
|
||||
__slots__ = ('idt', 'wrdicts')
|
||||
|
||||
def __init__(self, idt, wrdicts):
|
||||
self.idt = idt
|
||||
self.wrdicts = wrdicts
|
||||
|
||||
def __call__(self, _unused):
|
||||
dicts = self.wrdicts()
|
||||
if dicts:
|
||||
dicts.pop(self.idt, None)
|
||||
|
||||
class _local_deleted(object):
|
||||
__slots__ = ('key', 'wrthread', 'greenlet_deleted')
|
||||
|
||||
def __init__(self, key, wrthread, greenlet_deleted):
|
||||
self.key = key
|
||||
self.wrthread = wrthread
|
||||
self.greenlet_deleted = greenlet_deleted
|
||||
|
||||
def __call__(self, _unused):
|
||||
thread = self.wrthread()
|
||||
if thread is not None:
|
||||
try:
|
||||
unlink = thread.unlink
|
||||
except AttributeError:
|
||||
pass
|
||||
else:
|
||||
unlink(self.greenlet_deleted)
|
||||
del thread.__dict__[self.key]
|
||||
|
||||
class _localimpl(object):
|
||||
"""A class managing thread-local dicts"""
|
||||
__slots__ = ('key', 'dicts',
|
||||
'localargs', 'localkwargs',
|
||||
'localtypeid',
|
||||
'__weakref__',)
|
||||
|
||||
def __init__(self, args, kwargs, local_type, id_local):
|
||||
self.key = key_prefix + str(id(self))
|
||||
# { id(greenlet) -> _localimpl_dict_entry(ref(greenlet), greenlet-local dict) }
|
||||
self.dicts = _wrefdict()
|
||||
self.localargs = args
|
||||
self.localkwargs = kwargs
|
||||
self.localtypeid = local_type, id_local
|
||||
|
||||
# We need to create the thread dict in anticipation of
|
||||
# __init__ being called, to make sure we don't call it
|
||||
# again ourselves. MUST do this before setting any attributes.
|
||||
greenlet = getcurrent() # pylint:disable=undefined-variable
|
||||
_localimpl_create_dict(self, greenlet, id(greenlet))
|
||||
|
||||
class _localimpl_dict_entry(object):
|
||||
"""
|
||||
The object that goes in the ``dicts`` of ``_localimpl``
|
||||
object for each thread.
|
||||
"""
|
||||
# This is a class, not just a tuple, so that cython can optimize
|
||||
# attribute access
|
||||
__slots__ = ('wrgreenlet', 'localdict')
|
||||
|
||||
def __init__(self, wrgreenlet, localdict):
|
||||
self.wrgreenlet = wrgreenlet
|
||||
self.localdict = localdict
|
||||
|
||||
# We use functions instead of methods so that they can be cdef'd in
|
||||
# local.pxd; if they were cdef'd as methods, they would cause
|
||||
# the creation of a pointer and a vtable. This happens
|
||||
# even if we declare the class @cython.final. functions thus save memory overhead
|
||||
# (but not pointer chasing overhead; the vtable isn't used when we declare
|
||||
# the class final).
|
||||
|
||||
|
||||
def _localimpl_create_dict(self, greenlet, id_greenlet):
|
||||
"""Create a new dict for the current thread, and return it."""
|
||||
localdict = {}
|
||||
key = self.key
|
||||
|
||||
wrdicts = ref(self.dicts)
|
||||
|
||||
# When the greenlet is deleted, remove the local dict.
|
||||
# Note that this is suboptimal if the greenlet object gets
|
||||
# caught in a reference loop. We would like to be called
|
||||
# as soon as the OS-level greenlet ends instead.
|
||||
|
||||
# If we are working with a gevent.greenlet.Greenlet, we
|
||||
# can pro-actively clear out with a link, avoiding the
|
||||
# issue described above. Use rawlink to avoid spawning any
|
||||
# more greenlets.
|
||||
greenlet_deleted = _greenlet_deleted(id_greenlet, wrdicts)
|
||||
|
||||
rawlink = getattr(greenlet, 'rawlink', None)
|
||||
if rawlink is not None:
|
||||
rawlink(greenlet_deleted)
|
||||
wrthread = ref(greenlet)
|
||||
else:
|
||||
wrthread = ref(greenlet, greenlet_deleted)
|
||||
|
||||
|
||||
# When the localimpl is deleted, remove the thread attribute.
|
||||
local_deleted = _local_deleted(key, wrthread, greenlet_deleted)
|
||||
|
||||
|
||||
wrlocal = ref(self, local_deleted)
|
||||
greenlet.__dict__[key] = wrlocal
|
||||
|
||||
self.dicts[id_greenlet] = _localimpl_dict_entry(wrthread, localdict)
|
||||
return localdict
|
||||
|
||||
|
||||
_marker = object()
|
||||
|
||||
def _local_get_dict(self):
|
||||
impl = self._local__impl
|
||||
# Cython can optimize dict[], but not dict.get()
|
||||
greenlet = getcurrent() # pylint:disable=undefined-variable
|
||||
idg = id(greenlet)
|
||||
try:
|
||||
entry = impl.dicts[idg]
|
||||
dct = entry.localdict
|
||||
except KeyError:
|
||||
dct = _localimpl_create_dict(impl, greenlet, idg)
|
||||
self.__init__(*impl.localargs, **impl.localkwargs)
|
||||
return dct
|
||||
|
||||
def _init():
|
||||
greenlet_init() # pylint:disable=undefined-variable
|
||||
|
||||
_local_attrs = {
|
||||
'_local__impl',
|
||||
'_local_type_get_descriptors',
|
||||
'_local_type_set_or_del_descriptors',
|
||||
'_local_type_del_descriptors',
|
||||
'_local_type_set_descriptors',
|
||||
'_local_type',
|
||||
'_local_type_vars',
|
||||
'__class__',
|
||||
'__cinit__',
|
||||
}
|
||||
|
||||
class local(object):
|
||||
"""
|
||||
An object whose attributes are greenlet-local.
|
||||
"""
|
||||
__slots__ = tuple(_local_attrs - {'__class__', '__cinit__'})
|
||||
|
||||
def __cinit__(self, *args, **kw): # pylint:disable=bad-dunder-name
|
||||
if args or kw:
|
||||
if type(self).__init__ == object.__init__: # pylint:disable=comparison-with-callable
|
||||
raise TypeError("Initialization arguments are not supported", args, kw)
|
||||
impl = _localimpl(args, kw, type(self), id(self))
|
||||
# pylint:disable=attribute-defined-outside-init
|
||||
self._local__impl = impl
|
||||
get, dels, sets_or_dels, sets = _local_find_descriptors(self)
|
||||
self._local_type_get_descriptors = get
|
||||
self._local_type_set_or_del_descriptors = sets_or_dels
|
||||
self._local_type_del_descriptors = dels
|
||||
self._local_type_set_descriptors = sets
|
||||
self._local_type = type(self)
|
||||
self._local_type_vars = set(dir(self._local_type))
|
||||
|
||||
def __getattribute__(self, name): # pylint:disable=too-many-return-statements
|
||||
if name in _local_attrs:
|
||||
# The _local__impl, __cinit__, etc, won't be hit by the
|
||||
# Cython version, if we've done things right. If we haven't,
|
||||
# they will be, and this will produce an error.
|
||||
return object.__getattribute__(self, name)
|
||||
|
||||
dct = _local_get_dict(self)
|
||||
|
||||
if name == '__dict__':
|
||||
return dct
|
||||
# If there's no possible way we can switch, because this
|
||||
# attribute is *not* found in the class where it might be a
|
||||
# data descriptor (property), and it *is* in the dict
|
||||
# then we don't need to swizzle the dict and take the lock.
|
||||
|
||||
# We don't have to worry about people overriding __getattribute__
|
||||
# because if they did, the dict-swizzling would only last as
|
||||
# long as we were in here anyway.
|
||||
# Similarly, a __getattr__ will still be called by _oga() if needed
|
||||
# if it's not in the dict.
|
||||
|
||||
# Optimization: If we're not subclassed, then
|
||||
# there can be no descriptors except for methods, which will
|
||||
# never need to use __dict__.
|
||||
if self._local_type is local:
|
||||
return dct[name] if name in dct else object.__getattribute__(self, name)
|
||||
|
||||
# NOTE: If this is a descriptor, this will invoke its __get__.
|
||||
# A broken descriptor that doesn't return itself when called with
|
||||
# a None for the instance argument could mess us up here.
|
||||
# But this is faster than a loop over mro() checking each class __dict__
|
||||
# manually.
|
||||
if name in dct:
|
||||
if name not in self._local_type_vars:
|
||||
# If there is a dict value, and nothing in the type,
|
||||
# it can't possibly be a descriptor, so it is just returned.
|
||||
return dct[name]
|
||||
|
||||
# It's in the type *and* in the dict. If the type value is
|
||||
# a data descriptor (defines __get__ *and* either __set__ or
|
||||
# __delete__), then the type wins. If it's a non-data descriptor
|
||||
# (defines just __get__), then the instance wins. If it's not a
|
||||
# descriptor at all (doesn't have __get__), the instance wins.
|
||||
# NOTE that the docs for descriptors say that these methods must be
|
||||
# defined on the *class* of the object in the type.
|
||||
if name not in self._local_type_get_descriptors:
|
||||
# Entirely not a descriptor. Instance wins.
|
||||
return dct[name]
|
||||
if name in self._local_type_set_or_del_descriptors:
|
||||
# A data descriptor.
|
||||
# arbitrary code execution while these run. If they touch self again,
|
||||
# they'll call back into us and we'll repeat the dance.
|
||||
type_attr = getattr(self._local_type, name)
|
||||
return type(type_attr).__get__(type_attr, self, self._local_type)
|
||||
# Last case is a non-data descriptor. Instance wins.
|
||||
return dct[name]
|
||||
|
||||
if name in self._local_type_vars:
|
||||
# Not in the dictionary, but is found in the type. It could be
|
||||
# a non-data descriptor still. Some descriptors, like @staticmethod,
|
||||
# return objects (functions, in this case), that are *themselves*
|
||||
# descriptors, which when invoked, again, would do the wrong thing.
|
||||
# So we can't rely on getattr() on the type for them, we have to
|
||||
# look through the MRO dicts ourself.
|
||||
if name not in self._local_type_get_descriptors:
|
||||
# Not a descriptor, can't execute code. So all we need is
|
||||
# the return value of getattr() on our type.
|
||||
return getattr(self._local_type, name)
|
||||
|
||||
for base in self._local_type.mro():
|
||||
bd = base.__dict__
|
||||
if name in bd:
|
||||
attr_on_type = bd[name]
|
||||
result = type(attr_on_type).__get__(attr_on_type, self, self._local_type)
|
||||
return result
|
||||
|
||||
# It wasn't in the dict and it wasn't in the type.
|
||||
# So the next step is to invoke type(self)__getattr__, if it
|
||||
# exists, otherwise raise an AttributeError.
|
||||
# we will invoke type(self).__getattr__ or raise an attribute error.
|
||||
if hasattr(self._local_type, '__getattr__'):
|
||||
return self._local_type.__getattr__(self, name)
|
||||
raise AttributeError("%r object has no attribute '%s'"
|
||||
% (self._local_type.__name__, name))
|
||||
|
||||
def __setattr__(self, name, value):
|
||||
if name == '__dict__':
|
||||
raise AttributeError(
|
||||
"%r object attribute '__dict__' is read-only"
|
||||
% type(self))
|
||||
|
||||
if name in _local_attrs:
|
||||
object.__setattr__(self, name, value)
|
||||
return
|
||||
|
||||
dct = _local_get_dict(self)
|
||||
|
||||
if self._local_type is local:
|
||||
# Optimization: If we're not subclassed, we can't
|
||||
# have data descriptors, so this goes right in the dict.
|
||||
dct[name] = value
|
||||
return
|
||||
|
||||
if name in self._local_type_vars:
|
||||
if name in self._local_type_set_descriptors:
|
||||
type_attr = getattr(self._local_type, name, _marker)
|
||||
# A data descriptor, like a property or a slot.
|
||||
type(type_attr).__set__(type_attr, self, value)
|
||||
return
|
||||
# Otherwise it goes directly in the dict
|
||||
dct[name] = value
|
||||
|
||||
def __delattr__(self, name):
|
||||
if name == '__dict__':
|
||||
raise AttributeError(
|
||||
"%r object attribute '__dict__' is read-only"
|
||||
% self.__class__.__name__)
|
||||
|
||||
if name in self._local_type_vars:
|
||||
if name in self._local_type_del_descriptors:
|
||||
# A data descriptor, like a property or a slot.
|
||||
type_attr = getattr(self._local_type, name, _marker)
|
||||
type(type_attr).__delete__(type_attr, self)
|
||||
return
|
||||
# Otherwise it goes directly in the dict
|
||||
|
||||
# Begin inlined function _get_dict()
|
||||
dct = _local_get_dict(self)
|
||||
|
||||
try:
|
||||
del dct[name]
|
||||
except KeyError:
|
||||
raise AttributeError(name)
|
||||
|
||||
def __copy__(self):
|
||||
impl = self._local__impl
|
||||
entry = impl.dicts[id(getcurrent())] # pylint:disable=undefined-variable
|
||||
|
||||
dct = entry.localdict
|
||||
duplicate = copy(dct)
|
||||
|
||||
cls = type(self)
|
||||
instance = cls(*impl.localargs, **impl.localkwargs)
|
||||
_local__copy_dict_from(instance, impl, duplicate)
|
||||
return instance
|
||||
|
||||
def _local__copy_dict_from(self, impl, duplicate):
|
||||
current = getcurrent() # pylint:disable=undefined-variable
|
||||
currentId = id(current)
|
||||
new_impl = self._local__impl
|
||||
assert new_impl is not impl
|
||||
entry = new_impl.dicts[currentId]
|
||||
new_impl.dicts[currentId] = _localimpl_dict_entry(entry.wrgreenlet, duplicate)
|
||||
|
||||
def _local_find_descriptors(self):
|
||||
type_self = type(self)
|
||||
gets = set()
|
||||
dels = set()
|
||||
set_or_del = set()
|
||||
sets = set()
|
||||
mro = list(type_self.mro())
|
||||
|
||||
for attr_name in dir(type_self):
|
||||
# Conventionally, descriptors when called on a class
|
||||
# return themself, but not all do. Notable exceptions are
|
||||
# in the zope.interface package, where things like __provides__
|
||||
# return other class attributes. So we can't use getattr, and instead
|
||||
# walk up the dicts
|
||||
for base in mro:
|
||||
bd = base.__dict__
|
||||
if attr_name in bd:
|
||||
attr = bd[attr_name]
|
||||
break
|
||||
else:
|
||||
raise AttributeError(attr_name)
|
||||
|
||||
type_attr = type(attr)
|
||||
if hasattr(type_attr, '__get__'):
|
||||
gets.add(attr_name)
|
||||
if hasattr(type_attr, '__delete__'):
|
||||
dels.add(attr_name)
|
||||
set_or_del.add(attr_name)
|
||||
if hasattr(type_attr, '__set__'):
|
||||
sets.add(attr_name)
|
||||
|
||||
return (gets, dels, set_or_del, sets)
|
||||
|
||||
# Cython doesn't let us use __new__, it requires
|
||||
# __cinit__. But we need __new__ if we're not compiled
|
||||
# (e.g., on PyPy). So we set it at runtime. Cython
|
||||
# will raise an error if we're compiled.
|
||||
def __new__(cls, *args, **kw):
|
||||
self = super(local, cls).__new__(cls) # pylint:disable=no-value-for-parameter
|
||||
# We get the cls in *args for some reason
|
||||
# too when we do it this way....except on PyPy3, which does
|
||||
# not *unless* it's wrapped in a classmethod (which it is)
|
||||
self.__cinit__(*args[1:], **kw)
|
||||
return self
|
||||
|
||||
if local.__module__ == 'gevent.local':
|
||||
# PyPy2/3 and CPython handle adding a __new__ to the class
|
||||
# in different ways. In CPython and PyPy3, it must be wrapped with classmethod;
|
||||
# in PyPy2 < 7.3.3, it must not. In either case, the args that get passed to
|
||||
# it are stil wrong.
|
||||
#
|
||||
# Prior to Python 3.10, Cython-compiled classes were immutable and
|
||||
# raised a TypeError on assignment to __new__, and we relied on that
|
||||
# to detect the compiled version; but that breaks in
|
||||
# 3.10 as classes are now mutable. (See
|
||||
# https://github.com/cython/cython/issues/4326).
|
||||
#
|
||||
# That's OK; post https://github.com/gevent/gevent/issues/1480, the Cython-compiled
|
||||
# module has a different name than the pure-Python version and we can check for that.
|
||||
# It's not as direct, but it works.
|
||||
# So here we're not compiled
|
||||
local.__new__ = classmethod(__new__)
|
||||
else: # pragma: no cover
|
||||
# Make sure we revisit in case of changes to the (accelerator) module names.
|
||||
if local.__module__ != 'gevent._gevent_clocal': # pylint:disable=else-if-used
|
||||
raise AssertionError("Module names changed (local: %r; __name__: %r); revisit this code" % (
|
||||
local.__module__, __name__) )
|
||||
|
||||
_init()
|
||||
|
||||
from gevent._util import import_c_accel
|
||||
import_c_accel(globals(), 'gevent._local')
|
||||
Reference in New Issue
Block a user