Skip to content

Commit 52617cb

Browse files
Merge branch 'main' into pythongh-132042-precalc-mro-dict
2 parents 860fbe7 + 71da68d commit 52617cb

36 files changed

+520
-150
lines changed

.github/CODEOWNERS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,9 @@ Include/internal/pycore_time.h @pganssle @abalkin
167167
**/*imap* @python/email-team
168168
**/*poplib* @python/email-team
169169

170+
# Exclude .mailmap from being owned by @python/email-team
171+
/.mailmap
172+
170173
# Garbage collector
171174
/Modules/gcmodule.c @pablogsal
172175
/Doc/library/gc.rst @pablogsal

.mailmap

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
# This file sets the canonical name for contributors to the repository.
22
# Documentation: https://git-scm.com/docs/gitmailmap
3+
Willow Chargin <[email protected]>
34

Doc/library/shutil.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -473,7 +473,7 @@ Directory and files operations
473473
This is also applied when *cmd* is a path that contains a directory
474474
component::
475475

476-
>> shutil.which("C:\\Python33\\python")
476+
>>> shutil.which("C:\\Python33\\python")
477477
'C:\\Python33\\python.EXE'
478478

479479
.. versionadded:: 3.3

Doc/library/socket.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -498,6 +498,9 @@ Constants
498498
.. versionchanged:: 3.11
499499
NetBSD support was added.
500500

501+
.. versionchanged:: next
502+
Restored missing ``CAN_RAW_ERR_FILTER`` on Linux.
503+
501504
.. data:: CAN_BCM
502505
CAN_BCM_*
503506

Doc/whatsnew/3.14.rst

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -479,6 +479,12 @@ Other language changes
479479
:func:`textwrap.dedent`.
480480
(Contributed by Jon Crall and Steven Sun in :gh:`103998`.)
481481

482+
* Improve error message when an object supporting the synchronous (resp.
483+
asynchronous) context manager protocol is entered using :keyword:`async
484+
with` (resp. :keyword:`with`) instead of :keyword:`with` (resp.
485+
:keyword:`async with`).
486+
(Contributed by Bénédikt Tran in :gh:`128398`.)
487+
482488

483489
.. _whatsnew314-pep765:
484490

@@ -819,6 +825,12 @@ json
819825
See the :ref:`JSON command-line interface <json-commandline>` documentation.
820826
(Contributed by Trey Hunner in :gh:`122873`.)
821827

828+
* By default, the output of the :ref:`JSON command-line interface <json-commandline>`
829+
is highlighted in color. This can be controlled via the
830+
:envvar:`PYTHON_COLORS` environment variable as well as the canonical
831+
|NO_COLOR|_ and |FORCE_COLOR|_ environment variables. See also
832+
:ref:`using-on-controlling-color`.
833+
(Contributed by Tomas Roun in :gh:`131952`.)
822834

823835
linecache
824836
---------

Include/internal/pycore_ceval.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ PyAPI_DATA(const conversion_func) _PyEval_ConversionFuncs[];
279279
typedef struct _special_method {
280280
PyObject *name;
281281
const char *error;
282+
const char *error_suggestion; // improved optional suggestion
282283
} _Py_SpecialMethod;
283284

284285
PyAPI_DATA(const _Py_SpecialMethod) _Py_SpecialMethods[];
@@ -309,6 +310,16 @@ PyAPI_FUNC(PyObject *) _PyEval_LoadName(PyThreadState *tstate, _PyInterpreterFra
309310
PyAPI_FUNC(int)
310311
_Py_Check_ArgsIterable(PyThreadState *tstate, PyObject *func, PyObject *args);
311312

313+
/*
314+
* Indicate whether a special method of given 'oparg' can use the (improved)
315+
* alternative error message instead. Only methods loaded by LOAD_SPECIAL
316+
* support alternative error messages.
317+
*
318+
* Symbol is exported for the JIT (see discussion on GH-132218).
319+
*/
320+
PyAPI_FUNC(int)
321+
_PyEval_SpecialMethodCanSuggest(PyObject *self, int oparg);
322+
312323
/* Bits that can be set in PyThreadState.eval_breaker */
313324
#define _PY_GIL_DROP_REQUEST_BIT (1U << 0)
314325
#define _PY_SIGNALS_PENDING_BIT (1U << 1)

Include/internal/pycore_interp_structs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -754,6 +754,12 @@ struct _is {
754754
* and should be placed at the beginning. */
755755
struct _ceval_state ceval;
756756

757+
/* This structure is carefully allocated so that it's correctly aligned
758+
* to avoid undefined behaviors during LOAD and STORE. The '_malloced'
759+
* field stores the allocated pointer address that will later be freed.
760+
*/
761+
void *_malloced;
762+
757763
PyInterpreterState *next;
758764

759765
int64_t id;

Lib/json/tool.py

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,40 @@
55
"""
66
import argparse
77
import json
8+
import re
89
import sys
10+
from _colorize import ANSIColors, can_colorize
11+
12+
13+
# The string we are colorizing is valid JSON,
14+
# so we can use a looser but simpler regex to match
15+
# the various parts, most notably strings and numbers,
16+
# where the regex given by the spec is much more complex.
17+
_color_pattern = re.compile(r'''
18+
(?P<key>"(\\.|[^"\\])*")(?=:) |
19+
(?P<string>"(\\.|[^"\\])*") |
20+
(?P<boolean>true|false) |
21+
(?P<null>null)
22+
''', re.VERBOSE)
23+
24+
25+
_colors = {
26+
'key': ANSIColors.INTENSE_BLUE,
27+
'string': ANSIColors.BOLD_GREEN,
28+
'boolean': ANSIColors.BOLD_CYAN,
29+
'null': ANSIColors.BOLD_CYAN,
30+
}
31+
32+
33+
def _replace_match_callback(match):
34+
for key, color in _colors.items():
35+
if m := match.group(key):
36+
return f"{color}{m}{ANSIColors.RESET}"
37+
return match.group()
38+
39+
40+
def _colorize_json(json_str):
41+
return re.sub(_color_pattern, _replace_match_callback, json_str)
942

1043

1144
def main():
@@ -68,7 +101,11 @@ def main():
68101
outfile = open(options.outfile, 'w', encoding='utf-8')
69102
with outfile:
70103
for obj in objs:
71-
json.dump(obj, outfile, **dump_args)
104+
if can_colorize(file=outfile):
105+
json_str = json.dumps(obj, **dump_args)
106+
outfile.write(_colorize_json(json_str))
107+
else:
108+
json.dump(obj, outfile, **dump_args)
72109
outfile.write('\n')
73110
except ValueError as e:
74111
raise SystemExit(e)

Lib/test/test_json/test_tool.py

Lines changed: 89 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import subprocess
77

88
from test import support
9-
from test.support import os_helper
9+
from test.support import force_not_colorized, os_helper
1010
from test.support.script_helper import assert_python_ok
1111

1212

@@ -87,6 +87,7 @@ class TestMain(unittest.TestCase):
8787
}
8888
""")
8989

90+
@force_not_colorized
9091
def test_stdin_stdout(self):
9192
args = sys.executable, '-m', self.module
9293
process = subprocess.run(args, input=self.data, capture_output=True, text=True, check=True)
@@ -102,7 +103,8 @@ def _create_infile(self, data=None):
102103

103104
def test_infile_stdout(self):
104105
infile = self._create_infile()
105-
rc, out, err = assert_python_ok('-m', self.module, infile)
106+
rc, out, err = assert_python_ok('-m', self.module, infile,
107+
PYTHON_COLORS='0')
106108
self.assertEqual(rc, 0)
107109
self.assertEqual(out.splitlines(), self.expect.encode().splitlines())
108110
self.assertEqual(err, b'')
@@ -116,7 +118,8 @@ def test_non_ascii_infile(self):
116118
''').encode()
117119

118120
infile = self._create_infile(data)
119-
rc, out, err = assert_python_ok('-m', self.module, infile)
121+
rc, out, err = assert_python_ok('-m', self.module, infile,
122+
PYTHON_COLORS='0')
120123

121124
self.assertEqual(rc, 0)
122125
self.assertEqual(out.splitlines(), expect.splitlines())
@@ -125,7 +128,8 @@ def test_non_ascii_infile(self):
125128
def test_infile_outfile(self):
126129
infile = self._create_infile()
127130
outfile = os_helper.TESTFN + '.out'
128-
rc, out, err = assert_python_ok('-m', self.module, infile, outfile)
131+
rc, out, err = assert_python_ok('-m', self.module, infile, outfile,
132+
PYTHON_COLORS='0')
129133
self.addCleanup(os.remove, outfile)
130134
with open(outfile, "r", encoding="utf-8") as fp:
131135
self.assertEqual(fp.read(), self.expect)
@@ -135,33 +139,38 @@ def test_infile_outfile(self):
135139

136140
def test_writing_in_place(self):
137141
infile = self._create_infile()
138-
rc, out, err = assert_python_ok('-m', self.module, infile, infile)
142+
rc, out, err = assert_python_ok('-m', self.module, infile, infile,
143+
PYTHON_COLORS='0')
139144
with open(infile, "r", encoding="utf-8") as fp:
140145
self.assertEqual(fp.read(), self.expect)
141146
self.assertEqual(rc, 0)
142147
self.assertEqual(out, b'')
143148
self.assertEqual(err, b'')
144149

150+
@force_not_colorized
145151
def test_jsonlines(self):
146152
args = sys.executable, '-m', self.module, '--json-lines'
147153
process = subprocess.run(args, input=self.jsonlines_raw, capture_output=True, text=True, check=True)
148154
self.assertEqual(process.stdout, self.jsonlines_expect)
149155
self.assertEqual(process.stderr, '')
150156

151157
def test_help_flag(self):
152-
rc, out, err = assert_python_ok('-m', self.module, '-h')
158+
rc, out, err = assert_python_ok('-m', self.module, '-h',
159+
PYTHON_COLORS='0')
153160
self.assertEqual(rc, 0)
154161
self.assertTrue(out.startswith(b'usage: '))
155162
self.assertEqual(err, b'')
156163

157164
def test_sort_keys_flag(self):
158165
infile = self._create_infile()
159-
rc, out, err = assert_python_ok('-m', self.module, '--sort-keys', infile)
166+
rc, out, err = assert_python_ok('-m', self.module, '--sort-keys', infile,
167+
PYTHON_COLORS='0')
160168
self.assertEqual(rc, 0)
161169
self.assertEqual(out.splitlines(),
162170
self.expect_without_sort_keys.encode().splitlines())
163171
self.assertEqual(err, b'')
164172

173+
@force_not_colorized
165174
def test_indent(self):
166175
input_ = '[1, 2]'
167176
expect = textwrap.dedent('''\
@@ -175,6 +184,7 @@ def test_indent(self):
175184
self.assertEqual(process.stdout, expect)
176185
self.assertEqual(process.stderr, '')
177186

187+
@force_not_colorized
178188
def test_no_indent(self):
179189
input_ = '[1,\n2]'
180190
expect = '[1, 2]\n'
@@ -183,6 +193,7 @@ def test_no_indent(self):
183193
self.assertEqual(process.stdout, expect)
184194
self.assertEqual(process.stderr, '')
185195

196+
@force_not_colorized
186197
def test_tab(self):
187198
input_ = '[1, 2]'
188199
expect = '[\n\t1,\n\t2\n]\n'
@@ -191,6 +202,7 @@ def test_tab(self):
191202
self.assertEqual(process.stdout, expect)
192203
self.assertEqual(process.stderr, '')
193204

205+
@force_not_colorized
194206
def test_compact(self):
195207
input_ = '[ 1 ,\n 2]'
196208
expect = '[1,2]\n'
@@ -203,7 +215,8 @@ def test_no_ensure_ascii_flag(self):
203215
infile = self._create_infile('{"key":"💩"}')
204216
outfile = os_helper.TESTFN + '.out'
205217
self.addCleanup(os.remove, outfile)
206-
assert_python_ok('-m', self.module, '--no-ensure-ascii', infile, outfile)
218+
assert_python_ok('-m', self.module, '--no-ensure-ascii', infile,
219+
outfile, PYTHON_COLORS='0')
207220
with open(outfile, "rb") as f:
208221
lines = f.read().splitlines()
209222
# asserting utf-8 encoded output file
@@ -214,13 +227,14 @@ def test_ensure_ascii_default(self):
214227
infile = self._create_infile('{"key":"💩"}')
215228
outfile = os_helper.TESTFN + '.out'
216229
self.addCleanup(os.remove, outfile)
217-
assert_python_ok('-m', self.module, infile, outfile)
230+
assert_python_ok('-m', self.module, infile, outfile, PYTHON_COLORS='0')
218231
with open(outfile, "rb") as f:
219232
lines = f.read().splitlines()
220233
# asserting an ascii encoded output file
221234
expected = [b'{', rb' "key": "\ud83d\udca9"', b"}"]
222235
self.assertEqual(lines, expected)
223236

237+
@force_not_colorized
224238
@unittest.skipIf(sys.platform =="win32", "The test is failed with ValueError on Windows")
225239
def test_broken_pipe_error(self):
226240
cmd = [sys.executable, '-m', self.module]
@@ -232,7 +246,73 @@ def test_broken_pipe_error(self):
232246
proc.communicate(b'"{}"')
233247
self.assertEqual(proc.returncode, errno.EPIPE)
234248

249+
def test_colors(self):
250+
infile = os_helper.TESTFN
251+
self.addCleanup(os.remove, infile)
252+
253+
cases = (
254+
('{}', b'{}'),
255+
('[]', b'[]'),
256+
('null', b'\x1b[1;36mnull\x1b[0m'),
257+
('true', b'\x1b[1;36mtrue\x1b[0m'),
258+
('false', b'\x1b[1;36mfalse\x1b[0m'),
259+
('NaN', b'NaN'),
260+
('Infinity', b'Infinity'),
261+
('-Infinity', b'-Infinity'),
262+
('"foo"', b'\x1b[1;32m"foo"\x1b[0m'),
263+
(r'" \"foo\" "', b'\x1b[1;32m" \\"foo\\" "\x1b[0m'),
264+
('"α"', b'\x1b[1;32m"\\u03b1"\x1b[0m'),
265+
('123', b'123'),
266+
('-1.2345e+23', b'-1.2345e+23'),
267+
(r'{"\\": ""}',
268+
b'''\
269+
{
270+
\x1b[94m"\\\\"\x1b[0m: \x1b[1;32m""\x1b[0m
271+
}'''),
272+
(r'{"\\\\": ""}',
273+
b'''\
274+
{
275+
\x1b[94m"\\\\\\\\"\x1b[0m: \x1b[1;32m""\x1b[0m
276+
}'''),
277+
('''\
278+
{
279+
"foo": "bar",
280+
"baz": 1234,
281+
"qux": [true, false, null],
282+
"xyz": [NaN, -Infinity, Infinity]
283+
}''',
284+
b'''\
285+
{
286+
\x1b[94m"foo"\x1b[0m: \x1b[1;32m"bar"\x1b[0m,
287+
\x1b[94m"baz"\x1b[0m: 1234,
288+
\x1b[94m"qux"\x1b[0m: [
289+
\x1b[1;36mtrue\x1b[0m,
290+
\x1b[1;36mfalse\x1b[0m,
291+
\x1b[1;36mnull\x1b[0m
292+
],
293+
\x1b[94m"xyz"\x1b[0m: [
294+
NaN,
295+
-Infinity,
296+
Infinity
297+
]
298+
}'''),
299+
)
300+
301+
for input_, expected in cases:
302+
with self.subTest(input=input_):
303+
with open(infile, "w", encoding="utf-8") as fp:
304+
fp.write(input_)
305+
_, stdout, _ = assert_python_ok('-m', self.module, infile,
306+
PYTHON_COLORS='1')
307+
stdout = stdout.replace(b'\r\n', b'\n') # normalize line endings
308+
stdout = stdout.strip()
309+
self.assertEqual(stdout, expected)
310+
235311

236312
@support.requires_subprocess()
237313
class TestTool(TestMain):
238314
module = 'json.tool'
315+
316+
317+
if __name__ == "__main__":
318+
unittest.main()

Lib/test/test_pyrepl/test_unix_console.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ def unix_console(events, **kwargs):
2323
height = kwargs.get("height", 25)
2424
width = kwargs.get("width", 80)
2525
console.getheightwidth = MagicMock(side_effect=lambda: (height, width))
26+
console.wait = MagicMock()
2627

2728
console.prepare()
2829
for key, val in kwargs.items():

0 commit comments

Comments
 (0)