Skip to content
38 changes: 19 additions & 19 deletions .github/CODEOWNERS
Original file line number Diff line number Diff line change
Expand Up @@ -260,33 +260,33 @@ Include/pyhash.h @gpshead @picnixz
Python/pyhash.c @gpshead @picnixz

# The import system (including importlib)
**/*import* @brettcannon @ericsnowcurrently @ncoghlan @warsaw
Python/import.c @brettcannon @ericsnowcurrently @ncoghlan @warsaw @kumaraditya303
**/*import* @brettcannon @ericsnowcurrently @ncoghlan @warsaw @FFY00
Python/import.c @brettcannon @ericsnowcurrently @ncoghlan @warsaw @FFY00 @kumaraditya303
**/*freeze* @ericsnowcurrently
**/*frozen* @ericsnowcurrently
**/*modsupport* @ericsnowcurrently
**/*modulefinder* @ericsnowcurrently
**/*modulefinder* @ericsnowcurrently @FFY00
**/*moduleobject* @ericsnowcurrently
**/*multiphase* @ericsnowcurrently
**/*pkgutil* @ericsnowcurrently
**/*pkgutil* @ericsnowcurrently @FFY00
**/*pythonrun* @ericsnowcurrently
**/*runpy* @ericsnowcurrently
**/*runpy* @ericsnowcurrently @FFY00
**/*singlephase* @ericsnowcurrently
Doc/c-api/module.rst @ericsnowcurrently
Lib/test/test_module/ @ericsnowcurrently
Python/dynload_*.c @ericsnowcurrently
Python/dynload_*.c @ericsnowcurrently @FFY00

# Initialisation
**/*initconfig* @ericsnowcurrently
**/*pathconfig* @ericsnowcurrently
**/*preconfig* @ericsnowcurrently
**/*initconfig* @ericsnowcurrently @FFY00
**/*pathconfig* @ericsnowcurrently @FFY00
**/*preconfig* @ericsnowcurrently @FFY00
Doc/library/sys_path_init.rst @FFY00
Doc/c-api/init_config.rst @FFY00

# Interpreter main program
Modules/main.c @ericsnowcurrently
Programs/_bootstrap_python.c @ericsnowcurrently
Programs/python.c @ericsnowcurrently
Modules/main.c @ericsnowcurrently @FFY00
Programs/_bootstrap_python.c @ericsnowcurrently @FFY00
Programs/python.c @ericsnowcurrently @FFY00

# JIT
.github/workflows/jit.yml @savannahostrowski
Expand Down Expand Up @@ -316,8 +316,8 @@ Tools/peg_generator/ @pablogsal @lysnikolaou

# Runtime state/lifecycle
**/*gil* @ericsnowcurrently
**/*pylifecycle* @ericsnowcurrently @ZeroIntensity
**/*pystate* @ericsnowcurrently @ZeroIntensity
**/*pylifecycle* @ericsnowcurrently @ZeroIntensity @FFY00
**/*pystate* @ericsnowcurrently @ZeroIntensity @FFY00
Include/internal/pycore_*_init.h @ericsnowcurrently
Include/internal/pycore_*_state.h @ericsnowcurrently
Include/internal/pycore_atexit.h @ericsnowcurrently
Expand Down Expand Up @@ -505,13 +505,13 @@ Lib/idlelib/ @terryjreedy
Lib/turtledemo/ @terryjreedy

# importlib.metadata
Doc/library/importlib.metadata.rst @jaraco @warsaw
Lib/importlib/metadata/ @jaraco @warsaw
Lib/test/test_importlib/metadata/ @jaraco @warsaw
Doc/library/importlib.metadata.rst @jaraco @warsaw @FFY00
Lib/importlib/metadata/ @jaraco @warsaw @FFY00
Lib/test/test_importlib/metadata/ @jaraco @warsaw @FFY00

# importlib.resources
Doc/library/importlib.resources.abc.rst @jaraco @warsaw
Doc/library/importlib.resources.rst @jaraco @warsaw
Doc/library/importlib.resources.abc.rst @jaraco @warsaw @FFY00
Doc/library/importlib.resources.rst @jaraco @warsaw @FFY00
Lib/importlib/resources/ @jaraco @warsaw @FFY00
Lib/test/test_importlib/resources/ @jaraco @warsaw @FFY00

Expand Down
12 changes: 12 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2409,6 +2409,10 @@ features:
.. versionchanged:: 3.6
Accepts a :term:`path-like object`.

.. versionchanged:: next
``os.listdir(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
listing the current directory.


.. function:: listdrives()

Expand Down Expand Up @@ -2939,6 +2943,10 @@ features:
.. versionchanged:: 3.7
Added support for :ref:`file descriptors <path_fd>` on Unix.

.. versionchanged:: next
``os.scandir(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
listing the current directory.


.. class:: DirEntry

Expand Down Expand Up @@ -4574,6 +4582,10 @@ These functions are all available on Linux only.
.. versionchanged:: 3.6
Accepts a :term:`path-like object`.

.. versionchanged:: next
``os.listxattr(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
listing extended attributes of the current directory.


.. function:: removexattr(path, attribute, *, follow_symlinks=True)

Expand Down
2 changes: 1 addition & 1 deletion Doc/library/stdtypes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4536,7 +4536,7 @@ copying.
types such as :class:`bytes` and :class:`bytearray`, an element is a single
byte, but other types such as :class:`array.array` may have bigger elements.

``len(view)`` is equal to the length of :class:`~memoryview.tolist`, which
``len(view)`` is equal to the length of :meth:`~memoryview.tolist`, which
is the nested list representation of the view. If ``view.ndim = 1``,
this is equal to the number of elements in the view.

Expand Down
37 changes: 37 additions & 0 deletions Doc/library/wave.rst
Original file line number Diff line number Diff line change
Expand Up @@ -181,11 +181,21 @@ Wave_write Objects
Set the number of channels.


.. method:: getnchannels()

Return the number of channels.


.. method:: setsampwidth(n)

Set the sample width to *n* bytes.


.. method:: getsampwidth()

Return the sample width in bytes.


.. method:: setframerate(n)

Set the frame rate to *n*.
Expand All @@ -195,26 +205,53 @@ Wave_write Objects
integer.


.. method:: getframerate()

Return the frame rate.


.. method:: setnframes(n)

Set the number of frames to *n*. This will be changed later if the number
of frames actually written is different (this update attempt will
raise an error if the output stream is not seekable).


.. method:: getnframes()

Return the number of audio frames written so far.


.. method:: setcomptype(type, name)

Set the compression type and description. At the moment, only compression type
``NONE`` is supported, meaning no compression.


.. method:: getcomptype()

Return the compression type (``'NONE'``).


.. method:: getcompname()

Return the human-readable compression type name.


.. method:: setparams(tuple)

The *tuple* should be ``(nchannels, sampwidth, framerate, nframes, comptype,
compname)``, with values valid for the ``set*()`` methods. Sets all
parameters.


.. method:: getparams()

Return a :func:`~collections.namedtuple`
``(nchannels, sampwidth, framerate, nframes, comptype, compname)``
containing the current output parameters.


.. method:: tell()

Return current position in the file, with the same disclaimer for the
Expand Down
52 changes: 52 additions & 0 deletions Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,58 @@ def test_partial_genericalias(self):
self.assertEqual(alias.__args__, (int,))
self.assertEqual(alias.__parameters__, ())

# GH-144475: Tests that the partial object does not change until repr finishes
def test_repr_safety_against_reentrant_mutation(self):
g_partial = None

class Function:
def __init__(self, name):
self.name = name

def __call__(self):
return None

def __repr__(self):
return f"Function({self.name})"

class EvilObject:
def __init__(self):
self.triggered = False

def __repr__(self):
if not self.triggered and g_partial is not None:
self.triggered = True
new_args_tuple = (None,)
new_keywords_dict = {"keyword": None}
new_tuple_state = (Function("new_function"), new_args_tuple, new_keywords_dict, None)
g_partial.__setstate__(new_tuple_state)
gc.collect()
return f"EvilObject"

trigger = EvilObject()
func = Function("old_function")

g_partial = functools.partial(func, None, trigger=trigger)
self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), None, trigger=EvilObject)")

trigger.triggered = False
g_partial = functools.partial(func, trigger, arg=None)
self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, arg=None)")


trigger.triggered = False
g_partial = functools.partial(func, trigger, None)
self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, None)")

trigger.triggered = False
g_partial = functools.partial(func, trigger=trigger, arg=None)
self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), trigger=EvilObject, arg=None)")

trigger.triggered = False
g_partial = functools.partial(func, trigger, None, None, None, None, arg=None)
self.assertEqual(repr(g_partial),"functools.partial(Function(old_function), EvilObject, None, None, None, None, arg=None)")



@unittest.skipUnless(c_functools, 'requires the C _functools module')
class TestPartialC(TestPartial, unittest.TestCase):
Expand Down
11 changes: 11 additions & 0 deletions Lib/test/test_lazy_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
import os

from test import support
from test.support.script_helper import assert_python_ok

try:
import _testcapi
Expand Down Expand Up @@ -219,6 +220,16 @@ def test_lazy_import_type_cant_construct(self):
"""LazyImportType should not be directly constructible."""
self.assertRaises(TypeError, types.LazyImportType, {}, "module")

@support.requires_subprocess()
def test_lazy_import_type_attributes_accessible(self):
"""Check that static PyLazyImport_Type is initialized at startup."""
code = textwrap.dedent("""
lazy import json
print(globals()["json"].resolve)
""")
proc = assert_python_ok("-c", code)
self.assertIn(b"<built-in method resolve of lazy_import object at", proc.out)


class SyntaxRestrictionTests(unittest.TestCase):
"""Tests for syntax restrictions on lazy imports."""
Expand Down
51 changes: 51 additions & 0 deletions Lib/test/test_os/test_os.py
Original file line number Diff line number Diff line change
Expand Up @@ -2784,10 +2784,61 @@ def test_fpathconf_bad_fd(self):
'musl fpathconf ignores the file descriptor and returns a constant',
)
def test_pathconf_negative_fd_uses_fd_semantics(self):
if os.pathconf not in os.supports_fd:
self.skipTest('needs fpathconf()')

with self.assertRaises(OSError) as ctx:
os.pathconf(-1, 1)
self.assertEqual(ctx.exception.errno, errno.EBADF)

@support.subTests("fd", [-1, -5])
def test_negative_fd_ebadf(self, fd):
tests = [(os.stat, fd)]
if hasattr(os, "statx"):
tests.append((os.statx, fd, 0))
if os.chdir in os.supports_fd:
tests.append((os.chdir, fd))
if os.chmod in os.supports_fd:
tests.append((os.chmod, fd, 0o777))
if hasattr(os, "chown") and os.chown in os.supports_fd:
tests.append((os.chown, fd, 0, 0))
if os.listdir in os.supports_fd:
tests.append((os.listdir, fd))
if os.utime in os.supports_fd:
tests.append((os.utime, fd, (0, 0)))
if hasattr(os, "truncate") and os.truncate in os.supports_fd:
tests.append((os.truncate, fd, 0))
if hasattr(os, 'statvfs') and os.statvfs in os.supports_fd:
tests.append((os.statvfs, fd))
if hasattr(os, "setxattr"):
tests.append((os.getxattr, fd, b"user.test"))
tests.append((os.setxattr, fd, b"user.test", b"1"))
tests.append((os.removexattr, fd, b"user.test"))
tests.append((os.listxattr, fd))
if os.scandir in os.supports_fd:
tests.append((os.scandir, fd))

for func, *args in tests:
with self.subTest(func=func, args=args):
with self.assertRaises(OSError) as ctx:
func(*args)
self.assertEqual(ctx.exception.errno, errno.EBADF)

if hasattr(os, "execve") and os.execve in os.supports_fd:
# glibc fails with EINVAL, musl fails with EBADF
with self.assertRaises(OSError) as ctx:
os.execve(fd, [sys.executable, "-c", "pass"], os.environ)
self.assertIn(ctx.exception.errno, (errno.EBADF, errno.EINVAL))

if support.MS_WINDOWS:
import nt
self.assertFalse(nt._path_exists(fd))
self.assertFalse(nt._path_lexists(fd))
self.assertFalse(nt._path_isdir(fd))
self.assertFalse(nt._path_isfile(fd))
self.assertFalse(nt._path_islink(fd))
self.assertFalse(nt._path_isjunction(fd))

@unittest.skipUnless(hasattr(os, 'ftruncate'), 'test needs os.ftruncate()')
def test_ftruncate(self):
self.check(os.truncate, 0)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Fix reference leaks in various unusual error scenarios.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Document missing public :class:`wave.Wave_write` getter methods.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
Calling :func:`repr` on :func:`functools.partial` is now safer
when the partial object's internal attributes are replaced while
the string representation is being generated.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
``os.listdir(-1)`` and ``os.scandir(-1)`` now fail with
``OSError(errno.EBADF)`` rather than listing the current directory.
``os.listxattr(-1)`` now fails with ``OSError(errno.EBADF)`` rather than
listing extended attributes of the current directory. Patch by Victor
Stinner.
Loading
Loading