Skip to content

Commit f371b23

Browse files
miss-islingtonlysnikolaouhugovk
authored
[3.13] gh-107006: Move threading.local docstring to docs (GH-131840) (#133432)
Co-authored-by: Lysandros Nikolaou <[email protected]> Co-authored-by: Hugo van Kemenade <[email protected]>
1 parent df1669e commit f371b23

File tree

3 files changed

+120
-131
lines changed

3 files changed

+120
-131
lines changed

Doc/library/threading.rst

Lines changed: 118 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -260,23 +260,132 @@ All of the methods described below are executed atomically.
260260
Thread-Local Data
261261
-----------------
262262

263-
Thread-local data is data whose values are thread specific. To manage
264-
thread-local data, just create an instance of :class:`local` (or a
265-
subclass) and store attributes on it::
263+
Thread-local data is data whose values are thread specific. If you
264+
have data that you want to be local to a thread, create a
265+
:class:`local` object and use its attributes::
266266

267-
mydata = threading.local()
268-
mydata.x = 1
267+
>>> mydata = local()
268+
>>> mydata.number = 42
269+
>>> mydata.number
270+
42
269271

270-
The instance's values will be different for separate threads.
272+
You can also access the :class:`local`-object's dictionary::
273+
274+
>>> mydata.__dict__
275+
{'number': 42}
276+
>>> mydata.__dict__.setdefault('widgets', [])
277+
[]
278+
>>> mydata.widgets
279+
[]
280+
281+
If we access the data in a different thread::
282+
283+
>>> log = []
284+
>>> def f():
285+
... items = sorted(mydata.__dict__.items())
286+
... log.append(items)
287+
... mydata.number = 11
288+
... log.append(mydata.number)
289+
290+
>>> import threading
291+
>>> thread = threading.Thread(target=f)
292+
>>> thread.start()
293+
>>> thread.join()
294+
>>> log
295+
[[], 11]
296+
297+
we get different data. Furthermore, changes made in the other thread
298+
don't affect data seen in this thread::
299+
300+
>>> mydata.number
301+
42
302+
303+
Of course, values you get from a :class:`local` object, including their
304+
:attr:`~object.__dict__` attribute, are for whatever thread was current
305+
at the time the attribute was read. For that reason, you generally
306+
don't want to save these values across threads, as they apply only to
307+
the thread they came from.
308+
309+
You can create custom :class:`local` objects by subclassing the
310+
:class:`local` class::
311+
312+
>>> class MyLocal(local):
313+
... number = 2
314+
... def __init__(self, /, **kw):
315+
... self.__dict__.update(kw)
316+
... def squared(self):
317+
... return self.number ** 2
318+
319+
This can be useful to support default values, methods and
320+
initialization. Note that if you define an :py:meth:`~object.__init__`
321+
method, it will be called each time the :class:`local` object is used
322+
in a separate thread. This is necessary to initialize each thread's
323+
dictionary.
324+
325+
Now if we create a :class:`local` object::
326+
327+
>>> mydata = MyLocal(color='red')
328+
329+
we have a default number::
330+
331+
>>> mydata.number
332+
2
333+
334+
an initial color::
335+
336+
>>> mydata.color
337+
'red'
338+
>>> del mydata.color
339+
340+
And a method that operates on the data::
341+
342+
>>> mydata.squared()
343+
4
344+
345+
As before, we can access the data in a separate thread::
346+
347+
>>> log = []
348+
>>> thread = threading.Thread(target=f)
349+
>>> thread.start()
350+
>>> thread.join()
351+
>>> log
352+
[[('color', 'red')], 11]
353+
354+
without affecting this thread's data::
355+
356+
>>> mydata.number
357+
2
358+
>>> mydata.color
359+
Traceback (most recent call last):
360+
...
361+
AttributeError: 'MyLocal' object has no attribute 'color'
362+
363+
Note that subclasses can define :term:`__slots__`, but they are not
364+
thread local. They are shared across threads::
365+
366+
>>> class MyLocal(local):
367+
... __slots__ = 'number'
368+
369+
>>> mydata = MyLocal()
370+
>>> mydata.number = 42
371+
>>> mydata.color = 'red'
372+
373+
So, the separate thread::
374+
375+
>>> thread = threading.Thread(target=f)
376+
>>> thread.start()
377+
>>> thread.join()
378+
379+
affects what we see::
380+
381+
>>> mydata.number
382+
11
271383

272384

273385
.. class:: local()
274386

275387
A class that represents thread-local data.
276388

277-
For more details and extensive examples, see the documentation string of the
278-
:mod:`!_threading_local` module: :source:`Lib/_threading_local.py`.
279-
280389

281390
.. _thread-objects:
282391

Lib/_threading_local.py

Lines changed: 0 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -4,128 +4,6 @@
44
class. Depending on the version of Python you're using, there may be a
55
faster one available. You should always import the `local` class from
66
`threading`.)
7-
8-
Thread-local objects support the management of thread-local data.
9-
If you have data that you want to be local to a thread, simply create
10-
a thread-local object and use its attributes:
11-
12-
>>> mydata = local()
13-
>>> mydata.number = 42
14-
>>> mydata.number
15-
42
16-
17-
You can also access the local-object's dictionary:
18-
19-
>>> mydata.__dict__
20-
{'number': 42}
21-
>>> mydata.__dict__.setdefault('widgets', [])
22-
[]
23-
>>> mydata.widgets
24-
[]
25-
26-
What's important about thread-local objects is that their data are
27-
local to a thread. If we access the data in a different thread:
28-
29-
>>> log = []
30-
>>> def f():
31-
... items = sorted(mydata.__dict__.items())
32-
... log.append(items)
33-
... mydata.number = 11
34-
... log.append(mydata.number)
35-
36-
>>> import threading
37-
>>> thread = threading.Thread(target=f)
38-
>>> thread.start()
39-
>>> thread.join()
40-
>>> log
41-
[[], 11]
42-
43-
we get different data. Furthermore, changes made in the other thread
44-
don't affect data seen in this thread:
45-
46-
>>> mydata.number
47-
42
48-
49-
Of course, values you get from a local object, including a __dict__
50-
attribute, are for whatever thread was current at the time the
51-
attribute was read. For that reason, you generally don't want to save
52-
these values across threads, as they apply only to the thread they
53-
came from.
54-
55-
You can create custom local objects by subclassing the local class:
56-
57-
>>> class MyLocal(local):
58-
... number = 2
59-
... def __init__(self, /, **kw):
60-
... self.__dict__.update(kw)
61-
... def squared(self):
62-
... return self.number ** 2
63-
64-
This can be useful to support default values, methods and
65-
initialization. Note that if you define an __init__ method, it will be
66-
called each time the local object is used in a separate thread. This
67-
is necessary to initialize each thread's dictionary.
68-
69-
Now if we create a local object:
70-
71-
>>> mydata = MyLocal(color='red')
72-
73-
Now we have a default number:
74-
75-
>>> mydata.number
76-
2
77-
78-
an initial color:
79-
80-
>>> mydata.color
81-
'red'
82-
>>> del mydata.color
83-
84-
And a method that operates on the data:
85-
86-
>>> mydata.squared()
87-
4
88-
89-
As before, we can access the data in a separate thread:
90-
91-
>>> log = []
92-
>>> thread = threading.Thread(target=f)
93-
>>> thread.start()
94-
>>> thread.join()
95-
>>> log
96-
[[('color', 'red')], 11]
97-
98-
without affecting this thread's data:
99-
100-
>>> mydata.number
101-
2
102-
>>> mydata.color
103-
Traceback (most recent call last):
104-
...
105-
AttributeError: 'MyLocal' object has no attribute 'color'
106-
107-
Note that subclasses can define slots, but they are not thread
108-
local. They are shared across threads:
109-
110-
>>> class MyLocal(local):
111-
... __slots__ = 'number'
112-
113-
>>> mydata = MyLocal()
114-
>>> mydata.number = 42
115-
>>> mydata.color = 'red'
116-
117-
So, the separate thread:
118-
119-
>>> thread = threading.Thread(target=f)
120-
>>> thread.start()
121-
>>> thread.join()
122-
123-
affects what we see:
124-
125-
>>> mydata.number
126-
11
127-
128-
>>> del mydata
1297
"""
1308

1319
from weakref import ref
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Move documentation and example code for :class:`threading.local` from its
2+
docstring to the official docs.

0 commit comments

Comments
 (0)