Skip to content
7 changes: 6 additions & 1 deletion Doc/library/dataclasses.rst
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ Module contents
:func:`!astuple` raises :exc:`TypeError` if *obj* is not a dataclass
instance.

.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None, decorator=dataclass)
.. function:: make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False, match_args=True, kw_only=False, slots=False, weakref_slot=False, module=None, qualname=None, decorator=dataclass)

Creates a new dataclass with name *cls_name*, fields as defined
in *fields*, base classes as given in *bases*, and initialized
Expand All @@ -434,6 +434,9 @@ Module contents
of the dataclass is set to that value.
By default, it is set to the module name of the caller.

If *qualname* is defined, the :attr:`~type.__qualname__` attribute of the dataclass
is set to that value. By default, it is set to the value passed to *cls_name*.

The *decorator* parameter is a callable that will be used to create the dataclass.
It should take the class object as a first argument and the same keyword arguments
as :deco:`dataclass`. By default, the :deco:`dataclass`
Expand Down Expand Up @@ -464,6 +467,8 @@ Module contents

.. versionadded:: 3.14
Added the *decorator* parameter.
.. versionadded:: next
Added the *qualname* parameter.

.. function:: replace(obj, /, **changes)

Expand Down
45 changes: 42 additions & 3 deletions Doc/library/inspect.rst
Original file line number Diff line number Diff line change
Expand Up @@ -424,24 +424,63 @@ attributes (see :ref:`import-mod-attrs` for module attributes):

Return ``True`` if the object is a bound method written in Python.

.. note::

.. function:: ispackage(object)
For example, given this class::

Return ``True`` if the object is a :term:`package`.
>>> class Greeter:
... def say_hello(self):
... print('hello!')

.. versionadded:: 3.14
A bound method (also known as an *instance method*) is created when
accessing ``say_hello`` (a :term:`function` defined in the
``Greeter`` namespace) through an instance of the ``Greeter`` class::

>>> instance = Greeter()

>>> instance.say_hello
<bound method Greeter.say_hello of <__main__.Greeter object ...>>
>>> ismethod(instance.say_hello)
True
>>> isfunction(instance.say_hello)
False

Accessing ``say_hello`` through the ``Greeter`` class will return the
function itself. For this function, :func:`ismethod` will return
``False``, but :func:`isfunction` will return ``True``::

>>> Greeter.say_hello
<function Greeter.say_hello at 0x7f7503854a90>
>>> ismethod(Greeter.say_hello)
False
>>> isfunction(Greeter.say_hello)
True

See :ref:`typesmethods` for details.


.. function:: isfunction(object)

Return ``True`` if the object is a Python function, which includes functions
created by a :term:`lambda` expression.

See the note for :func:`~inspect.ismethod` for an example.


.. function:: ispackage(object)

Return ``True`` if the object is a :term:`package`.

.. versionadded:: 3.14


.. function:: isgeneratorfunction(object)

Return ``True`` if the object is a Python generator function.

It also returns ``True`` for bound methods created from Python generator functions
(see :ref:`typesmethods` for more information).

.. versionchanged:: 3.8
Functions wrapped in :func:`functools.partial` now return ``True`` if the
wrapped function is a Python generator function.
Expand Down
14 changes: 13 additions & 1 deletion Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2549,7 +2549,8 @@ features:
Windows now handles a *mode* of ``0o700``.


.. function:: makedirs(name, mode=0o777, exist_ok=False)
.. function:: makedirs(name, mode=0o777, exist_ok=False, *, \
parent_mode=None)

.. index::
single: directory; creating
Expand All @@ -2567,6 +2568,12 @@ features:
If *exist_ok* is ``False`` (the default), a :exc:`FileExistsError` is
raised if the target directory already exists.

If *parent_mode* is not ``None``, it is used as the mode for any
newly-created, intermediate-level directories. Like *mode*, it is
combined with the process's umask value; see :ref:`the mkdir()
description <mkdir_modebits>`. Otherwise, intermediate directories are
created with the default mode, which is also subject to the umask.

.. note::

:func:`makedirs` will become confused if the path elements to create
Expand All @@ -2593,6 +2600,11 @@ features:
The *mode* argument no longer affects the file permission bits of
newly created intermediate-level directories.

.. versionadded:: 3.15
The *parent_mode* parameter. To match the behavior from Python 3.6 and
earlier (where *mode* was applied to all created directories), pass
``parent_mode=mode``.


.. function:: mkfifo(path, mode=0o666, *, dir_fd=None)

Expand Down
12 changes: 11 additions & 1 deletion Doc/library/pathlib.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1518,7 +1518,8 @@ Creating files and directories
:meth:`~Path.write_bytes` methods are often used to create files.


.. method:: Path.mkdir(mode=0o777, parents=False, exist_ok=False)
.. method:: Path.mkdir(mode=0o777, parents=False, exist_ok=False, *, \
parent_mode=None)

Create a new directory at this given path. If *mode* is given, it is
combined with the process's ``umask`` value to determine the file mode
Expand All @@ -1529,6 +1530,12 @@ Creating files and directories
as needed; they are created with the default permissions without taking
*mode* into account (mimicking the POSIX ``mkdir -p`` command).

If *parent_mode* is not ``None``, it is used as the mode for any
newly-created, intermediate-level directories when *parents* is true.
Like *mode*, it is combined with the process's ``umask`` value.
Otherwise, intermediate directories are created with the default
permissions (also subject to the umask).

If *parents* is false (the default), a missing parent raises
:exc:`FileNotFoundError`.

Expand All @@ -1542,6 +1549,9 @@ Creating files and directories
.. versionchanged:: 3.5
The *exist_ok* parameter was added.

.. versionadded:: 3.15
The *parent_mode* parameter.


.. method:: Path.symlink_to(target, target_is_directory=False)

Expand Down
8 changes: 8 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -1285,6 +1285,10 @@ os
glibc versions 2.28 and later.
(Contributed by Jeffrey Bosboom and Victor Stinner in :gh:`83714`.)

* :func:`os.makedirs` function now has a *parent_mode* parameter that allows
specifying the mode for intermediate directories. This can be used to match
the behavior from Python 3.6 and earlier by passing ``parent_mode=mode``.
(Contributed by Zackery Spytz and Gregory P. Smith in :gh:`86533`.)

os.path
-------
Expand Down Expand Up @@ -2057,6 +2061,10 @@ importlib.resources
pathlib
-------

* :meth:`pathlib.Path.mkdir` now has a *parent_mode* parameter that allows
specifying the mode for intermediate directories when ``parents=True``.
(Contributed by Gregory P. Smith in :gh:`86533`.)

* Removed deprecated :meth:`!pathlib.PurePath.is_reserved`.
Use :func:`os.path.isreserved` to detect reserved paths on Windows.
(Contributed by Nikita Sobolev in :gh:`133875`.)
Expand Down
8 changes: 7 additions & 1 deletion Lib/dataclasses.py
Original file line number Diff line number Diff line change
Expand Up @@ -1644,7 +1644,7 @@ def _astuple_inner(obj, tuple_factory):
def make_dataclass(cls_name, fields, *, bases=(), namespace=None, init=True,
repr=True, eq=True, order=False, unsafe_hash=False,
frozen=False, match_args=True, kw_only=False, slots=False,
weakref_slot=False, module=None, decorator=dataclass):
weakref_slot=False, module=None, qualname=None, decorator=dataclass):
"""Return a new dynamically created dataclass.

The dataclass name will be 'cls_name'. 'fields' is an iterable
Expand All @@ -1669,6 +1669,9 @@ class C(Base):

If module parameter is defined, the '__module__' attribute of the dataclass is
set to that value.

If qualname parameter is defined, the '__qualname__' attribute of the dataclass is set
to that value.
"""

if namespace is None:
Expand Down Expand Up @@ -1758,6 +1761,9 @@ def exec_body_callback(ns):
if module is not None:
cls.__module__ = module

if qualname:
cls.__qualname__ = qualname

# Apply the normal provided decorator.
cls = decorator(cls, init=init, repr=repr, eq=eq, order=order,
unsafe_hash=unsafe_hash, frozen=frozen,
Expand Down
2 changes: 1 addition & 1 deletion Lib/multiprocessing/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@
default_family = 'AF_INET'
families = ['AF_INET']

if hasattr(socket, 'AF_UNIX'):
if hasattr(socket, 'AF_UNIX') and sys.platform != 'cygwin':
default_family = 'AF_UNIX'
families += ['AF_UNIX']

Expand Down
4 changes: 3 additions & 1 deletion Lib/multiprocessing/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -326,8 +326,10 @@ def _check_available(self):
_concrete_contexts = {
'fork': ForkContext(),
'spawn': SpawnContext(),
'forkserver': ForkServerContext(),
}
if reduction.HAVE_SEND_HANDLE:
_concrete_contexts['forkserver'] = ForkServerContext()

# bpo-33725: running arbitrary code after fork() is no longer reliable
# on macOS since macOS 10.14 (Mojave). Use spawn by default instead.
# gh-84559: We changed everyones default to a thread safeish one in 3.14.
Expand Down
15 changes: 11 additions & 4 deletions Lib/os.py
Original file line number Diff line number Diff line change
Expand Up @@ -219,22 +219,29 @@ def _add(str, fn):
# Super directory utilities.
# (Inspired by Eric Raymond; the doc strings are mostly his)

def makedirs(name, mode=0o777, exist_ok=False):
"""makedirs(name [, mode=0o777][, exist_ok=False])
def makedirs(name, mode=0o777, exist_ok=False, *, parent_mode=None):
"""makedirs(name [, mode=0o777][, exist_ok=False][, parent_mode=None])

Super-mkdir; create a leaf directory and all intermediate ones. Works like
mkdir, except that any intermediate path segment (not just the rightmost)
will be created if it does not exist. If the target directory already
exists, raise an OSError if exist_ok is False. Otherwise no exception is
raised. This is recursive.
raised. If parent_mode is not None, it will be used as the mode for any
newly-created, intermediate-level directories. Otherwise, intermediate
directories are created with the default permissions (respecting umask).
This is recursive.

"""
head, tail = path.split(name)
if not tail:
head, tail = path.split(head)
if head and tail and not path.exists(head):
try:
makedirs(head, exist_ok=exist_ok)
if parent_mode is not None:
makedirs(head, mode=parent_mode, exist_ok=exist_ok,
parent_mode=parent_mode)
else:
makedirs(head, exist_ok=exist_ok)
except FileExistsError:
# Defeats race condition when another thread created the path
pass
Expand Down
8 changes: 6 additions & 2 deletions Lib/pathlib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -1204,7 +1204,7 @@ def touch(self, mode=0o666, exist_ok=True):
fd = os.open(self, flags, mode)
os.close(fd)

def mkdir(self, mode=0o777, parents=False, exist_ok=False):
def mkdir(self, mode=0o777, parents=False, exist_ok=False, *, parent_mode=None):
"""
Create a new directory at this given path.
"""
Expand All @@ -1213,7 +1213,11 @@ def mkdir(self, mode=0o777, parents=False, exist_ok=False):
except FileNotFoundError:
if not parents or self.parent == self:
raise
self.parent.mkdir(parents=True, exist_ok=True)
if parent_mode is not None:
self.parent.mkdir(mode=parent_mode, parents=True, exist_ok=True,
parent_mode=parent_mode)
else:
self.parent.mkdir(parents=True, exist_ok=True)
self.mkdir(mode, parents=False, exist_ok=exist_ok)
except OSError:
# Cannot rely on checking for EEXIST, since the operating system
Expand Down
2 changes: 1 addition & 1 deletion Lib/tarfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -1425,7 +1425,7 @@ def _proc_gnulong(self, tarfile):
# Remove redundant slashes from directories. This is to be consistent
# with frombuf().
if next.isdir():
next.name = next.name.removesuffix("/")
next.name = next.name.rstrip("/")

return next

Expand Down
7 changes: 5 additions & 2 deletions Lib/test/_test_multiprocessing.py
Original file line number Diff line number Diff line change
Expand Up @@ -6143,7 +6143,10 @@ def test_preload_resources(self):
@only_run_in_spawn_testsuite("avoids redundant testing.")
def test_mixed_startmethod(self):
# Fork-based locks cannot be used with spawned process
for process_method in ["spawn", "forkserver"]:
test_methods = ["spawn"]
if "forkserver" in multiprocessing.get_all_start_methods():
test_methods.append("forkserver")
for process_method in test_methods:
queue = multiprocessing.get_context("fork").Queue()
process_ctx = multiprocessing.get_context(process_method)
p = process_ctx.Process(target=close_queue, args=(queue,))
Expand All @@ -6152,7 +6155,7 @@ def test_mixed_startmethod(self):
p.start()

# non-fork-based locks can be used with all other start methods
for queue_method in ["spawn", "forkserver"]:
for queue_method in test_methods:
for process_method in multiprocessing.get_all_start_methods():
queue = multiprocessing.get_context(queue_method).Queue()
process_ctx = multiprocessing.get_context(process_method)
Expand Down
9 changes: 9 additions & 0 deletions Lib/test/test_dataclasses/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -5289,6 +5289,15 @@ class A:
self.assertEqual(len(fs), 1)
self.assertEqual(fs[0].name, 'x')

def test_makedataclass_with_qualname(self):
A = make_dataclass("A", ['a'], qualname='ClassA')
self.assertEqual(A.__qualname__, 'ClassA')

B = make_dataclass("B", ['b'], qualname='module1.ClassB')
self.assertEqual(B.__qualname__, 'module1.ClassB')

C = make_dataclass("C", ['c'])
self.assertEqual(C.__qualname__, 'C')

class TestZeroArgumentSuperWithSlots(unittest.TestCase):
def test_zero_argument_super(self):
Expand Down
7 changes: 6 additions & 1 deletion Lib/test/test_lazy_import/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import unittest
import tempfile
import os
import contextlib

from test import support
from test.support.script_helper import assert_python_ok
Expand Down Expand Up @@ -441,10 +442,14 @@ def tearDown(self):

def test_lazy_import_pkg(self):
"""lazy import of package submodule should load the package."""
import test.test_lazy_import.data.lazy_import_pkg
out = io.StringIO()

with contextlib.redirect_stdout(out):
import test.test_lazy_import.data.lazy_import_pkg

self.assertIn("test.test_lazy_import.data.pkg", sys.modules)
self.assertIn("test.test_lazy_import.data.pkg.bar", sys.modules)
self.assertIn("BAR_MODULE_LOADED", out.getvalue())

def test_lazy_import_pkg_cross_import(self):
"""Cross-imports within package should preserve lazy imports."""
Expand Down
12 changes: 3 additions & 9 deletions Lib/test/test_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -4065,11 +4065,7 @@ def test_config_reject_simple_queue_handler_multiprocessing_context(self):
# and thus cannot be used as a queue-like object (gh-124653)

import multiprocessing

if support.MS_WINDOWS:
start_methods = ['spawn']
else:
start_methods = ['spawn', 'fork', 'forkserver']
start_methods = multiprocessing.get_all_start_methods()

for start_method in start_methods:
with self.subTest(start_method=start_method):
Expand All @@ -4085,10 +4081,8 @@ def test_config_reject_simple_queue_handler_multiprocessing_context(self):
" assertions in multiprocessing")
def test_config_queue_handler_multiprocessing_context(self):
# regression test for gh-121723
if support.MS_WINDOWS:
start_methods = ['spawn']
else:
start_methods = ['spawn', 'fork', 'forkserver']
import multiprocessing
start_methods = multiprocessing.get_all_start_methods()
for start_method in start_methods:
with self.subTest(start_method=start_method):
ctx = multiprocessing.get_context(start_method)
Expand Down
2 changes: 1 addition & 1 deletion Lib/test/test_multiprocessing_forkserver/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
if support.PGO:
raise unittest.SkipTest("test is not helpful for PGO")

if sys.platform == "win32":
if sys.platform in ("win32", "cygwin"):
raise unittest.SkipTest("forkserver is not available on Windows")

if not support.has_fork_support:
Expand Down
Loading
Loading