Skip to content

Commit ad00c98

Browse files
authored
Merge branch '3.13' into store_attr_with_hint
2 parents 0b4626f + d14a1b7 commit ad00c98

File tree

58 files changed

+1224
-539
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+1224
-539
lines changed

.github/CODEOWNERS

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,6 @@ Doc/c-api/stable.rst @encukou
194194
**/*itertools* @rhettinger
195195
**/*collections* @rhettinger
196196
**/*random* @rhettinger
197-
**/*queue* @rhettinger
198197
**/*bisect* @rhettinger
199198
**/*heapq* @rhettinger
200199
**/*functools* @rhettinger

.github/workflows/mypy.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ on:
1313
- "Lib/test/libregrtest/**"
1414
- "Lib/tomllib/**"
1515
- "Misc/mypy/**"
16+
- "Tools/build/compute-changes.py"
1617
- "Tools/build/generate_sbom.py"
18+
- "Tools/build/verify_ensurepip_wheels.py"
19+
- "Tools/build/update_file.py"
1720
- "Tools/cases_generator/**"
1821
- "Tools/clinic/**"
1922
- "Tools/jit/**"

.github/workflows/reusable-context.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,8 @@ jobs:
9797
run: python Tools/build/compute-changes.py
9898
env:
9999
GITHUB_DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
100+
CCF_TARGET_REF: ${{ github.base_ref || github.event.repository.default_branch }}
101+
CCF_HEAD_REF: ${{ github.event.pull_request.head.sha || github.sha }}
100102

101103
- name: Compute hash for config cache key
102104
id: config-hash

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ Tools/unicode/data/
138138
# hendrikmuhs/ccache-action@v1
139139
/.ccache
140140
/cross-build/
141-
/jit_stencils.h
141+
/jit_stencils*.h
142142
/platform
143143
/profile-clean-stamp
144144
/profile-run-stamp

Doc/library/html.parser.rst

Lines changed: 37 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,9 @@ Example HTML Parser Application
4343

4444
As a basic example, below is a simple HTML parser that uses the
4545
:class:`HTMLParser` class to print out start tags, end tags, and data
46-
as they are encountered::
46+
as they are encountered:
47+
48+
.. testcode::
4749

4850
from html.parser import HTMLParser
4951

@@ -63,7 +65,7 @@ as they are encountered::
6365

6466
The output will then be:
6567

66-
.. code-block:: none
68+
.. testoutput::
6769

6870
Encountered a start tag: html
6971
Encountered a start tag: head
@@ -230,7 +232,9 @@ Examples
230232
--------
231233

232234
The following class implements a parser that will be used to illustrate more
233-
examples::
235+
examples:
236+
237+
.. testcode::
234238

235239
from html.parser import HTMLParser
236240
from html.entities import name2codepoint
@@ -266,13 +270,17 @@ examples::
266270

267271
parser = MyHTMLParser()
268272

269-
Parsing a doctype::
273+
Parsing a doctype:
274+
275+
.. doctest::
270276

271277
>>> parser.feed('<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" '
272278
... '"http://www.w3.org/TR/html4/strict.dtd">')
273279
Decl : DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"
274280

275-
Parsing an element with a few attributes and a title::
281+
Parsing an element with a few attributes and a title:
282+
283+
.. doctest::
276284

277285
>>> parser.feed('<img src="python-logo.png" alt="The Python logo">')
278286
Start tag: img
@@ -285,7 +293,9 @@ Parsing an element with a few attributes and a title::
285293
End tag : h1
286294

287295
The content of ``script`` and ``style`` elements is returned as is, without
288-
further parsing::
296+
further parsing:
297+
298+
.. doctest::
289299

290300
>>> parser.feed('<style type="text/css">#python { color: green }</style>')
291301
Start tag: style
@@ -300,35 +310,48 @@ further parsing::
300310
Data : alert("<strong>hello!</strong>");
301311
End tag : script
302312

303-
Parsing comments::
313+
Parsing comments:
314+
315+
.. doctest::
304316

305-
>>> parser.feed('<!-- a comment -->'
317+
>>> parser.feed('<!--a comment-->'
306318
... '<!--[if IE 9]>IE-specific content<![endif]-->')
307-
Comment : a comment
319+
Comment : a comment
308320
Comment : [if IE 9]>IE-specific content<![endif]
309321

310322
Parsing named and numeric character references and converting them to the
311-
correct char (note: these 3 references are all equivalent to ``'>'``)::
323+
correct char (note: these 3 references are all equivalent to ``'>'``):
312324

325+
.. doctest::
326+
327+
>>> parser = MyHTMLParser()
328+
>>> parser.feed('&gt;&#62;&#x3E;')
329+
Data : >>>
330+
331+
>>> parser = MyHTMLParser(convert_charrefs=False)
313332
>>> parser.feed('&gt;&#62;&#x3E;')
314333
Named ent: >
315334
Num ent : >
316335
Num ent : >
317336

318337
Feeding incomplete chunks to :meth:`~HTMLParser.feed` works, but
319338
:meth:`~HTMLParser.handle_data` might be called more than once
320-
(unless *convert_charrefs* is set to ``True``)::
339+
(unless *convert_charrefs* is set to ``True``):
321340

322-
>>> for chunk in ['<sp', 'an>buff', 'ered ', 'text</s', 'pan>']:
341+
.. doctest::
342+
343+
>>> for chunk in ['<sp', 'an>buff', 'ered', ' text</s', 'pan>']:
323344
... parser.feed(chunk)
324345
...
325346
Start tag: span
326347
Data : buff
327348
Data : ered
328-
Data : text
349+
Data : text
329350
End tag : span
330351

331-
Parsing invalid HTML (e.g. unquoted attributes) also works::
352+
Parsing invalid HTML (e.g. unquoted attributes) also works:
353+
354+
.. doctest::
332355

333356
>>> parser.feed('<p><a class=link href=#main>tag soup</p ></a>')
334357
Start tag: p

Doc/library/stdtypes.rst

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -4629,7 +4629,13 @@ can be used interchangeably to index the same dictionary entry.
46294629
being added is already present, the value from the keyword argument
46304630
replaces the value from the positional argument.
46314631

4632-
To illustrate, the following examples all return a dictionary equal to
4632+
Providing keyword arguments as in the first example only works for keys that
4633+
are valid Python identifiers. Otherwise, any valid keys can be used.
4634+
4635+
Dictionaries compare equal if and only if they have the same ``(key,
4636+
value)`` pairs (regardless of ordering). Order comparisons ('<', '<=', '>=', '>') raise
4637+
:exc:`TypeError`. To illustrate dictionary creation and equality,
4638+
the following examples all return a dictionary equal to
46334639
``{"one": 1, "two": 2, "three": 3}``::
46344640

46354641
>>> a = dict(one=1, two=2, three=3)
@@ -4644,6 +4650,27 @@ can be used interchangeably to index the same dictionary entry.
46444650
Providing keyword arguments as in the first example only works for keys that
46454651
are valid Python identifiers. Otherwise, any valid keys can be used.
46464652

4653+
Dictionaries preserve insertion order. Note that updating a key does not
4654+
affect the order. Keys added after deletion are inserted at the end. ::
4655+
4656+
>>> d = {"one": 1, "two": 2, "three": 3, "four": 4}
4657+
>>> d
4658+
{'one': 1, 'two': 2, 'three': 3, 'four': 4}
4659+
>>> list(d)
4660+
['one', 'two', 'three', 'four']
4661+
>>> list(d.values())
4662+
[1, 2, 3, 4]
4663+
>>> d["one"] = 42
4664+
>>> d
4665+
{'one': 42, 'two': 2, 'three': 3, 'four': 4}
4666+
>>> del d["two"]
4667+
>>> d["two"] = None
4668+
>>> d
4669+
{'one': 42, 'three': 3, 'four': 4, 'two': None}
4670+
4671+
.. versionchanged:: 3.7
4672+
Dictionary order is guaranteed to be insertion order. This behavior was
4673+
an implementation detail of CPython from 3.6.
46474674

46484675
These are the operations that dictionaries support (and therefore, custom
46494676
mapping types should support too):
@@ -4814,32 +4841,6 @@ can be used interchangeably to index the same dictionary entry.
48144841

48154842
.. versionadded:: 3.9
48164843

4817-
Dictionaries compare equal if and only if they have the same ``(key,
4818-
value)`` pairs (regardless of ordering). Order comparisons ('<', '<=', '>=', '>') raise
4819-
:exc:`TypeError`.
4820-
4821-
Dictionaries preserve insertion order. Note that updating a key does not
4822-
affect the order. Keys added after deletion are inserted at the end. ::
4823-
4824-
>>> d = {"one": 1, "two": 2, "three": 3, "four": 4}
4825-
>>> d
4826-
{'one': 1, 'two': 2, 'three': 3, 'four': 4}
4827-
>>> list(d)
4828-
['one', 'two', 'three', 'four']
4829-
>>> list(d.values())
4830-
[1, 2, 3, 4]
4831-
>>> d["one"] = 42
4832-
>>> d
4833-
{'one': 42, 'two': 2, 'three': 3, 'four': 4}
4834-
>>> del d["two"]
4835-
>>> d["two"] = None
4836-
>>> d
4837-
{'one': 42, 'three': 3, 'four': 4, 'two': None}
4838-
4839-
.. versionchanged:: 3.7
4840-
Dictionary order is guaranteed to be insertion order. This behavior was
4841-
an implementation detail of CPython from 3.6.
4842-
48434844
Dictionaries and dictionary views are reversible. ::
48444845

48454846
>>> d = {"one": 1, "two": 2, "three": 3, "four": 4}

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

0 commit comments

Comments
 (0)