Skip to content
13 changes: 13 additions & 0 deletions Doc/c-api/tuple.rst
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,19 @@ Tuple Objects
or ``NULL`` with an exception set on failure.


.. c:function:: PyObject* PyTuple_FromArray(PyObject *const *array, Py_ssize_t size)

Create a tuple of *size* items and copy references from *array* to the new
tuple.

*array* can be NULL if *size* is ``0``.

On success, return a new reference.
On error, set an exception and return ``NULL``.

.. versionadded:: next


.. c:function:: PyObject* PyTuple_Pack(Py_ssize_t n, ...)

Return a new tuple object of size *n*,
Expand Down
5 changes: 5 additions & 0 deletions Doc/library/os.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4592,6 +4592,8 @@ written in Python, such as a mail server's external command delivery program.
master end of the pseudo-terminal. For a more portable approach, use the
:mod:`pty` module. If an error occurs :exc:`OSError` is raised.

The returned file descriptor *fd* is :ref:`non-inheritable <fd_inheritance>`.

.. audit-event:: os.forkpty "" os.forkpty

.. warning::
Expand All @@ -4608,6 +4610,9 @@ written in Python, such as a mail server's external command delivery program.
threads, this now raises a :exc:`DeprecationWarning`. See the
longer explanation on :func:`os.fork`.

.. versionchanged:: next
The returned file descriptor is now made non-inheritable.

.. availability:: Unix, not WASI, not Android, not iOS.


Expand Down
5 changes: 5 additions & 0 deletions Doc/library/pty.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,14 @@ The :mod:`pty` module defines the following functions:
file descriptor connected to the child's controlling terminal (and also to the
child's standard input and output).

The returned file descriptor *fd* is :ref:`non-inheritable <fd_inheritance>`.

.. warning:: On macOS the use of this function is unsafe when mixed with using
higher-level system APIs, and that includes using :mod:`urllib.request`.

.. versionchanged:: next
The returned file descriptor is now made non-inheritable.


.. function:: openpty()

Expand Down
6 changes: 6 additions & 0 deletions Doc/library/signal.rst
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,12 @@ The variables defined in the :mod:`signal` module are:

.. availability:: Unix.

.. data:: SIGQUIT

Terminal quit signal.

.. availability:: Unix.

.. data:: SIGSEGV

Segmentation fault: invalid memory reference.
Expand Down
3 changes: 3 additions & 0 deletions Doc/library/sysconfig.rst
Original file line number Diff line number Diff line change
Expand Up @@ -382,6 +382,9 @@ Other functions

Examples of returned values:

- linux-x86_64
- linux-aarch64
- solaris-2.6-sun4u

Windows:

Expand Down
43 changes: 39 additions & 4 deletions Doc/using/configure.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,51 @@ Features and minimum versions required to build CPython:

* Support for threads.

* OpenSSL 1.1.1 is the minimum version and OpenSSL 3.0.18 is the recommended
minimum version for the :mod:`ssl` and :mod:`hashlib` extension modules.
To build optional modules:

* SQLite 3.15.2 for the :mod:`sqlite3` extension module.
* `libbz2 <https://sourceware.org/bzip2/>`_ for the :mod:`bz2` module.

* Tcl/Tk 8.5.12 for the :mod:`tkinter` module.
* `libb2 <https://github.com/BLAKE2/libb2>`_ (:ref:`BLAKE2 <hashlib-blake2>`),
used by :mod:`hashlib` module.

* `libffi <https://sourceware.org/libffi/>`_ 3.3.0 is the recommended
minimum version for the :mod:`ctypes` module.

* ``liblzma``, for the :mod:`lzma` module.

* `libmpdec <https://www.bytereef.org/mpdecimal/doc/libmpdec/>`_ 2.5.0
for the :mod:`decimal` module.

* ``libncurses`` or ``libncursesw``,
for the :mod:`curses` module.

* ``libpanel`` or ``libpanelw``,
for the :mod:`curses.panel` module.

* `libreadline <https://tiswww.case.edu/php/chet/readline/rltop.html>`_ or
`libedit <https://www.thrysoee.dk/editline/>`_
for the :mod:`readline` module.

* `libuuid <https://linux.die.net/man/3/libuuid>`_, for the :mod:`uuid` module.

* `OpenSSL <https://www.openssl.org/>`_ 1.1.1 is the minimum version and
OpenSSL 3.0.18 is the recommended minimum version for the
:mod:`ssl` and :mod:`hashlib` extension modules.

* `SQLite <https://sqlite.org/>`_ 3.15.2 for the :mod:`sqlite3` extension module.

* `Tcl/Tk <https://www.tcl-lang.org/>`_ 8.5.12 for the :mod:`tkinter` module.

* `zlib <https://www.zlib.net>`_ 1.1.4 is the reccomended minimum version for the
:mod:`zlib` module.

* `zstd <https://facebook.github.io/zstd/>`_ 1.4.5 is the minimum version for
the :mod:`compression.zstd` module.

For a full list of dependencies required to build all modules and how to install
them, see the
`devguide <https://devguide.python.org/getting-started/setup-building/#install-dependencies>`_.

* Autoconf 2.72 and aclocal 1.16.5 are required to regenerate the
:file:`configure` script.

Expand Down
3 changes: 3 additions & 0 deletions Doc/whatsnew/3.15.rst
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,9 @@ New features

(Contributed by Victor Stinner in :gh:`129813`.)

* Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array.
(Contributed by Victor Stinner in :gh:`111489`.)


Porting to Python 3.15
----------------------
Expand Down
4 changes: 4 additions & 0 deletions Include/cpython/tupleobject.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,7 @@ PyTuple_SET_ITEM(PyObject *op, Py_ssize_t index, PyObject *value) {
}
#define PyTuple_SET_ITEM(op, index, value) \
PyTuple_SET_ITEM(_PyObject_CAST(op), (index), _PyObject_CAST(value))

PyAPI_FUNC(PyObject*) PyTuple_FromArray(
PyObject *const *array,
Py_ssize_t size);
4 changes: 3 additions & 1 deletion Include/internal/pycore_tuple.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ extern PyStatus _PyTuple_InitGlobalObjects(PyInterpreterState *);

#define _PyTuple_ITEMS(op) _Py_RVALUE(_PyTuple_CAST(op)->ob_item)

PyAPI_FUNC(PyObject *)_PyTuple_FromArray(PyObject *const *, Py_ssize_t);
// Alias for backward compatibility
#define _PyTuple_FromArray PyTuple_FromArray

PyAPI_FUNC(PyObject *)_PyTuple_FromStackRefStealOnSuccess(const union _PyStackRef *, Py_ssize_t);
PyAPI_FUNC(PyObject *)_PyTuple_FromArraySteal(PyObject *const *, Py_ssize_t);

Expand Down
4 changes: 3 additions & 1 deletion Lib/inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1065,7 +1065,9 @@ def __init__(self):

def tokeneater(self, type, token, srowcol, erowcol, line):
if not self.started and not self.indecorator:
if type == tokenize.INDENT or token == "async":
if type in (tokenize.INDENT, tokenize.COMMENT, tokenize.NL):
pass
elif token == "async":
pass
# skip any decorators
elif token == "@":
Expand Down
2 changes: 1 addition & 1 deletion Lib/site.py
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ def _get_path(userbase):
if sys.platform == 'darwin' and sys._framework:
return f'{userbase}/lib/{implementation_lower}/site-packages'

return f'{userbase}/lib/python{version[0]}.{version[1]}{abi_thread}/site-packages'
return f'{userbase}/lib/{implementation_lower}{version[0]}.{version[1]}{abi_thread}/site-packages'


def getuserbase():
Expand Down
3 changes: 3 additions & 0 deletions Lib/sysconfig/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,9 @@ def get_platform():
isn't particularly important.

Examples of returned values:
linux-x86_64
linux-aarch64
solaris-2.6-sun4u


Windows:
Expand Down
22 changes: 22 additions & 0 deletions Lib/test/test_capi/test_tuple.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,28 @@ def test_tuple_new(self):
self.assertRaises(SystemError, tuple_new, PY_SSIZE_T_MIN)
self.assertRaises(MemoryError, tuple_new, PY_SSIZE_T_MAX)

def test_tuple_fromarray(self):
# Test PyTuple_FromArray()
tuple_fromarray = _testcapi.tuple_fromarray

tup = tuple([i] for i in range(5))
copy = tuple_fromarray(tup)
self.assertEqual(copy, tup)

tup = ()
copy = tuple_fromarray(tup)
self.assertIs(copy, tup)

copy = tuple_fromarray(NULL, 0)
self.assertIs(copy, ())

with self.assertRaises(SystemError):
tuple_fromarray(NULL, -1)
with self.assertRaises(SystemError):
tuple_fromarray(NULL, PY_SSIZE_T_MIN)
with self.assertRaises(MemoryError):
tuple_fromarray(NULL, PY_SSIZE_T_MAX)

def test_tuple_pack(self):
# Test PyTuple_Pack()
pack = _testlimitedcapi.tuple_pack
Expand Down
12 changes: 12 additions & 0 deletions Lib/test/test_inspect/inspect_fodder2.py
Original file line number Diff line number Diff line change
Expand Up @@ -388,4 +388,16 @@ def func383():
)
return ge385

# line 391
@decorator
# comment
def func394():
return 395

# line 397
@decorator

def func400():
return 401

pass # end of file
4 changes: 4 additions & 0 deletions Lib/test/test_inspect/test_inspect.py
Original file line number Diff line number Diff line change
Expand Up @@ -1223,6 +1223,10 @@ def test_generator_expression(self):
self.assertSourceEqual(next(mod2.ge377), 377, 380)
self.assertSourceEqual(next(mod2.func383()), 385, 388)

def test_comment_or_empty_line_after_decorator(self):
self.assertSourceEqual(mod2.func394, 392, 395)
self.assertSourceEqual(mod2.func400, 398, 401)


class TestNoEOL(GetSourceBase):
def setUp(self):
Expand Down
1 change: 1 addition & 0 deletions Lib/test/test_pty.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,7 @@ def test_fork(self):
os._exit(2)
os._exit(4)
else:
self.assertFalse(os.get_inheritable(master_fd))
debug("Waiting for child (%d) to finish." % pid)
# In verbose mode, we have to consume the debug output from the
# child or the child will block, causing this test to hang in the
Expand Down
2 changes: 2 additions & 0 deletions Lib/test/test_sysconfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,7 @@ def test_parse_makefile(self):
print("var8=$$(var3)", file=makefile)
print("var9=$(var10)(var3)", file=makefile)
print("var10=$$", file=makefile)
print("var11=$${ORIGIN}${var5}", file=makefile)
vars = _parse_makefile(TESTFN)
self.assertEqual(vars, {
'var1': 'ab42',
Expand All @@ -782,6 +783,7 @@ def test_parse_makefile(self):
'var8': '$(var3)',
'var9': '$(var3)',
'var10': '$',
'var11': '${ORIGIN}dollar$5',
})

def _test_parse_makefile_recursion(self):
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Add :c:func:`PyTuple_FromArray` to create a :class:`tuple` from an array.
Patch by Victor Stinner.
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
:func:`os.forkpty` does now make the returned file descriptor non-inheritable.
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Fix :func:`inspect.getsourcelines` for the case when a decorator is followed
by a comment or an empty line.
28 changes: 28 additions & 0 deletions Modules/_testcapi/tuple.c
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,40 @@ _check_tuple_item_is_NULL(PyObject *Py_UNUSED(module), PyObject *args)
}


static PyObject *
tuple_fromarray(PyObject* Py_UNUSED(module), PyObject *args)
{
PyObject *src;
Py_ssize_t size = UNINITIALIZED_SIZE;
if (!PyArg_ParseTuple(args, "O|n", &src, &size)) {
return NULL;
}
if (src != Py_None && !PyTuple_Check(src)) {
PyErr_SetString(PyExc_TypeError, "expect a tuple");
return NULL;
}

PyObject **items;
if (src != Py_None) {
items = &PyTuple_GET_ITEM(src, 0);
if (size == UNINITIALIZED_SIZE) {
size = PyTuple_GET_SIZE(src);
}
}
else {
items = NULL;
}
return PyTuple_FromArray(items, size);
}


static PyMethodDef test_methods[] = {
{"tuple_get_size", tuple_get_size, METH_O},
{"tuple_get_item", tuple_get_item, METH_VARARGS},
{"tuple_set_item", tuple_set_item, METH_VARARGS},
{"_tuple_resize", _tuple_resize, METH_VARARGS},
{"_check_tuple_item_is_NULL", _check_tuple_item_is_NULL, METH_VARARGS},
{"tuple_fromarray", tuple_fromarray, METH_VARARGS},
{NULL},
};

Expand Down
5 changes: 3 additions & 2 deletions Modules/clinic/posixmodule.c.h

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 9 additions & 1 deletion Modules/posixmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -9018,11 +9018,12 @@ Returns a tuple of (pid, master_fd).
Like fork(), return pid of 0 to the child process,
and pid of child to the parent process.
To both, return fd of newly opened pseudo-terminal.
The master_fd is non-inheritable.
[clinic start generated code]*/

static PyObject *
os_forkpty_impl(PyObject *module)
/*[clinic end generated code: output=60d0a5c7512e4087 input=f1f7f4bae3966010]*/
/*[clinic end generated code: output=60d0a5c7512e4087 input=24765e0f33275b3b]*/
{
int master_fd = -1;
pid_t pid;
Expand All @@ -9048,13 +9049,20 @@ os_forkpty_impl(PyObject *module)
} else {
/* parent: release the import lock. */
PyOS_AfterFork_Parent();
/* set O_CLOEXEC on master_fd */
if (_Py_set_inheritable(master_fd, 0, NULL) < 0) {
PyErr_FormatUnraisable("Exception ignored when setting master_fd "
"non-inheritable in forkpty()");
}

// After PyOS_AfterFork_Parent() starts the world to avoid deadlock.
if (warn_about_fork_with_threads("forkpty") < 0)
return NULL;
}
if (pid == -1) {
return posix_error();
}

return Py_BuildValue("(Ni)", PyLong_FromPid(pid), master_fd);
}
#endif /* HAVE_FORKPTY */
Expand Down
2 changes: 1 addition & 1 deletion Objects/tupleobject.c
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ tuple_item(PyObject *op, Py_ssize_t i)
}

PyObject *
_PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
PyTuple_FromArray(PyObject *const *src, Py_ssize_t n)
{
if (n == 0) {
return tuple_get_empty();
Expand Down
4 changes: 2 additions & 2 deletions Tools/c-analyzer/distutils/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@ def get_host_platform():
particularly important.

Examples of returned values:
linux-i586
linux-alpha (?)
linux-x86_64
linux-aarch64
solaris-2.6-sun4u

Windows will return one of:
Expand Down
Loading