Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Doc/library/curses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -988,6 +988,10 @@ the following methods and attributes:
window.getstr(y, x, n)

Read a bytes object from the user, with primitive line editing capacity.
The maximum value for *n* is 2047.

.. versionchanged:: 3.14
The maximum value for *n* was increased from 1023 to 2047.


.. method:: window.getyx()
Expand Down Expand Up @@ -1079,6 +1083,10 @@ the following methods and attributes:
current cursor position, or at *y*, *x* if specified. Attributes are stripped
from the characters. If *n* is specified, :meth:`instr` returns a string
at most *n* characters long (exclusive of the trailing NUL).
The maximum value for *n* is 2047.

.. versionchanged:: 3.14
The maximum value for *n* was increased from 1023 to 2047.


.. method:: window.is_linetouched(line)
Expand Down
14 changes: 9 additions & 5 deletions Doc/library/datetime.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1502,11 +1502,11 @@ Instance methods:
returned by :func:`time.time`.

Naive :class:`.datetime` instances are assumed to represent local
time and this method relies on the platform C :c:func:`mktime`
function to perform the conversion. Since :class:`.datetime`
supports wider range of values than :c:func:`mktime` on many
platforms, this method may raise :exc:`OverflowError` or :exc:`OSError`
for times far in the past or far in the future.
time and this method relies on platform C functions to perform
the conversion. Since :class:`.datetime` supports a wider range of
values than the platform C functions on many platforms, this
method may raise :exc:`OverflowError` or :exc:`OSError` for times
far in the past or far in the future.

For aware :class:`.datetime` instances, the return value is computed
as::
Expand All @@ -1519,6 +1519,10 @@ Instance methods:
The :meth:`timestamp` method uses the :attr:`.fold` attribute to
disambiguate the times during a repeated interval.

.. versionchanged:: 3.6
This method no longer relies on the platform C :c:func:`mktime`
function to perform conversions.

.. note::

There is no method to obtain the POSIX timestamp directly from a
Expand Down
7 changes: 4 additions & 3 deletions Doc/library/io.rst
Original file line number Diff line number Diff line change
Expand Up @@ -894,9 +894,10 @@ Text I/O

.. attribute:: buffer

The underlying binary buffer (a :class:`BufferedIOBase` instance) that
:class:`TextIOBase` deals with. This is not part of the
:class:`TextIOBase` API and may not exist in some implementations.
The underlying binary buffer (a :class:`BufferedIOBase`
or :class:`RawIOBase` instance) that :class:`TextIOBase` deals with.
This is not part of the :class:`TextIOBase` API and may not exist
in some implementations.

.. method:: detach()

Expand Down
18 changes: 14 additions & 4 deletions Doc/whatsnew/3.14.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1200,6 +1200,9 @@ ctypes
making it a :term:`generic type`.
(Contributed by Brian Schubert in :gh:`132168`.)

* :mod:`ctypes` now supports :term:`free-threading builds <free threading>`.
(Contributed by Kumar Aditya and Peter Bierma in :gh:`127945`.)

curses
------

Expand Down Expand Up @@ -1997,11 +2000,19 @@ Optimizations
asyncio
-------

* :mod:`asyncio` now uses double linked list implementation for native tasks
which speeds up execution by 10% on standard pyperformance benchmarks and
reduces memory usage.
* :mod:`asyncio` has a new per-thread double linked list implementation internally for
:class:`native tasks <asyncio.Task>` which speeds up execution by 10-20% on standard
pyperformance benchmarks and reduces memory usage.
This enables external introspection tools such as
:ref:`python -m asyncio pstree <whatsnew314-asyncio-introspection>`
to introspect the call graph of asyncio tasks running in all threads.
(Contributed by Kumar Aditya in :gh:`107803`.)

* :mod:`asyncio` has first class support for :term:`free-threading builds <free threading>`.
This enables parallel execution of multiple event loops across different threads and scales
linearly with the number of threads.
(Contributed by Kumar Aditya in :gh:`128002`.)

* :mod:`asyncio` has new utility functions for introspecting and printing
the program's call graph: :func:`asyncio.capture_call_graph` and
:func:`asyncio.print_call_graph`.
Expand Down Expand Up @@ -2083,7 +2094,6 @@ Deprecated
* :class:`asyncio.WindowsProactorEventLoopPolicy`
* :func:`asyncio.get_event_loop_policy`
* :func:`asyncio.set_event_loop_policy`
* :func:`asyncio.set_event_loop`

Users should use :func:`asyncio.run` or :class:`asyncio.Runner` with
*loop_factory* to use the desired event loop implementation.
Expand Down
15 changes: 12 additions & 3 deletions Lib/_pyrepl/_module_completer.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,10 @@ def find_modules(self, path: str, prefix: str) -> list[str]:
def _find_modules(self, path: str, prefix: str) -> list[str]:
if not path:
# Top-level import (e.g. `import foo<tab>`` or `from foo<tab>`)`
builtin_modules = [name for name in sys.builtin_module_names if name.startswith(prefix)]
third_party_modules = [name for _, name, _ in self.global_cache if name.startswith(prefix)]
builtin_modules = [name for name in sys.builtin_module_names
if self.is_suggestion_match(name, prefix)]
third_party_modules = [module.name for module in self.global_cache
if self.is_suggestion_match(module.name, prefix)]
return sorted(builtin_modules + third_party_modules)

if path.startswith('.'):
Expand All @@ -98,7 +100,14 @@ def _find_modules(self, path: str, prefix: str) -> list[str]:
if mod_info.ispkg and mod_info.name == segment]
modules = self.iter_submodules(modules)
return [module.name for module in modules
if module.name.startswith(prefix)]
if self.is_suggestion_match(module.name, prefix)]

def is_suggestion_match(self, module_name: str, prefix: str) -> bool:
if prefix:
return module_name.startswith(prefix)
# For consistency with attribute completion, which
# does not suggest private attributes unless requested.
return not module_name.startswith("_")

def iter_submodules(self, parent_modules: list[pkgutil.ModuleInfo]) -> Iterator[pkgutil.ModuleInfo]:
"""Iterate over all submodules of the given parent modules."""
Expand Down
2 changes: 2 additions & 0 deletions Lib/_pyrepl/simple_interact.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
import sys
import code
import warnings
import errno

from .readline import _get_reader, multiline_input, append_history_file

Expand Down Expand Up @@ -153,6 +154,7 @@ def maybe_run_command(statement: str) -> bool:
append_history_file()
except (FileNotFoundError, PermissionError, OSError) as e:
warnings.warn(f"failed to open the history file for writing: {e}")

input_n += 1
except KeyboardInterrupt:
r = _get_reader()
Expand Down
45 changes: 23 additions & 22 deletions Lib/_strptime.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,7 +302,7 @@ def __init__(self, locale_time=None):
# W is set below by using 'U'
'y': r"(?P<y>\d\d)",
'Y': r"(?P<Y>\d\d\d\d)",
'z': r"(?P<z>[+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?|(?-i:Z))",
'z': r"(?P<z>([+-]\d\d:?[0-5]\d(:?[0-5]\d(\.\d{1,6})?)?)|(?-i:Z))?",
'A': self.__seqToRE(self.locale_time.f_weekday, 'A'),
'a': self.__seqToRE(self.locale_time.a_weekday, 'a'),
'B': self.__seqToRE(self.locale_time.f_month[1:], 'B'),
Expand Down Expand Up @@ -548,27 +548,28 @@ def _strptime(data_string, format="%a %b %d %H:%M:%S %Y"):
iso_week = int(found_dict['V'])
elif group_key == 'z':
z = found_dict['z']
if z == 'Z':
gmtoff = 0
else:
if z[3] == ':':
z = z[:3] + z[4:]
if len(z) > 5:
if z[5] != ':':
msg = f"Inconsistent use of : in {found_dict['z']}"
raise ValueError(msg)
z = z[:5] + z[6:]
hours = int(z[1:3])
minutes = int(z[3:5])
seconds = int(z[5:7] or 0)
gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
gmtoff_remainder = z[8:]
# Pad to always return microseconds.
gmtoff_remainder_padding = "0" * (6 - len(gmtoff_remainder))
gmtoff_fraction = int(gmtoff_remainder + gmtoff_remainder_padding)
if z.startswith("-"):
gmtoff = -gmtoff
gmtoff_fraction = -gmtoff_fraction
if z:
if z == 'Z':
gmtoff = 0
else:
if z[3] == ':':
z = z[:3] + z[4:]
if len(z) > 5:
if z[5] != ':':
msg = f"Inconsistent use of : in {found_dict['z']}"
raise ValueError(msg)
z = z[:5] + z[6:]
hours = int(z[1:3])
minutes = int(z[3:5])
seconds = int(z[5:7] or 0)
gmtoff = (hours * 60 * 60) + (minutes * 60) + seconds
gmtoff_remainder = z[8:]
# Pad to always return microseconds.
gmtoff_remainder_padding = "0" * (6 - len(gmtoff_remainder))
gmtoff_fraction = int(gmtoff_remainder + gmtoff_remainder_padding)
if z.startswith("-"):
gmtoff = -gmtoff
gmtoff_fraction = -gmtoff_fraction
elif group_key == 'Z':
# Since -1 is default value only need to worry about setting tz if
# it can be something other than -1.
Expand Down
79 changes: 79 additions & 0 deletions Lib/pprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,49 @@ def _pprint_ordered_dict(self, object, stream, indent, allowance, context, level

_dispatch[_collections.OrderedDict.__repr__] = _pprint_ordered_dict

def _pprint_dict_view(self, object, stream, indent, allowance, context, level):
"""Pretty print dict views (keys, values, items)."""
if isinstance(object, self._dict_items_view):
key = _safe_tuple
else:
key = _safe_key
write = stream.write
write(object.__class__.__name__ + '([')
if self._indent_per_level > 1:
write((self._indent_per_level - 1) * ' ')
length = len(object)
if length:
if self._sort_dicts:
entries = sorted(object, key=key)
else:
entries = object
self._format_items(entries, stream, indent, allowance + 1,
context, level)
write('])')

def _pprint_mapping_abc_view(self, object, stream, indent, allowance, context, level):
"""Pretty print mapping views from collections.abc."""
write = stream.write
write(object.__class__.__name__ + '(')
# Dispatch formatting to the view's _mapping
self._format(object._mapping, stream, indent, allowance, context, level)
write(')')

_dict_keys_view = type({}.keys())
_dispatch[_dict_keys_view.__repr__] = _pprint_dict_view

_dict_values_view = type({}.values())
_dispatch[_dict_values_view.__repr__] = _pprint_dict_view

_dict_items_view = type({}.items())
_dispatch[_dict_items_view.__repr__] = _pprint_dict_view

_dispatch[_collections.abc.MappingView.__repr__] = _pprint_mapping_abc_view

_view_reprs = {cls.__repr__ for cls in
(_dict_keys_view, _dict_values_view, _dict_items_view,
_collections.abc.MappingView)}

def _pprint_list(self, object, stream, indent, allowance, context, level):
stream.write('[')
self._format_items(object, stream, indent, allowance + 1,
Expand Down Expand Up @@ -610,6 +653,42 @@ def _safe_repr(self, object, context, maxlevels, level):
del context[objid]
return "{%s}" % ", ".join(components), readable, recursive

if issubclass(typ, _collections.abc.MappingView) and r in self._view_reprs:
objid = id(object)
if maxlevels and level >= maxlevels:
return "{...}", False, objid in context
if objid in context:
return _recursion(object), False, True
key = _safe_key
if issubclass(typ, (self._dict_items_view, _collections.abc.ItemsView)):
key = _safe_tuple
if hasattr(object, "_mapping"):
# Dispatch formatting to the view's _mapping
mapping_repr, readable, recursive = self.format(
object._mapping, context, maxlevels, level)
return (typ.__name__ + '(%s)' % mapping_repr), readable, recursive
elif hasattr(typ, "_mapping"):
# We have a view that somehow has lost its type's _mapping, raise
# an error by calling repr() instead of failing cryptically later
return repr(object), True, False
if self._sort_dicts:
object = sorted(object, key=key)
context[objid] = 1
readable = True
recursive = False
components = []
append = components.append
level += 1
for val in object:
vrepr, vreadable, vrecur = self.format(
val, context, maxlevels, level)
append(vrepr)
readable = readable and vreadable
if vrecur:
recursive = True
del context[objid]
return typ.__name__ + '([%s])' % ", ".join(components), readable, recursive

if (issubclass(typ, list) and r is list.__repr__) or \
(issubclass(typ, tuple) and r is tuple.__repr__):
if issubclass(typ, list):
Expand Down
8 changes: 7 additions & 1 deletion Lib/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@
import _sitebuiltins
import _io as io
import stat
import errno

# Prefixes for site-packages; add additional prefixes like /usr/local here
PREFIXES = [sys.prefix, sys.exec_prefix]
Expand Down Expand Up @@ -578,10 +579,15 @@ def register_readline():
def write_history():
try:
readline_module.write_history_file(history)
except (FileNotFoundError, PermissionError):
except FileNotFoundError, PermissionError:
# home directory does not exist or is not writable
# https://bugs.python.org/issue19891
pass
except OSError:
if errno.EROFS:
pass # gh-128066: read-only file system
else:
raise

atexit.register(write_history)

Expand Down
11 changes: 11 additions & 0 deletions Lib/test/datetimetester.py
Original file line number Diff line number Diff line change
Expand Up @@ -2972,6 +2972,17 @@ def test_strptime_leap_year(self):
with self._assertNotWarns(DeprecationWarning):
self.theclass.strptime('02-29,2024', '%m-%d,%Y')

def test_strptime_z_empty(self):
for directive in ('z',):
string = '2025-04-25 11:42:47'
format = f'%Y-%m-%d %H:%M:%S%{directive}'
target = self.theclass(2025, 4, 25, 11, 42, 47)
with self.subTest(string=string,
format=format,
target=target):
result = self.theclass.strptime(string, format)
self.assertEqual(result, target)

def test_more_timetuple(self):
# This tests fields beyond those tested by the TestDate.test_timetuple.
t = self.theclass(2004, 12, 31, 6, 22, 33)
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_builtin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,7 @@ def test_filter_pickle(self):
self.check_iter_pickle(f1, list(f2), proto)

@support.skip_wasi_stack_overflow()
@support.skip_emscripten_stack_overflow()
@support.requires_resource('cpu')
def test_filter_dealloc(self):
# Tests recursive deallocation of nested filter objects using the
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_capi/test_misc.py
Original file line number Diff line number Diff line change
Expand Up @@ -412,10 +412,12 @@ def test_trashcan_subclass(self):
L = MyList((L,))

@support.requires_resource('cpu')
@support.skip_emscripten_stack_overflow()
def test_trashcan_python_class1(self):
self.do_test_trashcan_python_class(list)

@support.requires_resource('cpu')
@support.skip_emscripten_stack_overflow()
def test_trashcan_python_class2(self):
from _testcapi import MyList
self.do_test_trashcan_python_class(MyList)
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_descr.py
Original file line number Diff line number Diff line change
Expand Up @@ -4523,6 +4523,7 @@ class Oops(object):
del o

@support.skip_wasi_stack_overflow()
@support.skip_emscripten_stack_overflow()
@support.requires_resource('cpu')
def test_wrapper_segfault(self):
# SF 927248: deeply nested wrappers could cause stack overflow
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -1429,6 +1429,7 @@ def g():
self.assertIn("maximum recursion depth exceeded", str(exc))

@support.skip_wasi_stack_overflow()
@support.skip_emscripten_stack_overflow()
@cpython_only
@support.requires_resource('cpu')
def test_trashcan_recursion(self):
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -572,7 +572,7 @@ def do_test(test, obj, abilities):
for [test, abilities] in tests:
with self.subTest(test):
if test == pipe_writer and not threading_helper.can_start_thread:
skipTest()
self.skipTest("Need threads")
with test() as obj:
do_test(test, obj, abilities)

Expand Down
Loading
Loading