gevent.local – Greenlet-local objects#

Greenlet-local objects.

This module is based on _threading_local.py from the standard library of Python 3.4.

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

Changed in version 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.

Changed in version 1.2.3: Use a weak-reference to clear the greenlet link we establish in case the local object dies before the greenlet does.

Changed in version 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.

Changed in version 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__.

class local(*args, **kw)[source]#

Bases: object

An object whose attributes are greenlet-local.