Skip to content

Commit a8c18b2

Browse files
authored
Merge branch 'main' into simplify-no-other-refs
2 parents 1e7e952 + a580838 commit a8c18b2

22 files changed

+360
-176
lines changed

Doc/deprecations/c-api-pending-removal-in-3.18.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ Pending removal in Python 3.18
77
* :c:func:`!_PyDict_GetItemStringWithError`: use :c:func:`PyDict_GetItemStringRef`.
88
* :c:func:`!_PyDict_Pop()`: :c:func:`PyDict_Pop`.
99
* :c:func:`!_PyLong_Sign()`: use :c:func:`PyLong_GetSign`.
10-
* :c:func:`!_PyLong_New`: use :c:func:`PyLongWriter_Create`.
10+
* :c:func:`!_PyLong_FromDigits` and :c:func:`!_PyLong_New`:
11+
use :c:func:`PyLongWriter_Create`.
1112
* :c:func:`!_PyThreadState_UncheckedGet`: use :c:func:`PyThreadState_GetUnchecked`.
1213
* :c:func:`!_PyUnicode_AsString`: use :c:func:`PyUnicode_AsUTF8`.
1314
* :c:func:`!_Py_HashPointer`: use :c:func:`Py_HashPointer`.

Doc/library/http.cookies.rst

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,7 @@ Morsel Objects
142142
version
143143
httponly
144144
samesite
145+
partitioned
145146

146147
The attribute :attr:`httponly` specifies that the cookie is only transferred
147148
in HTTP requests, and is not accessible through JavaScript. This is intended
@@ -151,6 +152,19 @@ Morsel Objects
151152
send the cookie along with cross-site requests. This helps to mitigate CSRF
152153
attacks. Valid values for this attribute are "Strict" and "Lax".
153154

155+
The attribute :attr:`partitioned` indicates to user agents that these
156+
cross-site cookies *should* only be available in the same top-level context
157+
that the cookie was first set in. For this to be accepted by the user agent,
158+
you **must** also set ``Secure``.
159+
160+
In addition, it is recommended to use the ``__Host`` prefix when setting
161+
partitioned cookies to make them bound to the hostname and not the
162+
registrable domain. Read
163+
`CHIPS (Cookies Having Independent Partitioned State)`_
164+
for full details and examples.
165+
166+
.. _CHIPS (Cookies Having Independent Partitioned State): https://github.com/privacycg/CHIPS/blob/main/README.md
167+
154168
The keys are case-insensitive and their default value is ``''``.
155169

156170
.. versionchanged:: 3.5
@@ -165,6 +179,9 @@ Morsel Objects
165179
.. versionchanged:: 3.8
166180
Added support for the :attr:`samesite` attribute.
167181

182+
.. versionchanged:: 3.14
183+
Added support for the :attr:`partitioned` attribute.
184+
168185

169186
.. attribute:: Morsel.value
170187

Doc/tools/extensions/pyspecific.py

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,16 +34,6 @@
3434
Body.enum.converters['lowerroman'] = \
3535
Body.enum.converters['upperroman'] = lambda x: None
3636

37-
# monkey-patch the productionlist directive to allow hyphens in group names
38-
# https://github.com/sphinx-doc/sphinx/issues/11854
39-
from sphinx.domains import std
40-
41-
std.token_re = re.compile(r'`((~?[\w-]*:)?\w+)`')
42-
43-
# backport :no-index:
44-
PyModule.option_spec['no-index'] = directives.flag
45-
46-
4737
# Support for marking up and linking to bugs.python.org issues
4838

4939
def issue_role(typ, rawtext, text, lineno, inliner, options={}, content=[]):

Doc/whatsnew/3.14.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1396,7 +1396,8 @@ Deprecated
13961396
* :c:func:`!_PyDict_GetItemStringWithError`: use :c:func:`PyDict_GetItemStringRef`.
13971397
* :c:func:`!_PyDict_Pop()`: use :c:func:`PyDict_Pop`.
13981398
* :c:func:`!_PyLong_Sign()`: use :c:func:`PyLong_GetSign`.
1399-
* :c:func:`!_PyLong_New`: use :c:func:`PyLongWriter_Create`.
1399+
* :c:func:`!_PyLong_FromDigits` and :c:func:`!_PyLong_New`:
1400+
use :c:func:`PyLongWriter_Create`.
14001401
* :c:func:`!_PyThreadState_UncheckedGet`: use :c:func:`PyThreadState_GetUnchecked`.
14011402
* :c:func:`!_PyUnicode_AsString`: use :c:func:`PyUnicode_AsUTF8`.
14021403
* :c:func:`!_Py_HashPointer`: use :c:func:`Py_HashPointer`.

Include/cpython/longintrepr.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,7 @@ Py_DEPRECATED(3.14) PyAPI_FUNC(PyLongObject*) _PyLong_New(Py_ssize_t);
105105
// Return a copy of src.
106106
PyAPI_FUNC(PyObject*) _PyLong_Copy(PyLongObject *src);
107107

108-
PyAPI_FUNC(PyLongObject*) _PyLong_FromDigits(
108+
Py_DEPRECATED(3.14) PyAPI_FUNC(PyLongObject*) _PyLong_FromDigits(
109109
int negative,
110110
Py_ssize_t digit_count,
111111
digit *digits);

Include/internal/pycore_debug_offsets.h

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,31 +17,32 @@ extern "C" {
1717

1818
// Macros to burn global values in custom sections so out-of-process
1919
// profilers can locate them easily.
20-
21-
#define GENERATE_DEBUG_SECTION(name, declaration) \
22-
_GENERATE_DEBUG_SECTION_WINDOWS(name) \
23-
_GENERATE_DEBUG_SECTION_APPLE(name) \
24-
declaration \
25-
_GENERATE_DEBUG_SECTION_LINUX(name)
20+
#define GENERATE_DEBUG_SECTION(name, declaration) \
21+
_GENERATE_DEBUG_SECTION_WINDOWS(name) \
22+
_GENERATE_DEBUG_SECTION_APPLE(name) \
23+
declaration \
24+
_GENERATE_DEBUG_SECTION_LINUX(name)
2625

2726
#if defined(MS_WINDOWS)
2827
#define _GENERATE_DEBUG_SECTION_WINDOWS(name) \
29-
_Pragma(Py_STRINGIFY(section(Py_STRINGIFY(name), read, write))) \
30-
__declspec(allocate(Py_STRINGIFY(name)))
28+
_Pragma(Py_STRINGIFY(section(Py_STRINGIFY(name), read, write))) \
29+
__declspec(allocate(Py_STRINGIFY(name)))
3130
#else
3231
#define _GENERATE_DEBUG_SECTION_WINDOWS(name)
3332
#endif
3433

3534
#if defined(__APPLE__)
3635
#define _GENERATE_DEBUG_SECTION_APPLE(name) \
37-
__attribute__((section(SEG_DATA "," Py_STRINGIFY(name))))
36+
__attribute__((section(SEG_DATA "," Py_STRINGIFY(name)))) \
37+
__attribute__((used))
3838
#else
3939
#define _GENERATE_DEBUG_SECTION_APPLE(name)
4040
#endif
4141

4242
#if defined(__linux__) && (defined(__GNUC__) || defined(__clang__))
4343
#define _GENERATE_DEBUG_SECTION_LINUX(name) \
44-
__attribute__((section("." Py_STRINGIFY(name))))
44+
__attribute__((section("." Py_STRINGIFY(name)))) \
45+
__attribute__((used))
4546
#else
4647
#define _GENERATE_DEBUG_SECTION_LINUX(name)
4748
#endif

Lib/http/cookies.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,12 @@ class Morsel(dict):
264264
"httponly" : "HttpOnly",
265265
"version" : "Version",
266266
"samesite" : "SameSite",
267+
"partitioned": "Partitioned",
267268
}
268269

269270
_reserved_defaults = dict.fromkeys(_reserved, "")
270271

271-
_flags = {'secure', 'httponly'}
272+
_flags = {'secure', 'httponly', 'partitioned'}
272273

273274
def __init__(self):
274275
# Set defaults

Lib/test/test__colorize.py

Lines changed: 108 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,132 @@
11
import contextlib
2+
import io
23
import sys
34
import unittest
45
import unittest.mock
56
import _colorize
6-
from test.support import force_not_colorized, make_clean_env
7+
from test.support.os_helper import EnvironmentVarGuard
78

8-
ORIGINAL_CAN_COLORIZE = _colorize.can_colorize
99

10+
@contextlib.contextmanager
11+
def clear_env():
12+
with EnvironmentVarGuard() as mock_env:
13+
for var in "FORCE_COLOR", "NO_COLOR", "PYTHON_COLORS":
14+
mock_env.unset(var)
15+
yield mock_env
1016

11-
def setUpModule():
12-
_colorize.can_colorize = lambda *args, **kwargs: False
1317

14-
15-
def tearDownModule():
16-
_colorize.can_colorize = ORIGINAL_CAN_COLORIZE
18+
def supports_virtual_terminal():
19+
if sys.platform == "win32":
20+
return unittest.mock.patch("nt._supports_virtual_terminal", return_value=True)
21+
else:
22+
return contextlib.nullcontext()
1723

1824

1925
class TestColorizeFunction(unittest.TestCase):
20-
def setUp(self):
21-
# Remove PYTHON* environment variables to isolate from local user
22-
# settings and simulate running with `-E`. Such variables should be
23-
# added to test methods later to patched os.environ.
24-
patcher = unittest.mock.patch("os.environ", new=make_clean_env())
25-
self.addCleanup(patcher.stop)
26-
patcher.start()
27-
28-
@force_not_colorized
2926
def test_colorized_detection_checks_for_environment_variables(self):
30-
flags = unittest.mock.MagicMock(ignore_environment=False)
27+
def check(env, fallback, expected):
28+
with (self.subTest(env=env, fallback=fallback),
29+
clear_env() as mock_env):
30+
mock_env.update(env)
31+
isatty_mock.return_value = fallback
32+
stdout_mock.isatty.return_value = fallback
33+
self.assertEqual(_colorize.can_colorize(), expected)
34+
3135
with (unittest.mock.patch("os.isatty") as isatty_mock,
3236
unittest.mock.patch("sys.stdout") as stdout_mock,
33-
unittest.mock.patch("sys.stderr") as stderr_mock,
34-
unittest.mock.patch("sys.flags", flags),
35-
unittest.mock.patch("_colorize.can_colorize", ORIGINAL_CAN_COLORIZE),
36-
(unittest.mock.patch("nt._supports_virtual_terminal", return_value=False)
37-
if sys.platform == "win32" else
38-
contextlib.nullcontext()) as vt_mock):
37+
supports_virtual_terminal()):
38+
stdout_mock.fileno.return_value = 1
39+
40+
for fallback in False, True:
41+
check({}, fallback, fallback)
42+
check({'TERM': 'dumb'}, fallback, False)
43+
check({'TERM': 'xterm'}, fallback, fallback)
44+
check({'TERM': ''}, fallback, fallback)
45+
check({'FORCE_COLOR': '1'}, fallback, True)
46+
check({'FORCE_COLOR': '0'}, fallback, True)
47+
check({'NO_COLOR': '1'}, fallback, False)
48+
check({'NO_COLOR': '0'}, fallback, False)
49+
50+
check({'TERM': 'dumb', 'FORCE_COLOR': '1'}, False, True)
51+
check({'FORCE_COLOR': '1', 'NO_COLOR': '1'}, True, False)
3952

53+
for ignore_environment in False, True:
54+
# Simulate running with or without `-E`.
55+
flags = unittest.mock.MagicMock(ignore_environment=ignore_environment)
56+
with unittest.mock.patch("sys.flags", flags):
57+
check({'PYTHON_COLORS': '1'}, True, True)
58+
check({'PYTHON_COLORS': '1'}, False, not ignore_environment)
59+
check({'PYTHON_COLORS': '0'}, True, ignore_environment)
60+
check({'PYTHON_COLORS': '0'}, False, False)
61+
for fallback in False, True:
62+
check({'PYTHON_COLORS': 'x'}, fallback, fallback)
63+
check({'PYTHON_COLORS': ''}, fallback, fallback)
64+
65+
check({'TERM': 'dumb', 'PYTHON_COLORS': '1'}, False, not ignore_environment)
66+
check({'NO_COLOR': '1', 'PYTHON_COLORS': '1'}, False, not ignore_environment)
67+
check({'FORCE_COLOR': '1', 'PYTHON_COLORS': '0'}, True, ignore_environment)
68+
69+
@unittest.skipUnless(sys.platform == "win32", "requires Windows")
70+
def test_colorized_detection_checks_on_windows(self):
71+
with (clear_env(),
72+
unittest.mock.patch("os.isatty") as isatty_mock,
73+
unittest.mock.patch("sys.stdout") as stdout_mock,
74+
supports_virtual_terminal() as vt_mock):
75+
stdout_mock.fileno.return_value = 1
4076
isatty_mock.return_value = True
77+
stdout_mock.isatty.return_value = True
78+
79+
vt_mock.return_value = True
80+
self.assertEqual(_colorize.can_colorize(), True)
81+
vt_mock.return_value = False
82+
self.assertEqual(_colorize.can_colorize(), False)
83+
import nt
84+
del nt._supports_virtual_terminal
85+
self.assertEqual(_colorize.can_colorize(), False)
86+
87+
def test_colorized_detection_checks_for_std_streams(self):
88+
with (clear_env(),
89+
unittest.mock.patch("os.isatty") as isatty_mock,
90+
unittest.mock.patch("sys.stdout") as stdout_mock,
91+
unittest.mock.patch("sys.stderr") as stderr_mock,
92+
supports_virtual_terminal()):
4193
stdout_mock.fileno.return_value = 1
94+
stderr_mock.fileno.side_effect = ZeroDivisionError
95+
stderr_mock.isatty.side_effect = ZeroDivisionError
96+
97+
isatty_mock.return_value = True
4298
stdout_mock.isatty.return_value = True
43-
stderr_mock.fileno.return_value = 2
44-
stderr_mock.isatty.return_value = True
45-
with unittest.mock.patch("os.environ", {'TERM': 'dumb'}):
46-
self.assertEqual(_colorize.can_colorize(), False)
47-
with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '1'}):
48-
self.assertEqual(_colorize.can_colorize(), True)
49-
with unittest.mock.patch("os.environ", {'PYTHON_COLORS': '0'}):
50-
self.assertEqual(_colorize.can_colorize(), False)
51-
with unittest.mock.patch("os.environ", {'NO_COLOR': '1'}):
52-
self.assertEqual(_colorize.can_colorize(), False)
53-
with unittest.mock.patch("os.environ",
54-
{'NO_COLOR': '1', "PYTHON_COLORS": '1'}):
55-
self.assertEqual(_colorize.can_colorize(), True)
56-
with unittest.mock.patch("os.environ", {'FORCE_COLOR': '1'}):
57-
self.assertEqual(_colorize.can_colorize(), True)
58-
with unittest.mock.patch("os.environ",
59-
{'FORCE_COLOR': '1', 'NO_COLOR': '1'}):
60-
self.assertEqual(_colorize.can_colorize(), False)
61-
with unittest.mock.patch("os.environ",
62-
{'FORCE_COLOR': '1', "PYTHON_COLORS": '0'}):
63-
self.assertEqual(_colorize.can_colorize(), False)
64-
65-
with unittest.mock.patch("os.environ", {}):
66-
if sys.platform == "win32":
67-
self.assertEqual(_colorize.can_colorize(), False)
68-
69-
vt_mock.return_value = True
70-
self.assertEqual(_colorize.can_colorize(), True)
71-
else:
72-
self.assertEqual(_colorize.can_colorize(), True)
99+
self.assertEqual(_colorize.can_colorize(), True)
100+
101+
isatty_mock.return_value = False
102+
stdout_mock.isatty.return_value = False
103+
self.assertEqual(_colorize.can_colorize(), False)
104+
105+
def test_colorized_detection_checks_for_file(self):
106+
with clear_env(), supports_virtual_terminal():
73107

108+
with unittest.mock.patch("os.isatty") as isatty_mock:
109+
file = unittest.mock.MagicMock()
110+
file.fileno.return_value = 1
111+
isatty_mock.return_value = True
112+
self.assertEqual(_colorize.can_colorize(file=file), True)
74113
isatty_mock.return_value = False
75-
stdout_mock.isatty.return_value = False
76-
stderr_mock.isatty.return_value = False
77-
self.assertEqual(_colorize.can_colorize(), False)
114+
self.assertEqual(_colorize.can_colorize(file=file), False)
115+
116+
# No file.fileno.
117+
with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError):
118+
file = unittest.mock.MagicMock(spec=['isatty'])
119+
file.isatty.return_value = True
120+
self.assertEqual(_colorize.can_colorize(file=file), False)
121+
122+
# file.fileno() raises io.UnsupportedOperation.
123+
with unittest.mock.patch("os.isatty", side_effect=ZeroDivisionError):
124+
file = unittest.mock.MagicMock()
125+
file.fileno.side_effect = io.UnsupportedOperation
126+
file.isatty.return_value = True
127+
self.assertEqual(_colorize.can_colorize(file=file), True)
128+
file.isatty.return_value = False
129+
self.assertEqual(_colorize.can_colorize(file=file), False)
78130

79131

80132
if __name__ == "__main__":

Lib/test/test_asyncio/test_free_threading.py

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import asyncio
2+
import threading
23
import unittest
34
from threading import Thread
45
from unittest import TestCase
@@ -58,6 +59,42 @@ def runner():
5859
with threading_helper.start_threads(threads):
5960
pass
6061

62+
def test_all_tasks_different_thread(self) -> None:
63+
loop = None
64+
started = threading.Event()
65+
done = threading.Event() # used for main task not finishing early
66+
async def coro():
67+
await asyncio.Future()
68+
69+
lock = threading.Lock()
70+
tasks = set()
71+
72+
async def main():
73+
nonlocal tasks, loop
74+
loop = asyncio.get_running_loop()
75+
started.set()
76+
for i in range(1000):
77+
with lock:
78+
asyncio.create_task(coro())
79+
tasks = self.all_tasks(loop)
80+
done.wait()
81+
82+
runner = threading.Thread(target=lambda: asyncio.run(main()))
83+
84+
def check():
85+
started.wait()
86+
with lock:
87+
self.assertSetEqual(tasks & self.all_tasks(loop), tasks)
88+
89+
threads = [threading.Thread(target=check) for _ in range(10)]
90+
runner.start()
91+
92+
with threading_helper.start_threads(threads):
93+
pass
94+
95+
done.set()
96+
runner.join()
97+
6198
def test_run_coroutine_threadsafe(self) -> None:
6299
results = []
63100

Lib/test/test_http_cookies.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,14 @@ def test_set_secure_httponly_attrs(self):
205205
self.assertEqual(C.output(),
206206
'Set-Cookie: Customer="WILE_E_COYOTE"; HttpOnly; Secure')
207207

208+
def test_set_secure_httponly_partitioned_attrs(self):
209+
C = cookies.SimpleCookie('Customer="WILE_E_COYOTE"')
210+
C['Customer']['secure'] = True
211+
C['Customer']['httponly'] = True
212+
C['Customer']['partitioned'] = True
213+
self.assertEqual(C.output(),
214+
'Set-Cookie: Customer="WILE_E_COYOTE"; HttpOnly; Partitioned; Secure')
215+
208216
def test_samesite_attrs(self):
209217
samesite_values = ['Strict', 'Lax', 'strict', 'lax']
210218
for val in samesite_values:

0 commit comments

Comments
 (0)