# -*- coding: utf-8 -*-# copyright 2018 gevent. See LICENSE"""Maintains the thread local hub."""from__future__importabsolute_importfrom__future__importdivisionfrom__future__importprint_functionimport_thread__all__=['get_hub','get_hub_noargs','get_hub_if_exists',]# These must be the "real" native thread versions,# not monkey-patched.# We are imported early enough (by gevent/__init__) that# we can rely on not being monkey-patched in any way yet.assert'gevent'notinstr(_thread._local)class_Threadlocal(_thread._local):def__init__(self):# Use a class with an initializer so that we can test# for 'is None' instead of catching AttributeError, making# the code cleaner and possibly solving some corner cases# (like #687).## However, under some weird circumstances, it _seems_ like the# __init__ method doesn't get called properly ("seems" is the# keyword). We've seen at least one instance# (https://github.com/gevent/gevent/issues/1961) of# ``AttributeError: '_Threadlocal' object has no attribute # 'hub'``# which should be impossible unless:## - Someone manually deletes the attribute# - The _threadlocal object itself is in the process of being# deleted. The C ``tp_clear`` slot for it deletes the ``__dict__``# of each instance in each thread (and/or the ``tp_clear`` of ``dict`` itself# clears the instance). Now, how we could be getting# cleared while still being used is unclear, but clearing is part of# circular garbage collection, and in the bug report it looks like we're inside a# weakref finalizer or ``__del__`` method, which could suggest that# garbage collection is happening.## See https://github.com/gevent/gevent/issues/1961# and ``get_hub_if_exists()``super(_Threadlocal,self).__init__()self.Hub=Noneself.loop=Noneself.hub=None_threadlocal=_Threadlocal()Hub=None# Set when gevent.hub is importeddefget_hub_class():"""Return the type of hub to use for the current thread. If there's no type of hub for the current thread yet, 'gevent.hub.Hub' is used. """hubtype=_threadlocal.HubifhubtypeisNone:hubtype=_threadlocal.Hub=Hubreturnhubtypedefset_default_hub_class(hubtype):globalHubHub=hubtype
[docs]defget_hub():""" Return the hub for the current thread. If a hub does not exist in the current thread, a new one is created of the type returned by :func:`get_hub_class`. .. deprecated:: 1.3b1 The ``*args`` and ``**kwargs`` arguments are deprecated. They were only used when the hub was created, and so were non-deterministic---to be sure they were used, *all* callers had to pass them, or they were order-dependent. Use ``set_hub`` instead. .. versionchanged:: 1.5a3 The *args* and *kwargs* arguments are now completely ignored. .. versionchanged:: 23.7.0 The long-deprecated ``args`` and ``kwargs`` parameters are no longer accepted. """# See get_hub_if_existstry:hub=_threadlocal.hubexceptAttributeError:hub=NoneifhubisNone:hubtype=get_hub_class()hub=_threadlocal.hub=hubtype()returnhub
# For Cython purposes, we need to duplicate get_hub into this function so it# can be directly called.defget_hub_noargs():# See get_hub_if_existstry:hub=_threadlocal.hubexceptAttributeError:hub=NoneifhubisNone:hubtype=get_hub_class()hub=_threadlocal.hub=hubtype()returnhubdefget_hub_if_exists():""" Return the hub for the current thread. Return ``None`` if no hub has been created yet. """# Attempt a band-aid for the poorly-understood behaviour# seen in https://github.com/gevent/gevent/issues/1961# where the ``hub`` attribute has gone missing.try:return_threadlocal.hubexceptAttributeError:# XXX: I'd really like to report this, but I'm not sure how# that can be done safely (because I don't know how we get# here in the first place). We may be in a place where imports# are unsafe, or the interpreter is shutting down, or the# thread is exiting, or...returnNonedefset_hub(hub):_threadlocal.hub=hubdefget_loop():return_threadlocal.loopdefset_loop(loop):_threadlocal.loop=loopfromgevent._utilimportimport_c_accelimport_c_accel(globals(),'gevent.__hub_local')