Skip to content

Commit de0e39e

Browse files
committed
Merge remote-tracking branch 'upstream/main' into typealiasmod
2 parents f98dfcc + 678bf57 commit de0e39e

35 files changed

+2142
-1658
lines changed

Doc/library/urllib.parse.rst

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -159,6 +159,10 @@ or on combining URL components into a URL string.
159159
ParseResult(scheme='http', netloc='www.cwi.nl:80', path='/%7Eguido/Python.html',
160160
params='', query='', fragment='')
161161

162+
.. warning::
163+
164+
:func:`urlparse` does not perform validation. See :ref:`URL parsing
165+
security <url-parsing-security>` for details.
162166

163167
.. versionchanged:: 3.2
164168
Added IPv6 URL parsing capabilities.
@@ -324,8 +328,14 @@ or on combining URL components into a URL string.
324328
``#``, ``@``, or ``:`` will raise a :exc:`ValueError`. If the URL is
325329
decomposed before parsing, no error will be raised.
326330

327-
Following the `WHATWG spec`_ that updates RFC 3986, ASCII newline
328-
``\n``, ``\r`` and tab ``\t`` characters are stripped from the URL.
331+
Following some of the `WHATWG spec`_ that updates RFC 3986, leading C0
332+
control and space characters are stripped from the URL. ``\n``,
333+
``\r`` and tab ``\t`` characters are removed from the URL at any position.
334+
335+
.. warning::
336+
337+
:func:`urlsplit` does not perform validation. See :ref:`URL parsing
338+
security <url-parsing-security>` for details.
329339

330340
.. versionchanged:: 3.6
331341
Out-of-range port numbers now raise :exc:`ValueError`, instead of
@@ -338,6 +348,9 @@ or on combining URL components into a URL string.
338348
.. versionchanged:: 3.10
339349
ASCII newline and tab characters are stripped from the URL.
340350

351+
.. versionchanged:: 3.12
352+
Leading WHATWG C0 control and space characters are stripped from the URL.
353+
341354
.. _WHATWG spec: https://url.spec.whatwg.org/#concept-basic-url-parser
342355

343356
.. function:: urlunsplit(parts)
@@ -414,6 +427,35 @@ or on combining URL components into a URL string.
414427
or ``scheme://host/path``). If *url* is not a wrapped URL, it is returned
415428
without changes.
416429

430+
.. _url-parsing-security:
431+
432+
URL parsing security
433+
--------------------
434+
435+
The :func:`urlsplit` and :func:`urlparse` APIs do not perform **validation** of
436+
inputs. They may not raise errors on inputs that other applications consider
437+
invalid. They may also succeed on some inputs that might not be considered
438+
URLs elsewhere. Their purpose is for practical functionality rather than
439+
purity.
440+
441+
Instead of raising an exception on unusual input, they may instead return some
442+
component parts as empty strings. Or components may contain more than perhaps
443+
they should.
444+
445+
We recommend that users of these APIs where the values may be used anywhere
446+
with security implications code defensively. Do some verification within your
447+
code before trusting a returned component part. Does that ``scheme`` make
448+
sense? Is that a sensible ``path``? Is there anything strange about that
449+
``hostname``? etc.
450+
451+
What constitutes a URL is not universally well defined. Different applications
452+
have different needs and desired constraints. For instance the living `WHATWG
453+
spec`_ describes what user facing web clients such as a web browser require.
454+
While :rfc:`3986` is more general. These functions incorporate some aspects of
455+
both, but cannot be claimed compliant with either. The APIs and existing user
456+
code with expectations on specific behaviors predate both standards leading us
457+
to be very cautious about making API behavior changes.
458+
417459
.. _parsing-ascii-encoded-bytes:
418460

419461
Parsing ASCII Encoded Bytes

Include/internal/pycore_pylifecycle.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@ PyAPI_FUNC(int) _Py_IsLocaleCoercionTarget(const char *ctype_loc);
3131

3232
extern void _Py_InitVersion(void);
3333
extern PyStatus _PyFaulthandler_Init(int enable);
34-
extern int _PyTraceMalloc_Init(int enable);
3534
extern PyObject * _PyBuiltin_Init(PyInterpreterState *interp);
3635
extern PyStatus _PySys_Create(
3736
PyThreadState *tstate,

Include/tracemalloc.h

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,40 @@ PyAPI_FUNC(int) PyTraceMalloc_Untrack(
3333
PyAPI_FUNC(PyObject*) _PyTraceMalloc_GetTraceback(
3434
unsigned int domain,
3535
uintptr_t ptr);
36+
37+
/* Return non-zero if tracemalloc is tracing */
38+
PyAPI_FUNC(int) _PyTraceMalloc_IsTracing(void);
39+
40+
/* Clear the tracemalloc traces */
41+
PyAPI_FUNC(void) _PyTraceMalloc_ClearTraces(void);
42+
43+
/* Clear the tracemalloc traces */
44+
PyAPI_FUNC(PyObject *) _PyTraceMalloc_GetTraces(void);
45+
46+
/* Clear tracemalloc traceback for an object */
47+
PyAPI_FUNC(PyObject *) _PyTraceMalloc_GetObjectTraceback(PyObject *obj);
48+
49+
/* Initialize tracemalloc */
50+
PyAPI_FUNC(int) _PyTraceMalloc_Init(void);
51+
52+
/* Start tracemalloc */
53+
PyAPI_FUNC(int) _PyTraceMalloc_Start(int max_nframe);
54+
55+
/* Stop tracemalloc */
56+
PyAPI_FUNC(void) _PyTraceMalloc_Stop(void);
57+
58+
/* Get the tracemalloc traceback limit */
59+
PyAPI_FUNC(int) _PyTraceMalloc_GetTracebackLimit(void);
60+
61+
/* Get the memory usage of tracemalloc in bytes */
62+
PyAPI_FUNC(size_t) _PyTraceMalloc_GetMemory(void);
63+
64+
/* Get the current size and peak size of traced memory blocks as a 2-tuple */
65+
PyAPI_FUNC(PyObject *) _PyTraceMalloc_GetTracedMemory(void);
66+
67+
/* Set the peak size of traced memory blocks to the current size */
68+
PyAPI_FUNC(void) _PyTraceMalloc_ResetPeak(void);
69+
3670
#endif
3771

3872
#endif /* !Py_TRACEMALLOC_H */

Lib/asyncio/subprocess.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,9 @@ def pipe_connection_lost(self, fd, exc):
8181
self._stdin_closed.set_result(None)
8282
else:
8383
self._stdin_closed.set_exception(exc)
84+
# Since calling `wait_closed()` is not mandatory,
85+
# we shouldn't log the traceback if this is not awaited.
86+
self._stdin_closed._log_traceback = False
8487
return
8588
if fd == 1:
8689
reader = self.stdout

Lib/idlelib/NEWS.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,11 @@ Released on 2023-10-02
44
=========================
55

66

7+
gh-104499: Fix completions for Tk Aqua 8.7 (currently blank).
8+
9+
gh-104486: Make About print both tcl and tk versions if different,
10+
as is expected someday.
11+
712
gh-88496 Fix IDLE test hang on macOS.
813

914
gh-103314 Support sys.last_exc after exceptions in Shell.

Lib/idlelib/autocomplete_w.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -182,16 +182,11 @@ def show_window(self, comp_lists, index, complete, mode, userWantsWin):
182182
self.userwantswindow = userWantsWin
183183
self.lasttypedstart = self.start
184184

185-
# Put widgets in place
186185
self.autocompletewindow = acw = Toplevel(self.widget)
187-
# Put it in a position so that it is not seen.
188-
acw.wm_geometry("+10000+10000")
189-
# Make it float
186+
acw.withdraw()
190187
acw.wm_overrideredirect(1)
191188
try:
192-
# This command is only needed and available on Tk >= 8.4.0 for OSX
193-
# Without it, call tips intrude on the typing process by grabbing
194-
# the focus.
189+
# Prevent grabbing focus on maxOS.
195190
acw.tk.call("::tk::unsupported::MacWindowStyle", "style", acw._w,
196191
"help", "noActivates")
197192
except TclError:
@@ -271,6 +266,7 @@ def winconfig_event(self, event):
271266
# place acw above current line
272267
new_y -= acw_height
273268
acw.wm_geometry("+%d+%d" % (new_x, new_y))
269+
acw.deiconify()
274270
acw.update_idletasks()
275271
except TclError:
276272
pass

Lib/idlelib/help_about.py

Lines changed: 16 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,12 @@
1111

1212
from idlelib import textview
1313

14-
version = python_version()
14+
pyver = python_version()
1515

16-
17-
def build_bits():
18-
"Return bits for platform."
19-
if sys.platform == 'darwin':
20-
return '64' if sys.maxsize > 2**32 else '32'
21-
else:
22-
return architecture()[0][:2]
16+
if sys.platform == 'darwin':
17+
bits = '64' if sys.maxsize > 2**32 else '32'
18+
else:
19+
bits = architecture()[0][:2]
2320

2421

2522
class AboutDialog(Toplevel):
@@ -45,7 +42,7 @@ def __init__(self, parent, title=None, *, _htest=False, _utest=False):
4542
self.create_widgets()
4643
self.resizable(height=False, width=False)
4744
self.title(title or
48-
f'About IDLE {version} ({build_bits()} bit)')
45+
f'About IDLE {pyver} ({bits} bit)')
4946
self.transient(parent)
5047
self.grab_set()
5148
self.protocol("WM_DELETE_WINDOW", self.ok)
@@ -76,8 +73,8 @@ def create_widgets(self):
7673
bg=self.bg, font=('courier', 24, 'bold'))
7774
header.grid(row=0, column=0, sticky=E, padx=10, pady=10)
7875

79-
tk_patchlevel = self.info_patchlevel()
80-
ext = '.png' if tk_patchlevel >= (8, 6) else '.gif'
76+
tkpatch = self._root().getvar('tk_patchLevel')
77+
ext = '.png' if tkpatch >= '8.6' else '.gif'
8178
icon = os.path.join(os.path.abspath(os.path.dirname(__file__)),
8279
'Icons', f'idle_48{ext}')
8380
self.icon_image = PhotoImage(master=self._root(), file=icon)
@@ -102,13 +99,11 @@ def create_widgets(self):
10299
height=2, bg=self.bg).grid(row=8, column=0, sticky=EW,
103100
columnspan=3, padx=5, pady=5)
104101

105-
pyver = Label(frame_background,
106-
text='Python version: ' + version,
107-
fg=self.fg, bg=self.bg)
108-
pyver.grid(row=9, column=0, sticky=W, padx=10, pady=0)
109-
tkver = Label(frame_background, text=f'Tk version: {tk_patchlevel}',
110-
fg=self.fg, bg=self.bg)
111-
tkver.grid(row=9, column=1, sticky=W, padx=2, pady=0)
102+
tclver = str(self.info_patchlevel())
103+
tkver = ' and ' + tkpatch if tkpatch != tclver else ''
104+
versions = f"Python {pyver} with tcl/tk {tclver}{tkver}"
105+
vers = Label(frame_background, text=versions, fg=self.fg, bg=self.bg)
106+
vers.grid(row=9, column=0, sticky=W, padx=10, pady=0)
112107
py_buttons = Frame(frame_background, bg=self.bg)
113108
py_buttons.grid(row=10, column=0, columnspan=2, sticky=NSEW)
114109
self.py_license = Button(py_buttons, text='License', width=8,
@@ -128,10 +123,10 @@ def create_widgets(self):
128123
height=2, bg=self.bg).grid(row=11, column=0, sticky=EW,
129124
columnspan=3, padx=5, pady=5)
130125

131-
idlever = Label(frame_background,
132-
text='IDLE version: ' + version,
126+
idle = Label(frame_background,
127+
text='IDLE',
133128
fg=self.fg, bg=self.bg)
134-
idlever.grid(row=12, column=0, sticky=W, padx=10, pady=0)
129+
idle.grid(row=12, column=0, sticky=W, padx=10, pady=0)
135130
idle_buttons = Frame(frame_background, bg=self.bg)
136131
idle_buttons.grid(row=13, column=0, columnspan=3, sticky=NSEW)
137132
self.readme = Button(idle_buttons, text='README', width=8,

Lib/idlelib/idle_test/test_help_about.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ def tearDownClass(cls):
3636
del cls.root
3737

3838
def test_build_bits(self):
39-
self.assertIn(help_about.build_bits(), ('32', '64'))
39+
self.assertIn(help_about.bits, ('32', '64'))
4040

4141
def test_dialog_title(self):
4242
"""Test about dialog title"""
@@ -107,7 +107,7 @@ def test_dialog_title(self):
107107
"""Test about dialog title"""
108108
self.assertEqual(self.dialog.title(),
109109
f'About IDLE {python_version()}'
110-
f' ({help_about.build_bits()} bit)')
110+
f' ({help_about.bits} bit)')
111111

112112

113113
class CloseTest(unittest.TestCase):

Lib/test/test_syntax.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1877,6 +1877,68 @@ def f(x: *b)
18771877
^^^^^^^^^^^
18781878
SyntaxError: bytes can only contain ASCII literal characters
18791879
1880+
Invalid expressions in type scopes:
1881+
1882+
>>> type A[T: (x:=3)] = int
1883+
Traceback (most recent call last):
1884+
...
1885+
SyntaxError: named expression cannot be used within a TypeVar bound
1886+
1887+
>>> type A[T: (yield 3)] = int
1888+
Traceback (most recent call last):
1889+
...
1890+
SyntaxError: yield expression cannot be used within a TypeVar bound
1891+
1892+
>>> type A[T: (await 3)] = int
1893+
Traceback (most recent call last):
1894+
...
1895+
SyntaxError: await expression cannot be used within a TypeVar bound
1896+
1897+
>>> type A[T: (yield from [])] = int
1898+
Traceback (most recent call last):
1899+
...
1900+
SyntaxError: yield expression cannot be used within a TypeVar bound
1901+
1902+
>>> type A = (x := 3)
1903+
Traceback (most recent call last):
1904+
...
1905+
SyntaxError: named expression cannot be used within a type alias
1906+
1907+
>>> type A = (yield 3)
1908+
Traceback (most recent call last):
1909+
...
1910+
SyntaxError: yield expression cannot be used within a type alias
1911+
1912+
>>> type A = (await 3)
1913+
Traceback (most recent call last):
1914+
...
1915+
SyntaxError: await expression cannot be used within a type alias
1916+
1917+
>>> type A = (yield from [])
1918+
Traceback (most recent call last):
1919+
...
1920+
SyntaxError: yield expression cannot be used within a type alias
1921+
1922+
>>> class A[T]((x := 3)): ...
1923+
Traceback (most recent call last):
1924+
...
1925+
SyntaxError: named expression cannot be used within the definition of a generic
1926+
1927+
>>> class A[T]((yield 3)): ...
1928+
Traceback (most recent call last):
1929+
...
1930+
SyntaxError: yield expression cannot be used within the definition of a generic
1931+
1932+
>>> class A[T]((await 3)): ...
1933+
Traceback (most recent call last):
1934+
...
1935+
SyntaxError: await expression cannot be used within the definition of a generic
1936+
1937+
>>> class A[T]((yield from [])): ...
1938+
Traceback (most recent call last):
1939+
...
1940+
SyntaxError: yield expression cannot be used within the definition of a generic
1941+
18801942
"""
18811943

18821944
import re

Lib/test/test_type_params.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -425,11 +425,11 @@ class Foo[T: Foo, U: (Foo, Foo)]:
425425
type_params = Foo.__type_params__
426426
self.assertEqual(len(type_params), 2)
427427
self.assertEqual(type_params[0].__name__, "T")
428-
self.assertEqual(type_params[0].__bound__, Foo)
429-
self.assertEqual(type_params[0].__constraints__, None)
428+
self.assertIs(type_params[0].__bound__, Foo)
429+
self.assertEqual(type_params[0].__constraints__, ())
430430

431431
self.assertEqual(type_params[1].__name__, "U")
432-
self.assertEqual(type_params[1].__bound__, None)
432+
self.assertIs(type_params[1].__bound__, None)
433433
self.assertEqual(type_params[1].__constraints__, (Foo, Foo))
434434

435435
def test_evaluation_error(self):
@@ -439,16 +439,16 @@ class Foo[T: Undefined, U: (Undefined,)]:
439439
type_params = Foo.__type_params__
440440
with self.assertRaises(NameError):
441441
type_params[0].__bound__
442-
self.assertEqual(type_params[0].__constraints__, None)
443-
self.assertEqual(type_params[1].__bound__, None)
442+
self.assertEqual(type_params[0].__constraints__, ())
443+
self.assertIs(type_params[1].__bound__, None)
444444
with self.assertRaises(NameError):
445445
type_params[1].__constraints__
446446

447447
Undefined = "defined"
448448
self.assertEqual(type_params[0].__bound__, "defined")
449-
self.assertEqual(type_params[0].__constraints__, None)
449+
self.assertEqual(type_params[0].__constraints__, ())
450450

451-
self.assertEqual(type_params[1].__bound__, None)
451+
self.assertIs(type_params[1].__bound__, None)
452452
self.assertEqual(type_params[1].__constraints__, ("defined",))
453453

454454

0 commit comments

Comments
 (0)