Skip to content

Commit

Permalink
Pull in main
Browse files Browse the repository at this point in the history
  • Loading branch information
erlend-aasland committed Jan 12, 2025
2 parents c2b3046 + 30268b5 commit 5d8d5fc
Show file tree
Hide file tree
Showing 22 changed files with 579 additions and 468 deletions.
12 changes: 7 additions & 5 deletions Doc/library/pdb.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,13 +179,15 @@ slightly different way:
.. versionadded:: 3.14
The *commands* argument.

.. function:: post_mortem(traceback=None)
.. function:: post_mortem(t=None)

Enter post-mortem debugging of the given *traceback* object. If no
*traceback* is given, it uses the one of the exception that is currently
being handled (an exception must be being handled if the default is to be
used).
Enter post-mortem debugging of the given exception or
:ref:`traceback object <traceback-objects>`. If no value is given, it uses
the exception that is currently being handled, or raises ``ValueError`` if
there isn’t one.

.. versionchanged:: 3.13
Support for exception objects was added.

.. function:: pm()

Expand Down
3 changes: 2 additions & 1 deletion Doc/library/sysconfig.rst
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ Other functions

Windows will return one of:

- win-amd64 (64bit Windows on AMD64, aka x86_64, Intel64, and EM64T)
- win-amd64 (64-bit Windows on AMD64, aka x86_64, Intel64, and EM64T)
- win-arm64 (64-bit Windows on ARM64, aka AArch64)
- win32 (all others - specifically, sys.platform is returned)

macOS can return:
Expand Down
42 changes: 26 additions & 16 deletions Lib/_pyrepl/windows_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,10 @@ def __init__(self, err: int | None, descr: str | None = None) -> None:
MOVE_DOWN = "\x1b[{}B"
CLEAR = "\x1b[H\x1b[J"

# State of control keys: https://learn.microsoft.com/en-us/windows/console/key-event-record-str
ALT_ACTIVE = 0x01 | 0x02
CTRL_ACTIVE = 0x04 | 0x08


class _error(Exception):
pass
Expand Down Expand Up @@ -407,31 +411,37 @@ def get_event(self, block: bool = True) -> Event | None:
continue
return None

key = rec.Event.KeyEvent.uChar.UnicodeChar
key_event = rec.Event.KeyEvent
raw_key = key = key_event.uChar.UnicodeChar

if rec.Event.KeyEvent.uChar.UnicodeChar == "\r":
# Make enter make unix-like
if key == "\r":
# Make enter unix-like
return Event(evt="key", data="\n", raw=b"\n")
elif rec.Event.KeyEvent.wVirtualKeyCode == 8:
elif key_event.wVirtualKeyCode == 8:
# Turn backspace directly into the command
return Event(
evt="key",
data="backspace",
raw=rec.Event.KeyEvent.uChar.UnicodeChar,
)
elif rec.Event.KeyEvent.uChar.UnicodeChar == "\x00":
key = "backspace"
elif key == "\x00":
# Handle special keys like arrow keys and translate them into the appropriate command
code = VK_MAP.get(rec.Event.KeyEvent.wVirtualKeyCode)
if code:
return Event(
evt="key", data=code, raw=rec.Event.KeyEvent.uChar.UnicodeChar
)
key = VK_MAP.get(key_event.wVirtualKeyCode)
if key:
if key_event.dwControlKeyState & CTRL_ACTIVE:
key = f"ctrl {key}"
elif key_event.dwControlKeyState & ALT_ACTIVE:
# queue the key, return the meta command
self.event_queue.insert(0, Event(evt="key", data=key, raw=key))
return Event(evt="key", data="\033") # keymap.py uses this for meta
return Event(evt="key", data=key, raw=key)
if block:
continue

return None

return Event(evt="key", data=key, raw=rec.Event.KeyEvent.uChar.UnicodeChar)
if key_event.dwControlKeyState & ALT_ACTIVE:
# queue the key, return the meta command
self.event_queue.insert(0, Event(evt="key", data=key, raw=raw_key))
return Event(evt="key", data="\033") # keymap.py uses this for meta

return Event(evt="key", data=key, raw=raw_key)

def push_char(self, char: int | bytes) -> None:
"""
Expand Down
110 changes: 62 additions & 48 deletions Lib/pathlib/_abc.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
it's developed alongside pathlib. If it finds success and maturity as a PyPI
package, it could become a public part of the standard library.
Two base classes are defined here -- PurePathBase and PathBase -- that
resemble pathlib's PurePath and Path respectively.
Three base classes are defined here -- JoinablePath, ReadablePath and
WritablePath.
"""

import functools
Expand Down Expand Up @@ -56,13 +56,13 @@ def concat_path(path, text):
return path.with_segments(str(path) + text)


class CopyWorker:
class CopyReader:
"""
Class that implements copying between path objects. An instance of this
class is available from the PathBase.copy property; it's made callable so
that PathBase.copy() can be treated as a method.
class is available from the ReadablePath.copy property; it's made callable
so that ReadablePath.copy() can be treated as a method.
The target path's CopyWorker drives the process from its _create() method.
The target path's CopyWriter drives the process from its _create() method.
Files and directories are exchanged by calling methods on the source and
target paths, and metadata is exchanged by calling
source.copy._read_metadata() and target.copy._write_metadata().
Expand All @@ -77,11 +77,15 @@ def __call__(self, target, follow_symlinks=True, dirs_exist_ok=False,
"""
Recursively copy this file or directory tree to the given destination.
"""
if not isinstance(target, PathBase):
if not isinstance(target, ReadablePath):
target = self._path.with_segments(target)

# Delegate to the target path's CopyWorker object.
return target.copy._create(self._path, follow_symlinks, dirs_exist_ok, preserve_metadata)
# Delegate to the target path's CopyWriter object.
try:
create = target.copy._create
except AttributeError:
raise TypeError(f"Target is not writable: {target}") from None
return create(self._path, follow_symlinks, dirs_exist_ok, preserve_metadata)

_readable_metakeys = frozenset()

Expand All @@ -91,6 +95,10 @@ def _read_metadata(self, metakeys, *, follow_symlinks=True):
"""
raise NotImplementedError


class CopyWriter(CopyReader):
__slots__ = ()

_writable_metakeys = frozenset()

def _write_metadata(self, metadata, *, follow_symlinks=True):
Expand Down Expand Up @@ -182,7 +190,7 @@ def _ensure_distinct_path(self, source):
raise err


class PurePathBase:
class JoinablePath:
"""Base class for pure path objects.
This class *does not* provide several magic methods that are defined in
Expand Down Expand Up @@ -334,7 +342,7 @@ def match(self, path_pattern, *, case_sensitive=None):
is matched. The recursive wildcard '**' is *not* supported by this
method.
"""
if not isinstance(path_pattern, PurePathBase):
if not isinstance(path_pattern, JoinablePath):
path_pattern = self.with_segments(path_pattern)
if case_sensitive is None:
case_sensitive = _is_case_sensitive(self.parser)
Expand All @@ -359,7 +367,7 @@ def full_match(self, pattern, *, case_sensitive=None):
Return True if this path matches the given glob-style pattern. The
pattern is matched against the entire path.
"""
if not isinstance(pattern, PurePathBase):
if not isinstance(pattern, JoinablePath):
pattern = self.with_segments(pattern)
if case_sensitive is None:
case_sensitive = _is_case_sensitive(self.parser)
Expand All @@ -369,7 +377,7 @@ def full_match(self, pattern, *, case_sensitive=None):



class PathBase(PurePathBase):
class ReadablePath(JoinablePath):
"""Base class for concrete path objects.
This class provides dummy implementations for many methods that derived
Expand Down Expand Up @@ -434,25 +442,6 @@ def read_text(self, encoding=None, errors=None, newline=None):
with self.open(mode='r', encoding=encoding, errors=errors, newline=newline) as f:
return f.read()

def write_bytes(self, data):
"""
Open the file in bytes mode, write to it, and close the file.
"""
# type-check for the buffer interface before truncating the file
view = memoryview(data)
with self.open(mode='wb') as f:
return f.write(view)

def write_text(self, data, encoding=None, errors=None, newline=None):
"""
Open the file in text mode, write to it, and close the file.
"""
if not isinstance(data, str):
raise TypeError('data must be str, not %s' %
data.__class__.__name__)
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
return f.write(data)

def _scandir(self):
"""Yield os.DirEntry-like objects of the directory contents.
Expand All @@ -474,7 +463,7 @@ def glob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
"""Iterate over this subtree and yield all existing files (of any
kind, including directories) matching the given relative pattern.
"""
if not isinstance(pattern, PurePathBase):
if not isinstance(pattern, JoinablePath):
pattern = self.with_segments(pattern)
anchor, parts = _explode_path(pattern)
if anchor:
Expand All @@ -496,7 +485,7 @@ def rglob(self, pattern, *, case_sensitive=None, recurse_symlinks=True):
directories) matching the given relative pattern, anywhere in
this subtree.
"""
if not isinstance(pattern, PurePathBase):
if not isinstance(pattern, JoinablePath):
pattern = self.with_segments(pattern)
pattern = '**' / pattern
return self.glob(pattern, case_sensitive=case_sensitive, recurse_symlinks=recurse_symlinks)
Expand Down Expand Up @@ -543,6 +532,28 @@ def readlink(self):
"""
raise NotImplementedError

copy = property(CopyReader, doc=CopyReader.__call__.__doc__)

def copy_into(self, target_dir, *, follow_symlinks=True,
dirs_exist_ok=False, preserve_metadata=False):
"""
Copy this file or directory tree into the given existing directory.
"""
name = self.name
if not name:
raise ValueError(f"{self!r} has an empty name")
elif isinstance(target_dir, ReadablePath):
target = target_dir / name
else:
target = self.with_segments(target_dir, name)
return self.copy(target, follow_symlinks=follow_symlinks,
dirs_exist_ok=dirs_exist_ok,
preserve_metadata=preserve_metadata)


class WritablePath(ReadablePath):
__slots__ = ()

def symlink_to(self, target, target_is_directory=False):
"""
Make this path a symlink pointing to the target path.
Expand All @@ -556,20 +567,23 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
"""
raise NotImplementedError

copy = property(CopyWorker, doc=CopyWorker.__call__.__doc__)
def write_bytes(self, data):
"""
Open the file in bytes mode, write to it, and close the file.
"""
# type-check for the buffer interface before truncating the file
view = memoryview(data)
with self.open(mode='wb') as f:
return f.write(view)

def copy_into(self, target_dir, *, follow_symlinks=True,
dirs_exist_ok=False, preserve_metadata=False):
def write_text(self, data, encoding=None, errors=None, newline=None):
"""
Copy this file or directory tree into the given existing directory.
Open the file in text mode, write to it, and close the file.
"""
name = self.name
if not name:
raise ValueError(f"{self!r} has an empty name")
elif isinstance(target_dir, PathBase):
target = target_dir / name
else:
target = self.with_segments(target_dir, name)
return self.copy(target, follow_symlinks=follow_symlinks,
dirs_exist_ok=dirs_exist_ok,
preserve_metadata=preserve_metadata)
if not isinstance(data, str):
raise TypeError('data must be str, not %s' %
data.__class__.__name__)
with self.open(mode='w', encoding=encoding, errors=errors, newline=newline) as f:
return f.write(data)

copy = property(CopyWriter, doc=CopyWriter.__call__.__doc__)
Loading

0 comments on commit 5d8d5fc

Please sign in to comment.