Skip to content

Commit

Permalink
Merge branch 'main' into strftime-windows-wcsftime
Browse files Browse the repository at this point in the history
  • Loading branch information
serhiy-storchaka authored Oct 18, 2024
2 parents 16782e9 + 2e950e3 commit 0e1bdf7
Show file tree
Hide file tree
Showing 29 changed files with 253 additions and 64 deletions.
9 changes: 3 additions & 6 deletions Doc/library/_thread.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,9 @@ Lock objects have the following methods:
.. versionchanged:: 3.2
Lock acquires can now be interrupted by signals on POSIX.

.. versionchanged:: 3.14
Lock acquires can now be interrupted by signals on Windows.


.. method:: lock.release()

Expand Down Expand Up @@ -219,12 +222,6 @@ In addition to these methods, lock objects can also be used via the
* Calling :func:`sys.exit` or raising the :exc:`SystemExit` exception is
equivalent to calling :func:`_thread.exit`.

* It is platform-dependent whether the :meth:`~threading.Lock.acquire` method
on a lock can be interrupted (so that the :exc:`KeyboardInterrupt` exception
will happen immediately, rather than only after the lock has been acquired or
the operation has timed out). It can be interrupted on POSIX, but not on
Windows.

* When the main thread exits, it is system defined whether the other threads
survive. On most systems, they are killed without executing
:keyword:`try` ... :keyword:`finally` clauses or executing object
Expand Down
4 changes: 2 additions & 2 deletions Doc/library/socket.rst
Original file line number Diff line number Diff line change
Expand Up @@ -451,8 +451,8 @@ Constants
network interface instead of its name.

.. versionchanged:: 3.14
Added missing ``IP_RECVERR``, ``IP_RECVTTL``, and ``IP_RECVORIGDSTADDR``
on Linux.
Added missing ``IP_RECVERR``, ``IPV6_RECVERR``, ``IP_RECVTTL``, and
``IP_RECVORIGDSTADDR`` on Linux.

.. versionchanged:: 3.14
Added support for ``TCP_QUICKACK`` on Windows platforms when available.
Expand Down
3 changes: 3 additions & 0 deletions Doc/library/threading.rst
Original file line number Diff line number Diff line change
Expand Up @@ -567,6 +567,9 @@ All methods are executed atomically.
Lock acquisition can now be interrupted by signals on POSIX if the
underlying threading implementation supports it.

.. versionchanged:: 3.14
Lock acquisition can now be interrupted by signals on Windows.


.. method:: release()

Expand Down
8 changes: 8 additions & 0 deletions Lib/ctypes/wintypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,10 +63,16 @@ def __repr__(self):
HBITMAP = HANDLE
HBRUSH = HANDLE
HCOLORSPACE = HANDLE
HCONV = HANDLE
HCONVLIST = HANDLE
HCURSOR = HANDLE
HDC = HANDLE
HDDEDATA = HANDLE
HDESK = HANDLE
HDROP = HANDLE
HDWP = HANDLE
HENHMETAFILE = HANDLE
HFILE = INT
HFONT = HANDLE
HGDIOBJ = HANDLE
HGLOBAL = HANDLE
Expand All @@ -82,9 +88,11 @@ def __repr__(self):
HMONITOR = HANDLE
HPALETTE = HANDLE
HPEN = HANDLE
HRESULT = LONG
HRGN = HANDLE
HRSRC = HANDLE
HSTR = HANDLE
HSZ = HANDLE
HTASK = HANDLE
HWINSTA = HANDLE
HWND = HANDLE
Expand Down
9 changes: 5 additions & 4 deletions Lib/json/decoder.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,18 @@ def __reduce__(self):
}


HEXDIGITS = re.compile(r'[0-9A-Fa-f]{4}', FLAGS)
STRINGCHUNK = re.compile(r'(.*?)(["\\\x00-\x1f])', FLAGS)
BACKSLASH = {
'"': '"', '\\': '\\', '/': '/',
'b': '\b', 'f': '\f', 'n': '\n', 'r': '\r', 't': '\t',
}

def _decode_uXXXX(s, pos):
esc = s[pos + 1:pos + 5]
if len(esc) == 4 and esc[1] not in 'xX':
def _decode_uXXXX(s, pos, _m=HEXDIGITS.match):
esc = _m(s, pos + 1)
if esc is not None:
try:
return int(esc, 16)
return int(esc.group(), 16)
except ValueError:
pass
msg = "Invalid \\uXXXX escape"
Expand Down
2 changes: 1 addition & 1 deletion Lib/json/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
__all__ = ['make_scanner']

NUMBER_RE = re.compile(
r'(-?(?:0|[1-9]\d*))(\.\d+)?([eE][-+]?\d+)?',
r'(-?(?:0|[1-9][0-9]*))(\.[0-9]+)?([eE][-+]?[0-9]+)?',
(re.VERBOSE | re.MULTILINE | re.DOTALL))

def py_make_scanner(context):
Expand Down
24 changes: 20 additions & 4 deletions Lib/pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -402,6 +402,8 @@ def setup(self, f, tb):
self.curframe = self.stack[self.curindex][0]
self.set_convenience_variable(self.curframe, '_frame', self.curframe)

self._save_initial_file_mtime(self.curframe)

if self._chained_exceptions:
self.set_convenience_variable(
self.curframe,
Expand Down Expand Up @@ -494,9 +496,21 @@ def _cmdloop(self):
except KeyboardInterrupt:
self.message('--KeyboardInterrupt--')

def _save_initial_file_mtime(self, frame):
"""save the mtime of the all the files in the frame stack in the file mtime table
if they haven't been saved yet."""
while frame:
filename = frame.f_code.co_filename
if filename not in self._file_mtime_table:
try:
self._file_mtime_table[filename] = os.path.getmtime(filename)
except Exception:
pass
frame = frame.f_back

def _validate_file_mtime(self):
"""Check if the source file of the current frame has been modified since
the last time we saw it. If so, give a warning."""
"""Check if the source file of the current frame has been modified.
If so, give a warning and reset the modify time to current."""
try:
filename = self.curframe.f_code.co_filename
mtime = os.path.getmtime(filename)
Expand All @@ -506,7 +520,7 @@ def _validate_file_mtime(self):
mtime != self._file_mtime_table[filename]):
self.message(f"*** WARNING: file '{filename}' was edited, "
"running stale code until the program is rerun")
self._file_mtime_table[filename] = mtime
self._file_mtime_table[filename] = mtime

# Called before loop, handles display expressions
# Set up convenience variable containers
Expand Down Expand Up @@ -836,7 +850,6 @@ def onecmd(self, line):
a breakpoint command list definition.
"""
if not self.commands_defining:
self._validate_file_mtime()
if line.startswith('_pdbcmd'):
command, arg, line = self.parseline(line)
if hasattr(self, command):
Expand Down Expand Up @@ -980,6 +993,7 @@ def completedefault(self, text, line, begidx, endidx):

def _pdbcmd_print_frame_status(self, arg):
self.print_stack_trace(0)
self._validate_file_mtime()
self._show_display()

def _pdbcmd_silence_frame_status(self, arg):
Expand Down Expand Up @@ -1861,6 +1875,7 @@ def do_list(self, arg):
self.message('[EOF]')
except KeyboardInterrupt:
pass
self._validate_file_mtime()
do_l = do_list

def do_longlist(self, arg):
Expand All @@ -1879,6 +1894,7 @@ def do_longlist(self, arg):
self.error(err)
return
self._print_lines(lines, lineno, breaklist, self.curframe)
self._validate_file_mtime()
do_ll = do_longlist

def do_source(self, arg):
Expand Down
31 changes: 26 additions & 5 deletions Lib/reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,17 @@ def wrapper(self):
return decorating_function

class Repr:
_lookup = {
'tuple': 'builtins',
'list': 'builtins',
'array': 'array',
'set': 'builtins',
'frozenset': 'builtins',
'deque': 'collections',
'dict': 'builtins',
'str': 'builtins',
'int': 'builtins'
}

def __init__(
self, *, maxlevel=6, maxtuple=6, maxlist=6, maxarray=5, maxdict=4,
Expand All @@ -60,14 +71,24 @@ def repr(self, x):
return self.repr1(x, self.maxlevel)

def repr1(self, x, level):
typename = type(x).__name__
cls = type(x)
typename = cls.__name__

if ' ' in typename:
parts = typename.split()
typename = '_'.join(parts)
if hasattr(self, 'repr_' + typename):
return getattr(self, 'repr_' + typename)(x, level)
else:
return self.repr_instance(x, level)

method = getattr(self, 'repr_' + typename, None)
if method:
# not defined in this class
if typename not in self._lookup:
return method(x, level)
module = getattr(cls, '__module__', None)
# defined in this class and is the module intended
if module == self._lookup[typename]:
return method(x, level)

return self.repr_instance(x, level)

def _join(self, pieces, level):
if self.indent is None:
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -5761,6 +5761,8 @@ def test_resource_tracker_sigterm(self):
# Catchable signal (ignored by semaphore tracker)
self.check_resource_tracker_death(signal.SIGTERM, False)

@unittest.skipIf(sys.platform.startswith("netbsd"),
"gh-125620: Skip on NetBSD due to long wait for SIGKILL process termination.")
def test_resource_tracker_sigkill(self):
# Uncatchable signal.
self.check_resource_tracker_death(signal.SIGKILL, True)
Expand Down
14 changes: 14 additions & 0 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -1618,6 +1618,9 @@ def annotated(cls) -> int: pass

for method in (annotated, unannotated):
with self.subTest(deco=deco, method=method):
with self.assertRaises(AttributeError):
del unannotated.__annotations__

original_annotations = dict(method.__wrapped__.__annotations__)
self.assertNotIn('__annotations__', method.__dict__)
self.assertEqual(method.__annotations__, original_annotations)
Expand All @@ -1644,6 +1647,17 @@ def annotated(cls) -> int: pass
del method.__annotate__
self.assertIs(method.__annotate__, original_annotate)

def test_staticmethod_annotations_without_dict_access(self):
# gh-125017: this used to crash
class Spam:
def __new__(cls, x, y):
pass

self.assertEqual(Spam.__new__.__annotations__, {})
obj = Spam.__dict__['__new__']
self.assertIsInstance(obj, staticmethod)
self.assertEqual(obj.__annotations__, {})

@support.refcount_test
def test_refleaks_in_classmethod___init__(self):
gettotalrefcount = support.get_attribute(sys, 'gettotalrefcount')
Expand Down
6 changes: 6 additions & 0 deletions Lib/test/test_json/test_decode.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,12 @@ def test_float(self):
self.assertIsInstance(rval, float)
self.assertEqual(rval, 1.0)

def test_nonascii_digits_rejected(self):
# JSON specifies only ascii digits, see gh-125687
for num in ["1\uff10", "0.\uff10", "0e\uff10"]:
with self.assertRaises(self.JSONDecodeError):
self.loads(num)

def test_bytes(self):
self.assertEqual(self.loads(b"1"), 1)

Expand Down
10 changes: 10 additions & 0 deletions Lib/test/test_json/test_scanstring.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,11 @@ def test_bad_escapes(self):
'"\\u012z"',
'"\\u0x12"',
'"\\u0X12"',
'"\\u{0}"'.format("\uff10" * 4),
'"\\u 123"',
'"\\u-123"',
'"\\u+123"',
'"\\u1_23"',
'"\\ud834\\"',
'"\\ud834\\u"',
'"\\ud834\\ud"',
Expand All @@ -127,6 +132,11 @@ def test_bad_escapes(self):
'"\\ud834\\udd2z"',
'"\\ud834\\u0x20"',
'"\\ud834\\u0X20"',
'"\\ud834\\u{0}"'.format("\uff10" * 4),
'"\\ud834\\u 123"',
'"\\ud834\\u-123"',
'"\\ud834\\u+123"',
'"\\ud834\\u1_23"',
]
for s in bad_escapes:
with self.assertRaises(self.JSONDecodeError, msg=s):
Expand Down
19 changes: 19 additions & 0 deletions Lib/test/test_pdb.py
Original file line number Diff line number Diff line change
Expand Up @@ -3711,6 +3711,25 @@ def test_file_modified_after_execution(self):
self.assertIn("WARNING:", stdout)
self.assertIn("was edited", stdout)

def test_file_modified_and_immediately_restarted(self):
script = """
print("hello")
"""

# the time.sleep is needed for low-resolution filesystems like HFS+
commands = """
filename = $_frame.f_code.co_filename
f = open(filename, "w")
f.write("print('goodbye')")
import time; time.sleep(1)
f.close()
restart
"""

stdout, stderr = self.run_pdb_script(script, commands)
self.assertNotIn("WARNING:", stdout)
self.assertNotIn("was edited", stdout)

def test_file_modified_after_execution_with_multiple_instances(self):
# the time.sleep is needed for low-resolution filesystems like HFS+
script = """
Expand Down
44 changes: 44 additions & 0 deletions Lib/test/test_reprlib.py
Original file line number Diff line number Diff line change
Expand Up @@ -580,6 +580,50 @@ def test_invalid_indent(self):
with self.assertRaisesRegex(expected_error, expected_msg):
r.repr(test_object)

def test_shadowed_stdlib_array(self):
# Issue #113570: repr() should not be fooled by an array
class array:
def __repr__(self):
return "not array.array"

self.assertEqual(r(array()), "not array.array")

def test_shadowed_builtin(self):
# Issue #113570: repr() should not be fooled
# by a shadowed builtin function
class list:
def __repr__(self):
return "not builtins.list"

self.assertEqual(r(list()), "not builtins.list")

def test_custom_repr(self):
class MyRepr(Repr):

def repr_TextIOWrapper(self, obj, level):
if obj.name in {'<stdin>', '<stdout>', '<stderr>'}:
return obj.name
return repr(obj)

aRepr = MyRepr()
self.assertEqual(aRepr.repr(sys.stdin), "<stdin>")

def test_custom_repr_class_with_spaces(self):
class TypeWithSpaces:
pass

t = TypeWithSpaces()
type(t).__name__ = "type with spaces"
self.assertEqual(type(t).__name__, "type with spaces")

class MyRepr(Repr):
def repr_type_with_spaces(self, obj, level):
return "Type With Spaces"


aRepr = MyRepr()
self.assertEqual(aRepr.repr(t), "Type With Spaces")

def write_file(path, text):
with open(path, 'w', encoding='ASCII') as fp:
fp.write(text)
Expand Down
Loading

0 comments on commit 0e1bdf7

Please sign in to comment.