Skip to content

Commit f208cd4

Browse files
committed
Merge branch 'main' into pr/92078
2 parents 4d3573a + e575190 commit f208cd4

Some content is hidden

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

44 files changed

+679
-290
lines changed

Doc/library/heapq.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,7 @@ The strange invariant above is meant to be an efficient memory representation
315315
for a tournament. The numbers below are *k*, not ``a[k]``:
316316

317317
.. figure:: heapq-binary-tree.svg
318+
:class: invert-in-dark-mode
318319
:align: center
319320
:alt: Example (min-heap) binary tree.
320321

Doc/library/unittest.mock.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2654,9 +2654,9 @@ with any methods on the mock:
26542654

26552655
.. code-block:: pycon
26562656
2657-
>>> mock.has_data()
2657+
>>> mock.header_items()
26582658
<mock.Mock object at 0x...>
2659-
>>> mock.has_data.assret_called_with() # Intentional typo!
2659+
>>> mock.header_items.assret_called_with() # Intentional typo!
26602660
26612661
Auto-speccing solves this problem. You can either pass ``autospec=True`` to
26622662
:func:`patch` / :func:`patch.object` or use the :func:`create_autospec` function to create a

Doc/library/urllib.request.rst

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1121,7 +1121,7 @@ HTTPHandler Objects
11211121
.. method:: HTTPHandler.http_open(req)
11221122

11231123
Send an HTTP request, which can be either GET or POST, depending on
1124-
``req.has_data()``.
1124+
``req.data``.
11251125

11261126

11271127
.. _https-handler-objects:
@@ -1133,7 +1133,7 @@ HTTPSHandler Objects
11331133
.. method:: HTTPSHandler.https_open(req)
11341134

11351135
Send an HTTPS request, which can be either GET or POST, depending on
1136-
``req.has_data()``.
1136+
``req.data``.
11371137

11381138

11391139
.. _file-handler-objects:

Include/internal/pycore_bytesobject.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@ extern PyObject* _PyBytes_FromHex(
2020

2121
// Helper for PyBytes_DecodeEscape that detects invalid escape chars.
2222
// Export for test_peg_generator.
23-
PyAPI_FUNC(PyObject*) _PyBytes_DecodeEscape(const char *, Py_ssize_t,
24-
const char *, const char **);
23+
PyAPI_FUNC(PyObject*) _PyBytes_DecodeEscape2(const char *, Py_ssize_t,
24+
const char *,
25+
int *, const char **);
2526

2627

2728
// Substring Search.

Include/internal/pycore_crossinterp.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,13 @@ PyAPI_FUNC(int) _PyCode_GetPureScriptXIData(
200200
PyObject *,
201201
_PyXIData_t *);
202202

203+
// _PyObject_GetXIData() for functions
204+
PyAPI_FUNC(PyObject *) _PyFunction_FromXIData(_PyXIData_t *);
205+
PyAPI_FUNC(int) _PyFunction_GetXIData(
206+
PyThreadState *,
207+
PyObject *,
208+
_PyXIData_t *);
209+
203210

204211
/* using cross-interpreter data */
205212

Include/internal/pycore_unicodeobject.h

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -139,14 +139,18 @@ extern PyObject* _PyUnicode_DecodeUnicodeEscapeStateful(
139139
// Helper for PyUnicode_DecodeUnicodeEscape that detects invalid escape
140140
// chars.
141141
// Export for test_peg_generator.
142-
PyAPI_FUNC(PyObject*) _PyUnicode_DecodeUnicodeEscapeInternal(
142+
PyAPI_FUNC(PyObject*) _PyUnicode_DecodeUnicodeEscapeInternal2(
143143
const char *string, /* Unicode-Escape encoded string */
144144
Py_ssize_t length, /* size of string */
145145
const char *errors, /* error handling */
146146
Py_ssize_t *consumed, /* bytes consumed */
147-
const char **first_invalid_escape); /* on return, points to first
148-
invalid escaped char in
149-
string. */
147+
int *first_invalid_escape_char, /* on return, if not -1, contain the first
148+
invalid escaped char (<= 0xff) or invalid
149+
octal escape (> 0xff) in string. */
150+
const char **first_invalid_escape_ptr); /* on return, if not NULL, may
151+
point to the first invalid escaped
152+
char in string.
153+
May be NULL if errors is not NULL. */
150154

151155
/* --- Raw-Unicode-Escape Codecs ---------------------------------------------- */
152156

Lib/argparse.py

Lines changed: 21 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -167,7 +167,6 @@ def __init__(
167167
indent_increment=2,
168168
max_help_position=24,
169169
width=None,
170-
prefix_chars='-',
171170
color=False,
172171
):
173172
# default setting for width
@@ -176,16 +175,7 @@ def __init__(
176175
width = shutil.get_terminal_size().columns
177176
width -= 2
178177

179-
from _colorize import can_colorize, decolor, get_theme
180-
181-
if color and can_colorize():
182-
self._theme = get_theme(force_color=True).argparse
183-
self._decolor = decolor
184-
else:
185-
self._theme = get_theme(force_no_color=True).argparse
186-
self._decolor = lambda text: text
187-
188-
self._prefix_chars = prefix_chars
178+
self._set_color(color)
189179
self._prog = prog
190180
self._indent_increment = indent_increment
191181
self._max_help_position = min(max_help_position,
@@ -202,6 +192,16 @@ def __init__(
202192
self._whitespace_matcher = _re.compile(r'\s+', _re.ASCII)
203193
self._long_break_matcher = _re.compile(r'\n\n\n+')
204194

195+
def _set_color(self, color):
196+
from _colorize import can_colorize, decolor, get_theme
197+
198+
if color and can_colorize():
199+
self._theme = get_theme(force_color=True).argparse
200+
self._decolor = decolor
201+
else:
202+
self._theme = get_theme(force_no_color=True).argparse
203+
self._decolor = lambda text: text
204+
205205
# ===============================
206206
# Section and indentation methods
207207
# ===============================
@@ -415,14 +415,7 @@ def _format_actions_usage(self, actions, groups):
415415
return ' '.join(self._get_actions_usage_parts(actions, groups))
416416

417417
def _is_long_option(self, string):
418-
return len(string) >= 2 and string[1] in self._prefix_chars
419-
420-
def _is_short_option(self, string):
421-
return (
422-
not self._is_long_option(string)
423-
and len(string) >= 1
424-
and string[0] in self._prefix_chars
425-
)
418+
return len(string) > 2
426419

427420
def _get_actions_usage_parts(self, actions, groups):
428421
# find group indices and identify actions in groups
@@ -471,25 +464,22 @@ def _get_actions_usage_parts(self, actions, groups):
471464
# produce the first way to invoke the option in brackets
472465
else:
473466
option_string = action.option_strings[0]
467+
if self._is_long_option(option_string):
468+
option_color = t.summary_long_option
469+
else:
470+
option_color = t.summary_short_option
474471

475472
# if the Optional doesn't take a value, format is:
476473
# -s or --long
477474
if action.nargs == 0:
478475
part = action.format_usage()
479-
if self._is_long_option(part):
480-
part = f"{t.summary_long_option}{part}{t.reset}"
481-
elif self._is_short_option(part):
482-
part = f"{t.summary_short_option}{part}{t.reset}"
476+
part = f"{option_color}{part}{t.reset}"
483477

484478
# if the Optional takes a value, format is:
485479
# -s ARGS or --long ARGS
486480
else:
487481
default = self._get_default_metavar_for_optional(action)
488482
args_string = self._format_args(action, default)
489-
if self._is_long_option(option_string):
490-
option_color = t.summary_long_option
491-
elif self._is_short_option(option_string):
492-
option_color = t.summary_short_option
493483
part = (
494484
f"{option_color}{option_string} "
495485
f"{t.summary_label}{args_string}{t.reset}"
@@ -606,10 +596,8 @@ def color_option_strings(strings):
606596
for s in strings:
607597
if self._is_long_option(s):
608598
parts.append(f"{t.long_option}{s}{t.reset}")
609-
elif self._is_short_option(s):
610-
parts.append(f"{t.short_option}{s}{t.reset}")
611599
else:
612-
parts.append(s)
600+
parts.append(f"{t.short_option}{s}{t.reset}")
613601
return parts
614602

615603
# if the Optional doesn't take a value, format is:
@@ -2723,16 +2711,9 @@ def format_help(self):
27232711
return formatter.format_help()
27242712

27252713
def _get_formatter(self):
2726-
if isinstance(self.formatter_class, type) and issubclass(
2727-
self.formatter_class, HelpFormatter
2728-
):
2729-
return self.formatter_class(
2730-
prog=self.prog,
2731-
prefix_chars=self.prefix_chars,
2732-
color=self.color,
2733-
)
2734-
else:
2735-
return self.formatter_class(prog=self.prog)
2714+
formatter = self.formatter_class(prog=self.prog)
2715+
formatter._set_color(self.color)
2716+
return formatter
27362717

27372718
# =====================
27382719
# Help-printing methods

Lib/glob.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,12 @@ def concat_path(path, text):
358358
"""
359359
raise NotImplementedError
360360

361+
@staticmethod
362+
def stringify_path(path):
363+
"""Converts the path to a string object
364+
"""
365+
raise NotImplementedError
366+
361367
# High-level methods
362368

363369
def compile(self, pat, altsep=None):
@@ -466,8 +472,9 @@ def recursive_selector(self, part, parts):
466472
select_next = self.selector(parts)
467473

468474
def select_recursive(path, exists=False):
469-
match_pos = len(str(path))
470-
if match is None or match(str(path), match_pos):
475+
path_str = self.stringify_path(path)
476+
match_pos = len(path_str)
477+
if match is None or match(path_str, match_pos):
471478
yield from select_next(path, exists)
472479
stack = [path]
473480
while stack:
@@ -489,7 +496,7 @@ def select_recursive_step(stack, match_pos):
489496
pass
490497

491498
if is_dir or not dir_only:
492-
entry_path_str = str(entry_path)
499+
entry_path_str = self.stringify_path(entry_path)
493500
if dir_only:
494501
entry_path = self.concat_path(entry_path, self.sep)
495502
if match is None or match(entry_path_str, match_pos):
@@ -529,19 +536,6 @@ def scandir(path):
529536
entries = list(scandir_it)
530537
return ((entry, entry.name, entry.path) for entry in entries)
531538

532-
533-
class _PathGlobber(_GlobberBase):
534-
"""Provides shell-style pattern matching and globbing for pathlib paths.
535-
"""
536-
537539
@staticmethod
538-
def lexists(path):
539-
return path.info.exists(follow_symlinks=False)
540-
541-
@staticmethod
542-
def scandir(path):
543-
return ((child.info, child.name, child) for child in path.iterdir())
544-
545-
@staticmethod
546-
def concat_path(path, text):
547-
return path.with_segments(str(path) + text)
540+
def stringify_path(path):
541+
return path # Already a string.

Lib/pathlib/__init__.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,9 @@
2828

2929
from pathlib._os import (
3030
PathInfo, DirEntryInfo,
31+
magic_open, vfspath,
3132
ensure_different_files, ensure_distinct_paths,
32-
copyfile2, copyfileobj, magic_open, copy_info,
33+
copyfile2, copyfileobj, copy_info,
3334
)
3435

3536

@@ -1164,12 +1165,12 @@ def _copy_from_file(self, source, preserve_metadata=False):
11641165
# os.symlink() incorrectly creates a file-symlink on Windows. Avoid
11651166
# this by passing *target_is_dir* to os.symlink() on Windows.
11661167
def _copy_from_symlink(self, source, preserve_metadata=False):
1167-
os.symlink(str(source.readlink()), self, source.info.is_dir())
1168+
os.symlink(vfspath(source.readlink()), self, source.info.is_dir())
11681169
if preserve_metadata:
11691170
copy_info(source.info, self, follow_symlinks=False)
11701171
else:
11711172
def _copy_from_symlink(self, source, preserve_metadata=False):
1172-
os.symlink(str(source.readlink()), self)
1173+
os.symlink(vfspath(source.readlink()), self)
11731174
if preserve_metadata:
11741175
copy_info(source.info, self, follow_symlinks=False)
11751176

Lib/pathlib/_os.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,26 @@ def magic_open(path, mode='r', buffering=-1, encoding=None, errors=None,
210210
raise TypeError(f"{cls.__name__} can't be opened with mode {mode!r}")
211211

212212

213+
def vfspath(path):
214+
"""
215+
Return the string representation of a virtual path object.
216+
"""
217+
try:
218+
return os.fsdecode(path)
219+
except TypeError:
220+
pass
221+
222+
path_type = type(path)
223+
try:
224+
return path_type.__vfspath__(path)
225+
except AttributeError:
226+
if hasattr(path_type, '__vfspath__'):
227+
raise
228+
229+
raise TypeError("expected str, bytes, os.PathLike or JoinablePath "
230+
"object, not " + path_type.__name__)
231+
232+
213233
def ensure_distinct_paths(source, target):
214234
"""
215235
Raise OSError(EINVAL) if the other path is within this path.
@@ -225,8 +245,8 @@ def ensure_distinct_paths(source, target):
225245
err = OSError(EINVAL, "Source path is a parent of target path")
226246
else:
227247
return
228-
err.filename = str(source)
229-
err.filename2 = str(target)
248+
err.filename = vfspath(source)
249+
err.filename2 = vfspath(target)
230250
raise err
231251

232252

@@ -247,8 +267,8 @@ def ensure_different_files(source, target):
247267
except (OSError, ValueError):
248268
return
249269
err = OSError(EINVAL, "Source and target are the same file")
250-
err.filename = str(source)
251-
err.filename2 = str(target)
270+
err.filename = vfspath(source)
271+
err.filename2 = vfspath(target)
252272
raise err
253273

254274

0 commit comments

Comments
 (0)