Python-checkins
Threads by month
- ----- 2025 -----
- February
- January
- ----- 2024 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2023 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2022 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2021 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2020 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2019 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2018 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2017 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2016 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2015 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2014 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2013 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2012 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2011 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2010 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2009 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2008 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2007 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2006 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2005 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2004 -----
- December
- November
- October
- September
- August
- July
- June
- May
- April
- March
- February
- January
- ----- 2003 -----
- December
- November
- October
- September
- August
January 2025
- 1 participants
- 705 discussions
Jan. 31, 2025
https://github.com/python/cpython/commit/e3eba8ce266f90d9f8faeb5b2b4b64e561…
commit: e3eba8ce266f90d9f8faeb5b2b4b64e56110bd2a
branch: main
author: Michael Osipov <michael.osipov(a)innomotics.com>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-31T10:02:45+01:00
summary:
gh-129393: Make 'sys.platform' return "freebsd" only on FreeBSD (#129394)
Make 'sys.platform' return "freebsd" only on FreeBSD without major version.
files:
A Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst
M Doc/library/sys.rst
M Doc/whatsnew/3.14.rst
M configure
M configure.ac
diff --git a/Doc/library/sys.rst b/Doc/library/sys.rst
index 151fd60532048a..5a096235713319 100644
--- a/Doc/library/sys.rst
+++ b/Doc/library/sys.rst
@@ -1422,6 +1422,7 @@ always available. Unless explicitly noted otherwise, all variables are read-only
AIX ``'aix'``
Android ``'android'``
Emscripten ``'emscripten'``
+ FreeBSD ``'freebsd'``
iOS ``'ios'``
Linux ``'linux'``
macOS ``'darwin'``
@@ -1432,12 +1433,12 @@ always available. Unless explicitly noted otherwise, all variables are read-only
On Unix systems not listed in the table, the value is the lowercased OS name
as returned by ``uname -s``, with the first part of the version as returned by
- ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, *at the time
- when Python was built*. Unless you want to test for a specific system
- version, it is therefore recommended to use the following idiom::
+ ``uname -r`` appended, e.g. ``'sunos5'``, *at the time when Python was built*.
+ Unless you want to test for a specific system version, it is therefore
+ recommended to use the following idiom::
- if sys.platform.startswith('freebsd'):
- # FreeBSD-specific code here...
+ if sys.platform.startswith('sunos'):
+ # SunOS-specific code here...
.. versionchanged:: 3.3
On Linux, :data:`sys.platform` doesn't contain the major version anymore.
@@ -1451,6 +1452,10 @@ always available. Unless explicitly noted otherwise, all variables are read-only
On Android, :data:`sys.platform` now returns ``'android'`` rather than
``'linux'``.
+ .. versionchanged:: 3.14
+ On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore.
+ It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``.
+
.. seealso::
:data:`os.name` has a coarser granularity. :func:`os.uname` gives
diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst
index 8d4cb94ae2d805..484e306335829a 100644
--- a/Doc/whatsnew/3.14.rst
+++ b/Doc/whatsnew/3.14.rst
@@ -649,6 +649,9 @@ sys
which only exists in specialized builds of Python, may now return objects
from other interpreters than the one it's called in.
+* On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore.
+ It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``.
+
sys.monitoring
--------------
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst
new file mode 100644
index 00000000000000..e36e6f565efd81
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2025-01-28-10-26-04.gh-issue-129393.0eICq6.rst
@@ -0,0 +1,2 @@
+On FreeBSD, :data:`sys.platform` doesn't contain the major version anymore.
+It is always ``'freebsd'``, instead of ``'freebsd13'`` or ``'freebsd14'``.
diff --git a/configure b/configure
index 885c2cf7828d6c..3eb787f788bfb9 100755
--- a/configure
+++ b/configure
@@ -4132,6 +4132,7 @@ then
case $MACHDEP in
aix*) MACHDEP="aix";;
+ freebsd*) MACHDEP="freebsd";;
linux-android*) MACHDEP="android";;
linux*) MACHDEP="linux";;
cygwin*) MACHDEP="cygwin";;
diff --git a/configure.ac b/configure.ac
index f89a0801948ca5..c0130b8082cd8a 100644
--- a/configure.ac
+++ b/configure.ac
@@ -365,6 +365,7 @@ then
case $MACHDEP in
aix*) MACHDEP="aix";;
+ freebsd*) MACHDEP="freebsd";;
linux-android*) MACHDEP="android";;
linux*) MACHDEP="linux";;
cygwin*) MACHDEP="cygwin";;
1
0
gh-127975: Avoid reusing quote types in ast.unparse if not needed (#127980)
by hauntsaninja Jan. 31, 2025
by hauntsaninja Jan. 31, 2025
Jan. 31, 2025
https://github.com/python/cpython/commit/8df5193d37f70a1478642c4b456dcc7d6d…
commit: 8df5193d37f70a1478642c4b456dcc7d6df6c117
branch: main
author: Shantanu <12621235+hauntsaninja(a)users.noreply.github.com>
committer: hauntsaninja <12621235+hauntsaninja(a)users.noreply.github.com>
date: 2025-01-31T08:49:06Z
summary:
gh-127975: Avoid reusing quote types in ast.unparse if not needed (#127980)
files:
A Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst
M Lib/ast.py
M Lib/test/test_unparse.py
diff --git a/Lib/ast.py b/Lib/ast.py
index 154d2c8c1f9ebb..0937c27bdf8a11 100644
--- a/Lib/ast.py
+++ b/Lib/ast.py
@@ -1196,9 +1196,14 @@ def visit_JoinedStr(self, node):
fallback_to_repr = True
break
quote_types = new_quote_types
- elif "\n" in value:
- quote_types = [q for q in quote_types if q in _MULTI_QUOTES]
- assert quote_types
+ else:
+ if "\n" in value:
+ quote_types = [q for q in quote_types if q in _MULTI_QUOTES]
+ assert quote_types
+
+ new_quote_types = [q for q in quote_types if q not in value]
+ if new_quote_types:
+ quote_types = new_quote_types
new_fstring_parts.append(value)
if fallback_to_repr:
diff --git a/Lib/test/test_unparse.py b/Lib/test/test_unparse.py
index 332919540da4d6..f6c4f1f3f6476a 100644
--- a/Lib/test/test_unparse.py
+++ b/Lib/test/test_unparse.py
@@ -513,11 +513,13 @@ def test_class_bases_and_keywords(self):
self.check_src_roundtrip("class X(*args, **kwargs):\n pass")
def test_fstrings(self):
- self.check_src_roundtrip("f'-{f'*{f'+{f'.{x}.'}+'}*'}-'")
- self.check_src_roundtrip("f'\\u2028{'x'}'")
+ self.check_src_roundtrip('''f\'\'\'-{f"""*{f"+{f'.{x}.'}+"}*"""}-\'\'\'''')
+ self.check_src_roundtrip('''f\'-{f\'\'\'*{f"""+{f".{f'{x}'}."}+"""}*\'\'\'}-\'''')
+ self.check_src_roundtrip('''f\'-{f\'*{f\'\'\'+{f""".{f"{f'{x}'}"}."""}+\'\'\'}*\'}-\'''')
+ self.check_src_roundtrip('''f"\\u2028{'x'}"''')
self.check_src_roundtrip(r"f'{x}\n'")
- self.check_src_roundtrip("f'{'\\n'}\\n'")
- self.check_src_roundtrip("f'{f'{x}\\n'}\\n'")
+ self.check_src_roundtrip('''f"{'\\n'}\\n"''')
+ self.check_src_roundtrip('''f"{f'{x}\\n'}\\n"''')
def test_docstrings(self):
docstrings = (
diff --git a/Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst b/Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst
new file mode 100644
index 00000000000000..597fa41deb811c
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2024-12-20-08-44-12.gh-issue-127975.8HJwu9.rst
@@ -0,0 +1 @@
+Avoid reusing quote types in :func:`ast.unparse` if not needed.
1
0
Jan. 31, 2025
https://github.com/python/cpython/commit/95504f429eec04010d0b815345ebcc3af2…
commit: 95504f429eec04010d0b815345ebcc3af2402af0
branch: main
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-31T09:45:35+01:00
summary:
gh-129354: Fix grammar in PyErr_FormatUnraisable() (#129475)
Replace "on verb+ing" with "while verb+ing".
files:
M Lib/test/test_cmd_line.py
M Lib/test/test_ctypes/test_callbacks.py
M Lib/test/test_ctypes/test_random_things.py
M Lib/test/test_signal.py
M Modules/_ctypes/_ctypes.c
M Modules/_ctypes/callbacks.c
M Modules/_datetimemodule.c
M Modules/_lsprof.c
M Modules/_testcapi/watchers.c
M Modules/_winapi.c
M Modules/atexitmodule.c
M Modules/getpath.c
M Modules/overlapped.c
M Modules/signalmodule.c
M Objects/dictobject.c
M Objects/moduleobject.c
M Objects/weakrefobject.c
M Python/compile.c
M Python/crossinterp.c
M Python/errors.c
M Python/gc.c
M Python/gc_free_threading.c
M Python/import.c
M Python/jit.c
M Python/pylifecycle.c
diff --git a/Lib/test/test_cmd_line.py b/Lib/test/test_cmd_line.py
index 24cf357c581096..b949b310ac0f5f 100644
--- a/Lib/test/test_cmd_line.py
+++ b/Lib/test/test_cmd_line.py
@@ -491,7 +491,7 @@ def test_stdout_flush_at_shutdown(self):
rc, out, err = assert_python_failure('-c', code)
self.assertEqual(b'', out)
self.assertEqual(120, rc)
- self.assertIn(b'Exception ignored on flushing sys.stdout:\n'
+ self.assertIn(b'Exception ignored while flushing sys.stdout:\n'
b'OSError: '.replace(b'\n', os.linesep.encode()),
err)
diff --git a/Lib/test/test_ctypes/test_callbacks.py b/Lib/test/test_ctypes/test_callbacks.py
index 8f483dfe1db801..6c7c2e5270736e 100644
--- a/Lib/test/test_ctypes/test_callbacks.py
+++ b/Lib/test/test_ctypes/test_callbacks.py
@@ -324,7 +324,7 @@ def func():
self.assertIsInstance(cm.unraisable.exc_value, TypeError)
self.assertEqual(cm.unraisable.err_msg,
- f"Exception ignored on converting result "
+ f"Exception ignored while converting result "
f"of ctypes callback function {func!r}")
self.assertIsNone(cm.unraisable.object)
diff --git a/Lib/test/test_ctypes/test_random_things.py b/Lib/test/test_ctypes/test_random_things.py
index 630f6ed9489eba..73ff57d925e2ea 100644
--- a/Lib/test/test_ctypes/test_random_things.py
+++ b/Lib/test/test_ctypes/test_random_things.py
@@ -51,7 +51,7 @@ def expect_unraisable(self, exc_type, exc_msg=None):
if exc_msg is not None:
self.assertEqual(str(cm.unraisable.exc_value), exc_msg)
self.assertEqual(cm.unraisable.err_msg,
- f"Exception ignored on calling ctypes "
+ f"Exception ignored while calling ctypes "
f"callback function {callback_func!r}")
self.assertIsNone(cm.unraisable.object)
diff --git a/Lib/test/test_signal.py b/Lib/test/test_signal.py
index 96ae79b0eb18b3..72a01cd1e451f4 100644
--- a/Lib/test/test_signal.py
+++ b/Lib/test/test_signal.py
@@ -383,7 +383,7 @@ def handler(signum, frame):
except ZeroDivisionError:
# An ignored exception should have been printed out on stderr
err = err.getvalue()
- if ('Exception ignored when trying to write to the signal wakeup fd'
+ if ('Exception ignored while trying to write to the signal wakeup fd'
not in err):
raise AssertionError(err)
if ('OSError: [Errno %d]' % errno.EBADF) not in err:
@@ -572,7 +572,7 @@ def handler(signum, frame):
signal.raise_signal(signum)
err = err.getvalue()
- if ('Exception ignored when trying to {action} to the signal wakeup fd'
+ if ('Exception ignored while trying to {action} to the signal wakeup fd'
not in err):
raise AssertionError(err)
""".format(action=action)
@@ -642,7 +642,7 @@ def handler(signum, frame):
"buffer" % written)
# By default, we get a warning when a signal arrives
- msg = ('Exception ignored when trying to {action} '
+ msg = ('Exception ignored while trying to {action} '
'to the signal wakeup fd')
signal.set_wakeup_fd(write.fileno())
diff --git a/Modules/_ctypes/_ctypes.c b/Modules/_ctypes/_ctypes.c
index d86adec0aeca58..606b4636380f11 100644
--- a/Modules/_ctypes/_ctypes.c
+++ b/Modules/_ctypes/_ctypes.c
@@ -183,7 +183,8 @@ _DictRemover_call(PyObject *myself, PyObject *args, PyObject *kw)
DictRemoverObject *self = _DictRemoverObject_CAST(myself);
if (self->key && self->dict) {
if (-1 == PyDict_DelItem(self->dict, self->key)) {
- PyErr_FormatUnraisable("Exception ignored on calling _ctypes.DictRemover");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "calling _ctypes.DictRemover");
}
Py_CLEAR(self->key);
Py_CLEAR(self->dict);
diff --git a/Modules/_ctypes/callbacks.c b/Modules/_ctypes/callbacks.c
index 591206a78035c5..f2df7052d84fb1 100644
--- a/Modules/_ctypes/callbacks.c
+++ b/Modules/_ctypes/callbacks.c
@@ -225,9 +225,9 @@ static void _CallPythonObject(ctypes_state *st,
result = PyObject_Vectorcall(callable, args, nargs, NULL);
if (result == NULL) {
- PyErr_FormatUnraisable(
- "Exception ignored on calling ctypes callback function %R",
- callable);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "calling ctypes callback function %R",
+ callable);
}
#ifdef MS_WIN32
@@ -269,7 +269,7 @@ static void _CallPythonObject(ctypes_state *st,
if (keep == NULL) {
/* Could not convert callback result. */
PyErr_FormatUnraisable(
- "Exception ignored on converting result "
+ "Exception ignored while converting result "
"of ctypes callback function %R",
callable);
}
@@ -282,7 +282,7 @@ static void _CallPythonObject(ctypes_state *st,
"memory leak in callback function.",
1) == -1) {
PyErr_FormatUnraisable(
- "Exception ignored on converting result "
+ "Exception ignored while converting result "
"of ctypes callback function %R",
callable);
}
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index 9d2abce203be7d..bcbf4217d41a9b 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -226,7 +226,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected)
goto finally;
error:
- PyErr_FormatUnraisable("Exception ignored when clearing _datetime module");
+ PyErr_FormatUnraisable("Exception ignored while clearing _datetime module");
finally:
PyErr_SetRaisedException(exc);
diff --git a/Modules/_lsprof.c b/Modules/_lsprof.c
index 29d9e5b6ef2cbe..fa1d333ecf3829 100644
--- a/Modules/_lsprof.c
+++ b/Modules/_lsprof.c
@@ -933,7 +933,8 @@ profiler_dealloc(ProfilerObject *op)
if (op->flags & POF_ENABLED) {
PyThreadState *tstate = _PyThreadState_GET();
if (_PyEval_SetProfile(tstate, NULL, NULL) < 0) {
- PyErr_FormatUnraisable("Exception ignored when destroying _lsprof profiler");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "destroying _lsprof profiler");
}
}
diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c
index 17a30355a266f0..f7440769b9594e 100644
--- a/Modules/_testcapi/watchers.c
+++ b/Modules/_testcapi/watchers.c
@@ -428,7 +428,7 @@ allocate_too_many_code_watchers(PyObject *self, PyObject *args)
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < num_watchers; i++) {
if (PyCode_ClearWatcher(watcher_ids[i]) < 0) {
- PyErr_FormatUnraisable("Exception ignored when "
+ PyErr_FormatUnraisable("Exception ignored while "
"clearing code watcher");
break;
}
@@ -610,7 +610,7 @@ allocate_too_many_func_watchers(PyObject *self, PyObject *args)
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < num_watchers; i++) {
if (PyFunction_ClearWatcher(watcher_ids[i]) < 0) {
- PyErr_FormatUnraisable("Exception ignored when "
+ PyErr_FormatUnraisable("Exception ignored while "
"clearing function watcher");
break;
}
@@ -757,7 +757,7 @@ allocate_too_many_context_watchers(PyObject *self, PyObject *args)
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < num_watchers; i++) {
if (PyContext_ClearWatcher(watcher_ids[i]) < 0) {
- PyErr_FormatUnraisable("Exception ignored when "
+ PyErr_FormatUnraisable("Exception ignored while "
"clearing context watcher");
break;
}
diff --git a/Modules/_winapi.c b/Modules/_winapi.c
index 56dd38401cb273..786a828f00908c 100644
--- a/Modules/_winapi.c
+++ b/Modules/_winapi.c
@@ -177,7 +177,7 @@ overlapped_dealloc(OverlappedObject *self)
PyErr_SetString(PyExc_PythonFinalizationError,
"I/O operations still in flight while destroying "
"Overlapped object, the process may crash");
- PyErr_FormatUnraisable("Exception ignored when deallocating "
+ PyErr_FormatUnraisable("Exception ignored while deallocating "
"overlapped operation %R", self);
}
else {
diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c
index 1dbcc1089c32ab..2bfdda53af8cb2 100644
--- a/Modules/atexitmodule.c
+++ b/Modules/atexitmodule.c
@@ -110,7 +110,7 @@ atexit_callfuncs(struct atexit_state *state)
PyObject *copy = PyList_GetSlice(state->callbacks, 0, PyList_GET_SIZE(state->callbacks));
if (copy == NULL)
{
- PyErr_FormatUnraisable("Exception ignored when "
+ PyErr_FormatUnraisable("Exception ignored while "
"copying atexit callbacks");
return;
}
diff --git a/Modules/getpath.c b/Modules/getpath.c
index 2d3c9757298d16..e2478da021f511 100644
--- a/Modules/getpath.c
+++ b/Modules/getpath.c
@@ -955,7 +955,7 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
) {
Py_DECREF(co);
Py_DECREF(dict);
- PyErr_FormatUnraisable("Exception ignored in preparing getpath");
+ PyErr_FormatUnraisable("Exception ignored while preparing getpath");
return PyStatus_Error("error evaluating initial values");
}
@@ -964,13 +964,13 @@ _PyConfig_InitPathConfig(PyConfig *config, int compute_path_config)
if (!r) {
Py_DECREF(dict);
- PyErr_FormatUnraisable("Exception ignored in running getpath");
+ PyErr_FormatUnraisable("Exception ignored while running getpath");
return PyStatus_Error("error evaluating path");
}
Py_DECREF(r);
if (_PyConfig_FromDict(config, configDict) < 0) {
- PyErr_FormatUnraisable("Exception ignored in reading getpath results");
+ PyErr_FormatUnraisable("Exception ignored while reading getpath results");
Py_DECREF(dict);
return PyStatus_Error("error getting getpath results");
}
diff --git a/Modules/overlapped.c b/Modules/overlapped.c
index 525b288f3ff54d..806ebee7a70ff1 100644
--- a/Modules/overlapped.c
+++ b/Modules/overlapped.c
@@ -759,7 +759,7 @@ Overlapped_dealloc(OverlappedObject *self)
PyExc_RuntimeError,
"%R still has pending operation at "
"deallocation, the process may crash", self);
- PyErr_FormatUnraisable("Exception ignored when deallocating "
+ PyErr_FormatUnraisable("Exception ignored while deallocating "
"overlapped operation %R", self);
}
}
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index 0cc9b35300dcca..b679b83bed5365 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -245,7 +245,8 @@ report_wakeup_write_error(void *data)
errno = (int) (intptr_t) data;
PyObject *exc = PyErr_GetRaisedException();
PyErr_SetFromErrno(PyExc_OSError);
- PyErr_FormatUnraisable("Exception ignored when trying to write to the signal wakeup fd");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "trying to write to the signal wakeup fd");
PyErr_SetRaisedException(exc);
errno = save_errno;
return 0;
@@ -262,7 +263,8 @@ report_wakeup_send_error(void* data)
recognizes the error codes used by both GetLastError() and
WSAGetLastError */
PyErr_SetExcFromWindowsErr(PyExc_OSError, send_errno);
- PyErr_FormatUnraisable("Exception ignored when trying to send to the signal wakeup fd");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "trying to send to the signal wakeup fd");
PyErr_SetRaisedException(exc);
return 0;
}
@@ -1837,7 +1839,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate)
PyErr_Format(PyExc_OSError,
"Signal %i ignored due to race condition",
i);
- PyErr_FormatUnraisable("Exception ignored when "
+ PyErr_FormatUnraisable("Exception ignored while "
"calling signal handler");
continue;
}
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index a05359ca0b16ef..91cf013a1dc24b 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -7351,7 +7351,7 @@ PyObject_ClearManagedDict(PyObject *obj)
if (set_or_clear_managed_dict(obj, NULL, true) < 0) {
/* Must be out of memory */
assert(PyErr_Occurred() == PyExc_MemoryError);
- PyErr_FormatUnraisable("Exception ignored when "
+ PyErr_FormatUnraisable("Exception ignored while "
"clearing an object managed dict");
/* Clear the dict */
PyDictObject *dict = _PyObject_GetManagedDict(obj);
diff --git a/Objects/moduleobject.c b/Objects/moduleobject.c
index a8d64c9aefae6b..740392b061ba9a 100644
--- a/Objects/moduleobject.c
+++ b/Objects/moduleobject.c
@@ -703,7 +703,8 @@ _PyModule_ClearDict(PyObject *d)
PyErr_Clear();
}
if (PyDict_SetItem(d, key, Py_None) != 0) {
- PyErr_FormatUnraisable("Exception ignored on clearing module dict");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing module dict");
}
}
}
@@ -724,7 +725,8 @@ _PyModule_ClearDict(PyObject *d)
PyErr_Clear();
}
if (PyDict_SetItem(d, key, Py_None) != 0) {
- PyErr_FormatUnraisable("Exception ignored on clearing module dict");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing module dict");
}
}
}
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 8ced82ef36e30d..05ae43d1df475b 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -1042,7 +1042,8 @@ PyObject_ClearWeakRefs(PyObject *object)
PyObject *tuple = PyTuple_New(num_weakrefs * 2);
if (tuple == NULL) {
_PyWeakref_ClearWeakRefsNoCallbacks(object);
- PyErr_FormatUnraisable("Exception ignored when clearing object weakrefs");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing object weakrefs");
PyErr_SetRaisedException(exc);
return;
}
diff --git a/Python/compile.c b/Python/compile.c
index ef470830336dde..b58c12d4b881ac 100644
--- a/Python/compile.c
+++ b/Python/compile.c
@@ -704,12 +704,12 @@ _PyCompile_ExitScope(compiler *c)
assert(c->u);
/* we are deleting from a list so this really shouldn't fail */
if (PySequence_DelItem(c->c_stack, n) < 0) {
- PyErr_FormatUnraisable("Exception ignored on removing "
+ PyErr_FormatUnraisable("Exception ignored while removing "
"the last compiler stack item");
}
if (nested_seq != NULL) {
if (_PyInstructionSequence_AddNested(c->u->u_instr_sequence, nested_seq) < 0) {
- PyErr_FormatUnraisable("Exception ignored on appending "
+ PyErr_FormatUnraisable("Exception ignored while appending "
"nested instruction sequence");
}
}
diff --git a/Python/crossinterp.c b/Python/crossinterp.c
index 0a106ad636bfe8..7eb5bc267487d1 100644
--- a/Python/crossinterp.c
+++ b/Python/crossinterp.c
@@ -784,7 +784,8 @@ _PyXI_excinfo_Apply(_PyXI_excinfo *info, PyObject *exctype)
PyObject *exc = PyErr_GetRaisedException();
if (PyObject_SetAttrString(exc, "_errdisplay", tbexc) < 0) {
#ifdef Py_DEBUG
- PyErr_FormatUnraisable("Exception ignored when setting _errdisplay");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "setting _errdisplay");
#endif
PyErr_Clear();
}
diff --git a/Python/errors.c b/Python/errors.c
index 9c7b771133dcf4..0a19d898da75d7 100644
--- a/Python/errors.c
+++ b/Python/errors.c
@@ -1633,7 +1633,7 @@ format_unraisable_v(const char *format, va_list va, PyObject *obj)
PyObject *hook_args = make_unraisable_hook_args(
tstate, exc_type, exc_value, exc_tb, err_msg, obj);
if (hook_args == NULL) {
- err_msg_str = ("Exception ignored on building "
+ err_msg_str = ("Exception ignored while building "
"sys.unraisablehook arguments");
goto error;
}
diff --git a/Python/gc.c b/Python/gc.c
index 3fe0b7f814544d..420240fc3020be 100644
--- a/Python/gc.c
+++ b/Python/gc.c
@@ -1779,7 +1779,7 @@ do_gc_callback(GCState *gcstate, const char *phase,
"collected", stats->collected,
"uncollectable", stats->uncollectable);
if (info == NULL) {
- PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks");
+ PyErr_FormatUnraisable("Exception ignored while invoking gc callbacks");
return;
}
}
@@ -1787,7 +1787,7 @@ do_gc_callback(GCState *gcstate, const char *phase,
PyObject *phase_obj = PyUnicode_FromString(phase);
if (phase_obj == NULL) {
Py_XDECREF(info);
- PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks");
+ PyErr_FormatUnraisable("Exception ignored while invoking gc callbacks");
return;
}
diff --git a/Python/gc_free_threading.c b/Python/gc_free_threading.c
index 211d52d6cc7164..905a14f660ec6c 100644
--- a/Python/gc_free_threading.c
+++ b/Python/gc_free_threading.c
@@ -1427,7 +1427,8 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase,
"collected", collected,
"uncollectable", uncollectable);
if (info == NULL) {
- PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "invoking gc callbacks");
return;
}
}
@@ -1435,7 +1436,8 @@ invoke_gc_callback(PyThreadState *tstate, const char *phase,
PyObject *phase_obj = PyUnicode_FromString(phase);
if (phase_obj == NULL) {
Py_XDECREF(info);
- PyErr_FormatUnraisable("Exception ignored on invoking gc callbacks");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "invoking gc callbacks");
return;
}
diff --git a/Python/import.c b/Python/import.c
index dd7a0b4b1ed8de..8cc8d3a503bffa 100644
--- a/Python/import.c
+++ b/Python/import.c
@@ -594,7 +594,8 @@ _PyImport_ClearModulesByIndex(PyInterpreterState *interp)
if (PyList_SetSlice(MODULES_BY_INDEX(interp),
0, PyList_GET_SIZE(MODULES_BY_INDEX(interp)),
NULL)) {
- PyErr_FormatUnraisable("Exception ignored on clearing interpreters module list");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing interpreters module list");
}
}
@@ -4080,13 +4081,15 @@ _PyImport_FiniCore(PyInterpreterState *interp)
int verbose = _PyInterpreterState_GetConfig(interp)->verbose;
if (_PySys_ClearAttrString(interp, "meta_path", verbose) < 0) {
- PyErr_FormatUnraisable("Exception ignored on clearing sys.meta_path");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing sys.meta_path");
}
// XXX Pull in most of finalize_modules() in pylifecycle.c.
if (_PySys_ClearAttrString(interp, "modules", verbose) < 0) {
- PyErr_FormatUnraisable("Exception ignored on clearing sys.modules");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing sys.modules");
}
_PyImport_ClearCore(interp);
@@ -4161,10 +4164,12 @@ _PyImport_FiniExternal(PyInterpreterState *interp)
// XXX Uninstall importlib metapath importers here?
if (_PySys_ClearAttrString(interp, "path_importer_cache", verbose) < 0) {
- PyErr_FormatUnraisable("Exception ignored on clearing sys.path_importer_cache");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing sys.path_importer_cache");
}
if (_PySys_ClearAttrString(interp, "path_hooks", verbose) < 0) {
- PyErr_FormatUnraisable("Exception ignored on clearing sys.path_hooks");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing sys.path_hooks");
}
}
diff --git a/Python/jit.c b/Python/jit.c
index 4b01112f9b0a5a..33c2418084b1fd 100644
--- a/Python/jit.c
+++ b/Python/jit.c
@@ -563,7 +563,8 @@ _PyJIT_Free(_PyExecutorObject *executor)
executor->jit_side_entry = NULL;
executor->jit_size = 0;
if (jit_free(memory, size)) {
- PyErr_FormatUnraisable("Exception ignored when freeing JIT memory");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "freeing JIT memory");
}
}
}
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 7031d743174650..300a871d2cc4bf 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1475,13 +1475,15 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose)
PySys_WriteStderr("# clear builtins._\n");
}
if (PyDict_SetItemString(interp->builtins, "_", Py_None) < 0) {
- PyErr_FormatUnraisable("Exception ignored on setting builtin variable _");
+ PyErr_FormatUnraisable("Exception ignored while "
+ "setting builtin variable _");
}
const char * const *p;
for (p = sys_deletes; *p != NULL; p++) {
if (_PySys_ClearAttrString(interp, *p, verbose) < 0) {
- PyErr_FormatUnraisable("Exception ignored on clearing sys.%s", *p);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "clearing sys.%s", *p);
}
}
for (p = sys_files; *p != NULL; p+=2) {
@@ -1492,13 +1494,15 @@ finalize_modules_delete_special(PyThreadState *tstate, int verbose)
}
PyObject *value;
if (PyDict_GetItemStringRef(interp->sysdict, orig_name, &value) < 0) {
- PyErr_FormatUnraisable("Exception ignored on restoring sys.%s", name);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "restoring sys.%s", name);
}
if (value == NULL) {
value = Py_NewRef(Py_None);
}
if (PyDict_SetItemString(interp->sysdict, name, value) < 0) {
- PyErr_FormatUnraisable("Exception ignored on restoring sys.%s", name);
+ PyErr_FormatUnraisable("Exception ignored while "
+ "restoring sys.%s", name);
}
Py_DECREF(value);
}
@@ -1510,7 +1514,7 @@ finalize_remove_modules(PyObject *modules, int verbose)
{
PyObject *weaklist = PyList_New(0);
if (weaklist == NULL) {
- PyErr_FormatUnraisable("Exception ignored on removing modules");
+ PyErr_FormatUnraisable("Exception ignored while removing modules");
}
#define STORE_MODULE_WEAKREF(name, mod) \
@@ -1519,13 +1523,13 @@ finalize_remove_modules(PyObject *modules, int verbose)
if (wr) { \
PyObject *tup = PyTuple_Pack(2, name, wr); \
if (!tup || PyList_Append(weaklist, tup) < 0) { \
- PyErr_FormatUnraisable("Exception ignored on removing modules"); \
+ PyErr_FormatUnraisable("Exception ignored while removing modules"); \
} \
Py_XDECREF(tup); \
Py_DECREF(wr); \
} \
else { \
- PyErr_FormatUnraisable("Exception ignored on removing modules"); \
+ PyErr_FormatUnraisable("Exception ignored while removing modules"); \
} \
}
@@ -1536,7 +1540,7 @@ finalize_remove_modules(PyObject *modules, int verbose)
} \
STORE_MODULE_WEAKREF(name, mod); \
if (PyObject_SetItem(modules, name, Py_None) < 0) { \
- PyErr_FormatUnraisable("Exception ignored on removing modules"); \
+ PyErr_FormatUnraisable("Exception ignored while removing modules"); \
} \
}
@@ -1550,14 +1554,14 @@ finalize_remove_modules(PyObject *modules, int verbose)
else {
PyObject *iterator = PyObject_GetIter(modules);
if (iterator == NULL) {
- PyErr_FormatUnraisable("Exception ignored on removing modules");
+ PyErr_FormatUnraisable("Exception ignored while removing modules");
}
else {
PyObject *key;
while ((key = PyIter_Next(iterator))) {
PyObject *value = PyObject_GetItem(modules, key);
if (value == NULL) {
- PyErr_FormatUnraisable("Exception ignored on removing modules");
+ PyErr_FormatUnraisable("Exception ignored while removing modules");
continue;
}
CLEAR_MODULE(key, value);
@@ -1565,7 +1569,7 @@ finalize_remove_modules(PyObject *modules, int verbose)
Py_DECREF(key);
}
if (PyErr_Occurred()) {
- PyErr_FormatUnraisable("Exception ignored on removing modules");
+ PyErr_FormatUnraisable("Exception ignored while removing modules");
}
Py_DECREF(iterator);
}
@@ -1585,7 +1589,7 @@ finalize_clear_modules_dict(PyObject *modules)
}
else {
if (PyObject_CallMethodNoArgs(modules, &_Py_ID(clear)) == NULL) {
- PyErr_FormatUnraisable("Exception ignored on clearing sys.modules");
+ PyErr_FormatUnraisable("Exception ignored while clearing sys.modules");
}
}
}
@@ -1597,11 +1601,11 @@ finalize_restore_builtins(PyThreadState *tstate)
PyInterpreterState *interp = tstate->interp;
PyObject *dict = PyDict_Copy(interp->builtins);
if (dict == NULL) {
- PyErr_FormatUnraisable("Exception ignored on restoring builtins");
+ PyErr_FormatUnraisable("Exception ignored while restoring builtins");
}
PyDict_Clear(interp->builtins);
if (PyDict_Update(interp->builtins, interp->builtins_copy)) {
- PyErr_FormatUnraisable("Exception ignored on restoring builtins");
+ PyErr_FormatUnraisable("Exception ignored while restoring builtins");
}
Py_XDECREF(dict);
}
@@ -1773,7 +1777,7 @@ flush_std_files(void)
if (fout != NULL && fout != Py_None && !file_is_closed(fout)) {
if (_PyFile_Flush(fout) < 0) {
- PyErr_FormatUnraisable("Exception ignored on flushing sys.stdout");
+ PyErr_FormatUnraisable("Exception ignored while flushing sys.stdout");
status = -1;
}
}
1
0
Revert "gh-129005: _pyio.BufferedIO remove copy on readall (#129454)" (#129500)
by vstinner Jan. 31, 2025
by vstinner Jan. 31, 2025
Jan. 31, 2025
https://github.com/python/cpython/commit/3ebe3d7688475e98a34d691e1ba50d8b71…
commit: 3ebe3d7688475e98a34d691e1ba50d8b7178a575
branch: main
author: Cody Maloney <cmaloney(a)users.noreply.github.com>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-31T09:40:44+01:00
summary:
Revert "gh-129005: _pyio.BufferedIO remove copy on readall (#129454)" (#129500)
This reverts commit e1c4ba928852eac0b0e0bded1c314e3e36975286.
files:
D Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst
M Lib/_pyio.py
diff --git a/Lib/_pyio.py b/Lib/_pyio.py
index 755e0258770891..76a27910da4d5f 100644
--- a/Lib/_pyio.py
+++ b/Lib/_pyio.py
@@ -1062,9 +1062,6 @@ def _read_unlocked(self, n=None):
if chunk is None:
return buf[pos:] or None
else:
- # Avoid slice + copy if there is no data in buf
- if not buf:
- return chunk
return buf[pos:] + chunk
chunks = [buf[pos:]] # Strip the consumed bytes.
current_size = 0
diff --git a/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst b/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst
deleted file mode 100644
index 48ee57109be2ff..00000000000000
--- a/Misc/NEWS.d/next/Library/2025-01-29-00-00-01.gh-issue-129005.aV_3O8.rst
+++ /dev/null
@@ -1,2 +0,0 @@
-:mod:`!_pyio`: Remove an unnecessary copy when ``_pyio.BufferedReader.read()``
-is called to read all data from a file and has no data already in buffer.
1
0
https://github.com/python/cpython/commit/c07ac3c86a8f5021e27bf2c27c6bf0a252…
commit: c07ac3c86a8f5021e27bf2c27c6bf0a25229d846
branch: main
author: Kumar Aditya <kumaraditya(a)python.org>
committer: kumaraditya303 <kumaraditya(a)python.org>
date: 2025-01-31T13:41:11+05:30
summary:
gh-128452: fix warning in socketmodule.c (#129478)
files:
M Modules/socketmodule.c
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
index 01811afa578119..b178eb42ac8e6a 100644
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -2952,6 +2952,8 @@ sock_accept(PyObject *self, PyObject *Py_UNUSED(ignored))
ctx.addrlen = &addrlen;
ctx.addrbuf = &addrbuf;
+ ctx.result = INVALID_SOCKET;
+
if (sock_call(s, 0, sock_accept_impl, &ctx) < 0)
return NULL;
newfd = ctx.result;
1
0
gh-129205: Update multiprocessing.forkserver to use os.readinto() (#129425)
by vstinner Jan. 30, 2025
by vstinner Jan. 30, 2025
Jan. 30, 2025
https://github.com/python/cpython/commit/10ee2d9d3bcde27c75f179214ad41c00e4…
commit: 10ee2d9d3bcde27c75f179214ad41c00e4852a7a
branch: main
author: Cody Maloney <cmaloney(a)users.noreply.github.com>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-30T22:24:52Z
summary:
gh-129205: Update multiprocessing.forkserver to use os.readinto() (#129425)
files:
M Lib/multiprocessing/forkserver.py
diff --git a/Lib/multiprocessing/forkserver.py b/Lib/multiprocessing/forkserver.py
index df9b9be9d1898b..681af2610e9b37 100644
--- a/Lib/multiprocessing/forkserver.py
+++ b/Lib/multiprocessing/forkserver.py
@@ -382,13 +382,14 @@ def _serve_one(child_r, fds, unused_fds, handlers):
#
def read_signed(fd):
- data = b''
- length = SIGNED_STRUCT.size
- while len(data) < length:
- s = os.read(fd, length - len(data))
- if not s:
+ data = bytearray(SIGNED_STRUCT.size)
+ unread = memoryview(data)
+ while unread:
+ count = os.readinto(fd, unread)
+ if count == 0:
raise EOFError('unexpected EOF')
- data += s
+ unread = unread[count:]
+
return SIGNED_STRUCT.unpack(data)[0]
def write_signed(fd, n):
1
0
Jan. 30, 2025
https://github.com/python/cpython/commit/510fefdc625dd2ed2b6b3975314a59e291…
commit: 510fefdc625dd2ed2b6b3975314a59e291b94ae8
branch: main
author: donBarbos <donbarbos(a)proton.me>
committer: pablogsal <Pablogsal(a)gmail.com>
date: 2025-01-30T19:34:09Z
summary:
gh-127349: Add check for correct resizing in REPL (#127387)
files:
A Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst
M Lib/_pyrepl/reader.py
M Lib/test/test_pyrepl/test_reader.py
diff --git a/Lib/_pyrepl/reader.py b/Lib/_pyrepl/reader.py
index 4b0700d069c621..1252847e02b2ea 100644
--- a/Lib/_pyrepl/reader.py
+++ b/Lib/_pyrepl/reader.py
@@ -587,10 +587,11 @@ def setpos_from_xy(self, x: int, y: int) -> None:
def pos2xy(self) -> tuple[int, int]:
"""Return the x, y coordinates of position 'pos'."""
# this *is* incomprehensible, yes.
- y = 0
+ p, y = 0, 0
+ l2: list[int] = []
pos = self.pos
assert 0 <= pos <= len(self.buffer)
- if pos == len(self.buffer):
+ if pos == len(self.buffer) and len(self.screeninfo) > 0:
y = len(self.screeninfo) - 1
p, l2 = self.screeninfo[y]
return p + sum(l2) + l2.count(0), y
diff --git a/Lib/test/test_pyrepl/test_reader.py b/Lib/test/test_pyrepl/test_reader.py
index 863ecc61ddd432..27c6d6664eda9e 100644
--- a/Lib/test/test_pyrepl/test_reader.py
+++ b/Lib/test/test_pyrepl/test_reader.py
@@ -4,7 +4,7 @@
from unittest import TestCase
from unittest.mock import MagicMock
-from .support import handle_all_events, handle_events_narrow_console, code_to_events, prepare_reader
+from .support import handle_all_events, handle_events_narrow_console, code_to_events, prepare_reader, prepare_console
from _pyrepl.console import Event
from _pyrepl.reader import Reader
@@ -312,3 +312,10 @@ def test_key_press_on_tab_press_once(self):
reader, _ = handle_all_events(events, prepare_reader=completing_reader)
self.assert_screen_equals(reader, f"{code}a")
+
+ def test_pos2xy_with_no_columns(self):
+ console = prepare_console([])
+ reader = prepare_reader(console)
+ # Simulate a resize to 0 columns
+ reader.screeninfo = []
+ self.assertEqual(reader.pos2xy(), (0, 0))
diff --git a/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst
new file mode 100644
index 00000000000000..3c1586b6cbb8e7
--- /dev/null
+++ b/Misc/NEWS.d/next/Core_and_Builtins/2024-11-30-16-13-31.gh-issue-127349.ssYd6n.rst
@@ -0,0 +1,2 @@
+Fixed the error when resizing terminal in Python REPL. Patch by Semyon
+Moroz.
1
0
https://github.com/python/cpython/commit/9a59a51733e58b6091ca9157fd43cc9d0f…
commit: 9a59a51733e58b6091ca9157fd43cc9d0f93a96f
branch: 3.13
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-30T20:29:27+01:00
summary:
[3.13] gh-111495: Add PyFile tests (#129449) (#129477)
gh-111495: Add PyFile tests (#129449)
Add tests for the following functions in test_capi.test_file:
* PyFile_FromFd()
* PyFile_GetLine()
* PyFile_NewStdPrinter()
* PyFile_WriteObject()
* PyFile_WriteString()
* PyObject_AsFileDescriptor()
Add Modules/_testlimitedcapi/file.c file.
Remove test_embed.StdPrinterTests which became redundant.
(cherry picked from commit 4ca9fc08f89bf7172d41e523d9e520eb1729ee8c)
files:
A Lib/test/test_capi/test_file.py
A Modules/_testcapi/clinic/file.c.h
A Modules/_testlimitedcapi/clinic/file.c.h
A Modules/_testlimitedcapi/file.c
M Lib/test/test_embed.py
M Modules/Setup.stdlib.in
M Modules/_testcapi/file.c
M Modules/_testlimitedcapi.c
M Modules/_testlimitedcapi/parts.h
M PCbuild/_testlimitedcapi.vcxproj
M PCbuild/_testlimitedcapi.vcxproj.filters
diff --git a/Lib/test/test_capi/test_file.py b/Lib/test/test_capi/test_file.py
new file mode 100644
index 00000000000000..373692d938568a
--- /dev/null
+++ b/Lib/test/test_capi/test_file.py
@@ -0,0 +1,234 @@
+import io
+import os
+import unittest
+import warnings
+from test import support
+from test.support import import_helper, os_helper, warnings_helper
+
+
+_testcapi = import_helper.import_module('_testcapi')
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
+_io = import_helper.import_module('_io')
+NULL = None
+STDOUT_FD = 1
+
+with open(__file__, 'rb') as fp:
+ FIRST_LINE = next(fp).decode()
+FIRST_LINE_NORM = FIRST_LINE.rstrip() + '\n'
+
+
+class CAPIFileTest(unittest.TestCase):
+ def test_pyfile_fromfd(self):
+ # Test PyFile_FromFd() which is a thin wrapper to _io.open()
+ pyfile_fromfd = _testlimitedcapi.pyfile_fromfd
+ filename = __file__
+ with open(filename, "rb") as fp:
+ fd = fp.fileno()
+
+ # FileIO
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "rb", 0, NULL, NULL, NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.FileIO)
+ self.assertEqual(obj.readline(), FIRST_LINE.encode())
+ finally:
+ obj.close()
+
+ # BufferedReader
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "rb", 1024, NULL, NULL, NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.BufferedReader)
+ self.assertEqual(obj.readline(), FIRST_LINE.encode())
+ finally:
+ obj.close()
+
+ # TextIOWrapper
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "r", 1,
+ "utf-8", "replace", NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.TextIOWrapper)
+ self.assertEqual(obj.encoding, "utf-8")
+ self.assertEqual(obj.errors, "replace")
+ self.assertEqual(obj.readline(), FIRST_LINE_NORM)
+ finally:
+ obj.close()
+
+ def test_pyfile_getline(self):
+ # Test PyFile_GetLine(file, n): call file.readline()
+ # and strip "\n" suffix if n < 0.
+ pyfile_getline = _testlimitedcapi.pyfile_getline
+
+ # Test Unicode
+ with open(__file__, "r") as fp:
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, -1),
+ FIRST_LINE_NORM.rstrip('\n'))
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 0),
+ FIRST_LINE_NORM)
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 6),
+ FIRST_LINE_NORM[:6])
+
+ # Test bytes
+ with open(__file__, "rb") as fp:
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, -1),
+ FIRST_LINE.rstrip('\n').encode())
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 0),
+ FIRST_LINE.encode())
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 6),
+ FIRST_LINE.encode()[:6])
+
+ def test_pyfile_writestring(self):
+ # Test PyFile_WriteString(str, file): call file.write(str)
+ writestr = _testlimitedcapi.pyfile_writestring
+
+ with io.StringIO() as fp:
+ self.assertEqual(writestr("a\xe9\u20ac\U0010FFFF".encode(), fp), 0)
+ with self.assertRaises(UnicodeDecodeError):
+ writestr(b"\xff", fp)
+ with self.assertRaises(UnicodeDecodeError):
+ writestr("\udc80".encode("utf-8", "surrogatepass"), fp)
+
+ text = fp.getvalue()
+ self.assertEqual(text, "a\xe9\u20ac\U0010FFFF")
+
+ with self.assertRaises(SystemError):
+ writestr(b"abc", NULL)
+
+ def test_pyfile_writeobject(self):
+ # Test PyFile_WriteObject(obj, file, flags):
+ # - Call file.write(str(obj)) if flags equals Py_PRINT_RAW.
+ # - Call file.write(repr(obj)) otherwise.
+ writeobject = _testlimitedcapi.pyfile_writeobject
+ Py_PRINT_RAW = 1
+
+ with io.StringIO() as fp:
+ # Test flags=Py_PRINT_RAW
+ self.assertEqual(writeobject("raw", fp, Py_PRINT_RAW), 0)
+ writeobject(NULL, fp, Py_PRINT_RAW)
+
+ # Test flags=0
+ self.assertEqual(writeobject("repr", fp, 0), 0)
+ writeobject(NULL, fp, 0)
+
+ text = fp.getvalue()
+ self.assertEqual(text, "raw<NULL>'repr'<NULL>")
+
+ # invalid file type
+ for invalid_file in (123, "abc", object()):
+ with self.subTest(file=invalid_file):
+ with self.assertRaises(AttributeError):
+ writeobject("abc", invalid_file, Py_PRINT_RAW)
+
+ with self.assertRaises(TypeError):
+ writeobject("abc", NULL, 0)
+
+ def test_pyobject_asfiledescriptor(self):
+ # Test PyObject_AsFileDescriptor(obj):
+ # - Return obj if obj is an integer.
+ # - Return obj.fileno() otherwise.
+ # File descriptor must be >= 0.
+ asfd = _testlimitedcapi.pyobject_asfiledescriptor
+
+ self.assertEqual(asfd(123), 123)
+ self.assertEqual(asfd(0), 0)
+
+ with open(__file__, "rb") as fp:
+ self.assertEqual(asfd(fp), fp.fileno())
+
+ # bool emits RuntimeWarning
+ msg = r"bool is used as a file descriptor"
+ with warnings_helper.check_warnings((msg, RuntimeWarning)):
+ self.assertEqual(asfd(True), 1)
+
+ class FakeFile:
+ def __init__(self, fd):
+ self.fd = fd
+ def fileno(self):
+ return self.fd
+
+ # file descriptor must be positive
+ with self.assertRaises(ValueError):
+ asfd(-1)
+ with self.assertRaises(ValueError):
+ asfd(FakeFile(-1))
+
+ # fileno() result must be an integer
+ with self.assertRaises(TypeError):
+ asfd(FakeFile("text"))
+
+ # unsupported types
+ for obj in ("string", ["list"], object()):
+ with self.subTest(obj=obj):
+ with self.assertRaises(TypeError):
+ asfd(obj)
+
+ # CRASHES asfd(NULL)
+
+ def test_pyfile_newstdprinter(self):
+ # Test PyFile_NewStdPrinter()
+ pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
+
+ file = pyfile_newstdprinter(STDOUT_FD)
+ self.assertEqual(file.closed, False)
+ self.assertIsNone(file.encoding)
+ self.assertEqual(file.mode, "w")
+
+ self.assertEqual(file.fileno(), STDOUT_FD)
+ self.assertEqual(file.isatty(), os.isatty(STDOUT_FD))
+
+ # flush() is a no-op
+ self.assertIsNone(file.flush())
+
+ # close() is a no-op
+ self.assertIsNone(file.close())
+ self.assertEqual(file.closed, False)
+
+ support.check_disallow_instantiation(self, type(file))
+
+ def test_pyfile_newstdprinter_write(self):
+ # Test the write() method of PyFile_NewStdPrinter()
+ pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
+
+ filename = os_helper.TESTFN
+ self.addCleanup(os_helper.unlink, filename)
+
+ try:
+ old_stdout = os.dup(STDOUT_FD)
+ except OSError as exc:
+ # os.dup(STDOUT_FD) is not supported on WASI
+ self.skipTest(f"os.dup() failed with {exc!r}")
+
+ try:
+ with open(filename, "wb") as fp:
+ # PyFile_NewStdPrinter() only accepts fileno(stdout)
+ # or fileno(stderr) file descriptor.
+ fd = fp.fileno()
+ os.dup2(fd, STDOUT_FD)
+
+ file = pyfile_newstdprinter(STDOUT_FD)
+ self.assertEqual(file.write("text"), 4)
+ # The surrogate character is encoded with
+ # the "surrogateescape" error handler
+ self.assertEqual(file.write("[\udc80]"), 8)
+ finally:
+ os.dup2(old_stdout, STDOUT_FD)
+ os.close(old_stdout)
+
+ with open(filename, "r") as fp:
+ self.assertEqual(fp.read(), "text[\\udc80]")
+
+ # TODO: Test Py_UniversalNewlineFgets()
+
+ # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by
+ # test_embed.test_open_code_hook()
+
+
+if __name__ == "__main__":
+ unittest.main()
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index 5f70632182ec24..ed459794952581 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -1955,56 +1955,5 @@ def test_presite(self):
self.assertIn("cmd", out)
-class StdPrinterTests(EmbeddingTestsMixin, unittest.TestCase):
- # Test PyStdPrinter_Type which is used by _PySys_SetPreliminaryStderr():
- # "Set up a preliminary stderr printer until we have enough
- # infrastructure for the io module in place."
-
- STDOUT_FD = 1
-
- def create_printer(self, fd):
- ctypes = import_helper.import_module('ctypes')
- PyFile_NewStdPrinter = ctypes.pythonapi.PyFile_NewStdPrinter
- PyFile_NewStdPrinter.argtypes = (ctypes.c_int,)
- PyFile_NewStdPrinter.restype = ctypes.py_object
- return PyFile_NewStdPrinter(fd)
-
- def test_write(self):
- message = "unicode:\xe9-\u20ac-\udc80!\n"
-
- stdout_fd = self.STDOUT_FD
- stdout_fd_copy = os.dup(stdout_fd)
- self.addCleanup(os.close, stdout_fd_copy)
-
- rfd, wfd = os.pipe()
- self.addCleanup(os.close, rfd)
- self.addCleanup(os.close, wfd)
- try:
- # PyFile_NewStdPrinter() only accepts fileno(stdout)
- # or fileno(stderr) file descriptor.
- os.dup2(wfd, stdout_fd)
-
- printer = self.create_printer(stdout_fd)
- printer.write(message)
- finally:
- os.dup2(stdout_fd_copy, stdout_fd)
-
- data = os.read(rfd, 100)
- self.assertEqual(data, message.encode('utf8', 'backslashreplace'))
-
- def test_methods(self):
- fd = self.STDOUT_FD
- printer = self.create_printer(fd)
- self.assertEqual(printer.fileno(), fd)
- self.assertEqual(printer.isatty(), os.isatty(fd))
- printer.flush() # noop
- printer.close() # noop
-
- def test_disallow_instantiation(self):
- fd = self.STDOUT_FD
- printer = self.create_printer(fd)
- support.check_disallow_instantiation(self, type(printer))
-
-
if __name__ == "__main__":
unittest.main()
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index 0dc7cf4ea0957d..57b90101bbe4a6 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -164,7 +164,7 @@
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c
-@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c
+@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/file.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
diff --git a/Modules/_testcapi/clinic/file.c.h b/Modules/_testcapi/clinic/file.c.h
new file mode 100644
index 00000000000000..2a01a63caf7ff3
--- /dev/null
+++ b/Modules/_testcapi/clinic/file.c.h
@@ -0,0 +1,31 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(_testcapi_pyfile_newstdprinter__doc__,
+"pyfile_newstdprinter($module, fd, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF \
+ {"pyfile_newstdprinter", (PyCFunction)_testcapi_pyfile_newstdprinter, METH_O, _testcapi_pyfile_newstdprinter__doc__},
+
+static PyObject *
+_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd);
+
+static PyObject *
+_testcapi_pyfile_newstdprinter(PyObject *module, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int fd;
+
+ fd = PyLong_AsInt(arg);
+ if (fd == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_newstdprinter_impl(module, fd);
+
+exit:
+ return return_value;
+}
+/*[clinic end generated code: output=44002184a5d9dbb9 input=a9049054013a1b77]*/
diff --git a/Modules/_testcapi/file.c b/Modules/_testcapi/file.c
index 634563f6ea12cb..bfb794d9ceaf77 100644
--- a/Modules/_testcapi/file.c
+++ b/Modules/_testcapi/file.c
@@ -1,17 +1,37 @@
#include "parts.h"
#include "util.h"
+#include "clinic/file.c.h"
+
+
+/*[clinic input]
+module _testcapi
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/
+
+
+/*[clinic input]
+_testcapi.pyfile_newstdprinter
+
+ fd: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd)
+/*[clinic end generated code: output=8a2d1c57b6892db3 input=442f1824142262ea]*/
+{
+ return PyFile_NewStdPrinter(fd);
+}
static PyMethodDef test_methods[] = {
+ _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF
{NULL},
};
int
_PyTestCapi_Init_File(PyObject *m)
{
- if (PyModule_AddFunctions(m, test_methods) < 0){
- return -1;
- }
-
- return 0;
+ return PyModule_AddFunctions(m, test_methods);
}
diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c
index b8edf8078a741d..b183df7751d8db 100644
--- a/Modules/_testlimitedcapi.c
+++ b/Modules/_testlimitedcapi.c
@@ -83,5 +83,8 @@ PyInit__testlimitedcapi(void)
if (_PyTestLimitedCAPI_Init_VectorcallLimited(mod) < 0) {
return NULL;
}
+ if (_PyTestLimitedCAPI_Init_File(mod) < 0) {
+ return NULL;
+ }
return mod;
}
diff --git a/Modules/_testlimitedcapi/clinic/file.c.h b/Modules/_testlimitedcapi/clinic/file.c.h
new file mode 100644
index 00000000000000..663619eead2a3a
--- /dev/null
+++ b/Modules/_testlimitedcapi/clinic/file.c.h
@@ -0,0 +1,81 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(_testcapi_pyfile_getline__doc__,
+"pyfile_getline($module, file, n, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_GETLINE_METHODDEF \
+ {"pyfile_getline", (PyCFunction)(void(*)(void))_testcapi_pyfile_getline, METH_FASTCALL, _testcapi_pyfile_getline__doc__},
+
+static PyObject *
+_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n);
+
+static PyObject *
+_testcapi_pyfile_getline(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *file;
+ int n;
+
+ if (nargs != 2) {
+ PyErr_Format(PyExc_TypeError, "pyfile_getline expected 2 arguments, got %zd", nargs);
+ goto exit;
+ }
+ file = args[0];
+ n = PyLong_AsInt(args[1]);
+ if (n == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_getline_impl(module, file, n);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_testcapi_pyfile_writeobject__doc__,
+"pyfile_writeobject($module, obj, file, flags, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF \
+ {"pyfile_writeobject", (PyCFunction)(void(*)(void))_testcapi_pyfile_writeobject, METH_FASTCALL, _testcapi_pyfile_writeobject__doc__},
+
+static PyObject *
+_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj,
+ PyObject *file, int flags);
+
+static PyObject *
+_testcapi_pyfile_writeobject(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *obj;
+ PyObject *file;
+ int flags;
+
+ if (nargs != 3) {
+ PyErr_Format(PyExc_TypeError, "pyfile_writeobject expected 3 arguments, got %zd", nargs);
+ goto exit;
+ }
+ obj = args[0];
+ file = args[1];
+ flags = PyLong_AsInt(args[2]);
+ if (flags == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_writeobject_impl(module, obj, file, flags);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_testcapi_pyobject_asfiledescriptor__doc__,
+"pyobject_asfiledescriptor($module, obj, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF \
+ {"pyobject_asfiledescriptor", (PyCFunction)_testcapi_pyobject_asfiledescriptor, METH_O, _testcapi_pyobject_asfiledescriptor__doc__},
+/*[clinic end generated code: output=ea572aaaa01aec7b input=a9049054013a1b77]*/
diff --git a/Modules/_testlimitedcapi/file.c b/Modules/_testlimitedcapi/file.c
new file mode 100644
index 00000000000000..e082e3c6700ee7
--- /dev/null
+++ b/Modules/_testlimitedcapi/file.c
@@ -0,0 +1,128 @@
+#include "pyconfig.h" // Py_GIL_DISABLED
+#ifndef Py_GIL_DISABLED
+ // Need limited C API 3.13 for PyLong_AsInt()
+# define Py_LIMITED_API 0x030d0000
+#endif
+
+#include "parts.h"
+#include "util.h"
+#include "clinic/file.c.h"
+
+
+/*[clinic input]
+module _testcapi
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/
+
+
+static PyObject *
+pyfile_fromfd(PyObject *module, PyObject *args)
+{
+ int fd;
+ const char *name;
+ Py_ssize_t size;
+ const char *mode;
+ int buffering;
+ const char *encoding;
+ const char *errors;
+ const char *newline;
+ int closefd;
+ if (!PyArg_ParseTuple(args,
+ "iz#z#"
+ "iz#z#"
+ "z#i",
+ &fd, &name, &size, &mode, &size,
+ &buffering, &encoding, &size, &errors, &size,
+ &newline, &size, &closefd)) {
+ return NULL;
+ }
+
+ return PyFile_FromFd(fd, name, mode, buffering,
+ encoding, errors, newline, closefd);
+}
+
+
+/*[clinic input]
+_testcapi.pyfile_getline
+
+ file: object
+ n: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n)
+/*[clinic end generated code: output=137fde2774563266 input=df26686148b3657e]*/
+{
+ return PyFile_GetLine(file, n);
+}
+
+
+/*[clinic input]
+_testcapi.pyfile_writeobject
+
+ obj: object
+ file: object
+ flags: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj,
+ PyObject *file, int flags)
+/*[clinic end generated code: output=ebb4d802e3db489c input=64a34a3e75b9935a]*/
+{
+ NULLABLE(obj);
+ NULLABLE(file);
+ RETURN_INT(PyFile_WriteObject(obj, file, flags));
+}
+
+
+static PyObject *
+pyfile_writestring(PyObject *module, PyObject *args)
+{
+ const char *str;
+ Py_ssize_t size;
+ PyObject *file;
+ if (!PyArg_ParseTuple(args, "z#O", &str, &size, &file)) {
+ return NULL;
+ }
+ NULLABLE(file);
+
+ RETURN_INT(PyFile_WriteString(str, file));
+}
+
+
+/*[clinic input]
+_testcapi.pyobject_asfiledescriptor
+
+ obj: object
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyobject_asfiledescriptor(PyObject *module, PyObject *obj)
+/*[clinic end generated code: output=2d640c6a1970c721 input=45fa1171d62b18d7]*/
+{
+ NULLABLE(obj);
+ RETURN_INT(PyObject_AsFileDescriptor(obj));
+}
+
+
+static PyMethodDef test_methods[] = {
+ {"pyfile_fromfd", pyfile_fromfd, METH_VARARGS},
+ _TESTCAPI_PYFILE_GETLINE_METHODDEF
+ _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF
+ {"pyfile_writestring", pyfile_writestring, METH_VARARGS},
+ _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF
+ {NULL},
+};
+
+int
+_PyTestLimitedCAPI_Init_File(PyObject *m)
+{
+ return PyModule_AddFunctions(m, test_methods);
+}
diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h
index 1d4cbcdfa94320..11b2e5c6b833bb 100644
--- a/Modules/_testlimitedcapi/parts.h
+++ b/Modules/_testlimitedcapi/parts.h
@@ -40,5 +40,6 @@ int _PyTestLimitedCAPI_Init_Sys(PyObject *module);
int _PyTestLimitedCAPI_Init_Tuple(PyObject *module);
int _PyTestLimitedCAPI_Init_Unicode(PyObject *module);
int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module);
+int _PyTestLimitedCAPI_Init_File(PyObject *module);
#endif // Py_TESTLIMITEDCAPI_PARTS_H
diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj
index 9cff1bb481ff40..a5e0be93ab9390 100644
--- a/PCbuild/_testlimitedcapi.vcxproj
+++ b/PCbuild/_testlimitedcapi.vcxproj
@@ -112,6 +112,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
+ <ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters
index 78828c28d98f22..4b3521afc06158 100644
--- a/PCbuild/_testlimitedcapi.vcxproj.filters
+++ b/PCbuild/_testlimitedcapi.vcxproj.filters
@@ -28,6 +28,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\tuple.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
+ <ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
<ClCompile Include="..\Modules\_testlimitedcapi.c" />
</ItemGroup>
<ItemGroup>
1
0
https://github.com/python/cpython/commit/4ca9fc08f89bf7172d41e523d9e520eb17…
commit: 4ca9fc08f89bf7172d41e523d9e520eb1729ee8c
branch: main
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-30T18:05:32+01:00
summary:
gh-111495: Add PyFile tests (#129449)
Add tests for the following functions in test_capi.test_file:
* PyFile_FromFd()
* PyFile_GetLine()
* PyFile_NewStdPrinter()
* PyFile_WriteObject()
* PyFile_WriteString()
* PyObject_AsFileDescriptor()
Add Modules/_testlimitedcapi/file.c file.
Remove test_embed.StdPrinterTests which became redundant.
files:
A Modules/_testlimitedcapi/clinic/file.c.h
A Modules/_testlimitedcapi/file.c
M Lib/test/test_capi/test_file.py
M Lib/test/test_embed.py
M Modules/Setup.stdlib.in
M Modules/_testcapi/clinic/file.c.h
M Modules/_testcapi/file.c
M Modules/_testlimitedcapi.c
M Modules/_testlimitedcapi/parts.h
M PCbuild/_testlimitedcapi.vcxproj
M PCbuild/_testlimitedcapi.vcxproj.filters
diff --git a/Lib/test/test_capi/test_file.py b/Lib/test/test_capi/test_file.py
index a67a5121c4588b..b5767756992861 100644
--- a/Lib/test/test_capi/test_file.py
+++ b/Lib/test/test_capi/test_file.py
@@ -1,26 +1,242 @@
+import io
import os
import unittest
+import warnings
from test import support
-from test.support import import_helper, os_helper
+from test.support import import_helper, os_helper, warnings_helper
-_testcapi = import_helper.import_module('_testcapi')
+_testcapi = import_helper.import_module('_testcapi')
+_testlimitedcapi = import_helper.import_module('_testlimitedcapi')
+_io = import_helper.import_module('_io')
NULL = None
+STDOUT_FD = 1
+
+with open(__file__, 'rb') as fp:
+ FIRST_LINE = next(fp).decode()
+FIRST_LINE_NORM = FIRST_LINE.rstrip() + '\n'
class CAPIFileTest(unittest.TestCase):
+ def test_pyfile_fromfd(self):
+ # Test PyFile_FromFd() which is a thin wrapper to _io.open()
+ pyfile_fromfd = _testlimitedcapi.pyfile_fromfd
+ filename = __file__
+ with open(filename, "rb") as fp:
+ fd = fp.fileno()
+
+ # FileIO
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "rb", 0, NULL, NULL, NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.FileIO)
+ self.assertEqual(obj.readline(), FIRST_LINE.encode())
+ finally:
+ obj.close()
+
+ # BufferedReader
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "rb", 1024, NULL, NULL, NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.BufferedReader)
+ self.assertEqual(obj.readline(), FIRST_LINE.encode())
+ finally:
+ obj.close()
+
+ # TextIOWrapper
+ fp.seek(0)
+ obj = pyfile_fromfd(fd, filename, "r", 1,
+ "utf-8", "replace", NULL, 0)
+ try:
+ self.assertIsInstance(obj, _io.TextIOWrapper)
+ self.assertEqual(obj.encoding, "utf-8")
+ self.assertEqual(obj.errors, "replace")
+ self.assertEqual(obj.readline(), FIRST_LINE_NORM)
+ finally:
+ obj.close()
+
+ def test_pyfile_getline(self):
+ # Test PyFile_GetLine(file, n): call file.readline()
+ # and strip "\n" suffix if n < 0.
+ pyfile_getline = _testlimitedcapi.pyfile_getline
+
+ # Test Unicode
+ with open(__file__, "r") as fp:
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, -1),
+ FIRST_LINE_NORM.rstrip('\n'))
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 0),
+ FIRST_LINE_NORM)
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 6),
+ FIRST_LINE_NORM[:6])
+
+ # Test bytes
+ with open(__file__, "rb") as fp:
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, -1),
+ FIRST_LINE.rstrip('\n').encode())
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 0),
+ FIRST_LINE.encode())
+ fp.seek(0)
+ self.assertEqual(pyfile_getline(fp, 6),
+ FIRST_LINE.encode()[:6])
+
+ def test_pyfile_writestring(self):
+ # Test PyFile_WriteString(str, file): call file.write(str)
+ writestr = _testlimitedcapi.pyfile_writestring
+
+ with io.StringIO() as fp:
+ self.assertEqual(writestr("a\xe9\u20ac\U0010FFFF".encode(), fp), 0)
+ with self.assertRaises(UnicodeDecodeError):
+ writestr(b"\xff", fp)
+ with self.assertRaises(UnicodeDecodeError):
+ writestr("\udc80".encode("utf-8", "surrogatepass"), fp)
+
+ text = fp.getvalue()
+ self.assertEqual(text, "a\xe9\u20ac\U0010FFFF")
+
+ with self.assertRaises(SystemError):
+ writestr(b"abc", NULL)
+
+ def test_pyfile_writeobject(self):
+ # Test PyFile_WriteObject(obj, file, flags):
+ # - Call file.write(str(obj)) if flags equals Py_PRINT_RAW.
+ # - Call file.write(repr(obj)) otherwise.
+ writeobject = _testlimitedcapi.pyfile_writeobject
+ Py_PRINT_RAW = 1
+
+ with io.StringIO() as fp:
+ # Test flags=Py_PRINT_RAW
+ self.assertEqual(writeobject("raw", fp, Py_PRINT_RAW), 0)
+ writeobject(NULL, fp, Py_PRINT_RAW)
+
+ # Test flags=0
+ self.assertEqual(writeobject("repr", fp, 0), 0)
+ writeobject(NULL, fp, 0)
+
+ text = fp.getvalue()
+ self.assertEqual(text, "raw<NULL>'repr'<NULL>")
+
+ # invalid file type
+ for invalid_file in (123, "abc", object()):
+ with self.subTest(file=invalid_file):
+ with self.assertRaises(AttributeError):
+ writeobject("abc", invalid_file, Py_PRINT_RAW)
+
+ with self.assertRaises(TypeError):
+ writeobject("abc", NULL, 0)
+
+ def test_pyobject_asfiledescriptor(self):
+ # Test PyObject_AsFileDescriptor(obj):
+ # - Return obj if obj is an integer.
+ # - Return obj.fileno() otherwise.
+ # File descriptor must be >= 0.
+ asfd = _testlimitedcapi.pyobject_asfiledescriptor
+
+ self.assertEqual(asfd(123), 123)
+ self.assertEqual(asfd(0), 0)
+
+ with open(__file__, "rb") as fp:
+ self.assertEqual(asfd(fp), fp.fileno())
+
+ # bool emits RuntimeWarning
+ msg = r"bool is used as a file descriptor"
+ with warnings_helper.check_warnings((msg, RuntimeWarning)):
+ self.assertEqual(asfd(True), 1)
+
+ class FakeFile:
+ def __init__(self, fd):
+ self.fd = fd
+ def fileno(self):
+ return self.fd
+
+ # file descriptor must be positive
+ with self.assertRaises(ValueError):
+ asfd(-1)
+ with self.assertRaises(ValueError):
+ asfd(FakeFile(-1))
+
+ # fileno() result must be an integer
+ with self.assertRaises(TypeError):
+ asfd(FakeFile("text"))
+
+ # unsupported types
+ for obj in ("string", ["list"], object()):
+ with self.subTest(obj=obj):
+ with self.assertRaises(TypeError):
+ asfd(obj)
+
+ # CRASHES asfd(NULL)
+
+ def test_pyfile_newstdprinter(self):
+ # Test PyFile_NewStdPrinter()
+ pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
+
+ file = pyfile_newstdprinter(STDOUT_FD)
+ self.assertEqual(file.closed, False)
+ self.assertIsNone(file.encoding)
+ self.assertEqual(file.mode, "w")
+
+ self.assertEqual(file.fileno(), STDOUT_FD)
+ self.assertEqual(file.isatty(), os.isatty(STDOUT_FD))
+
+ # flush() is a no-op
+ self.assertIsNone(file.flush())
+
+ # close() is a no-op
+ self.assertIsNone(file.close())
+ self.assertEqual(file.closed, False)
+
+ support.check_disallow_instantiation(self, type(file))
+
+ def test_pyfile_newstdprinter_write(self):
+ # Test the write() method of PyFile_NewStdPrinter()
+ pyfile_newstdprinter = _testcapi.pyfile_newstdprinter
+
+ filename = os_helper.TESTFN
+ self.addCleanup(os_helper.unlink, filename)
+
+ try:
+ old_stdout = os.dup(STDOUT_FD)
+ except OSError as exc:
+ # os.dup(STDOUT_FD) is not supported on WASI
+ self.skipTest(f"os.dup() failed with {exc!r}")
+
+ try:
+ with open(filename, "wb") as fp:
+ # PyFile_NewStdPrinter() only accepts fileno(stdout)
+ # or fileno(stderr) file descriptor.
+ fd = fp.fileno()
+ os.dup2(fd, STDOUT_FD)
+
+ file = pyfile_newstdprinter(STDOUT_FD)
+ self.assertEqual(file.write("text"), 4)
+ # The surrogate character is encoded with
+ # the "surrogateescape" error handler
+ self.assertEqual(file.write("[\udc80]"), 8)
+ finally:
+ os.dup2(old_stdout, STDOUT_FD)
+ os.close(old_stdout)
+
+ with open(filename, "r") as fp:
+ self.assertEqual(fp.read(), "text[\\udc80]")
+
def test_py_fopen(self):
# Test Py_fopen() and Py_fclose()
+ py_fopen = _testcapi.py_fopen
with open(__file__, "rb") as fp:
source = fp.read()
for filename in (__file__, os.fsencode(__file__)):
with self.subTest(filename=filename):
- data = _testcapi.py_fopen(filename, "rb")
+ data = py_fopen(filename, "rb")
self.assertEqual(data, source[:256])
- data = _testcapi.py_fopen(os_helper.FakePath(filename), "rb")
+ data = py_fopen(os_helper.FakePath(filename), "rb")
self.assertEqual(data, source[:256])
filenames = [
@@ -43,41 +259,46 @@ def test_py_fopen(self):
filename = None
continue
try:
- data = _testcapi.py_fopen(filename, "rb")
+ data = py_fopen(filename, "rb")
self.assertEqual(data, source[:256])
finally:
os_helper.unlink(filename)
# embedded null character/byte in the filename
with self.assertRaises(ValueError):
- _testcapi.py_fopen("a\x00b", "rb")
+ py_fopen("a\x00b", "rb")
with self.assertRaises(ValueError):
- _testcapi.py_fopen(b"a\x00b", "rb")
+ py_fopen(b"a\x00b", "rb")
# non-ASCII mode failing with "Invalid argument"
with self.assertRaises(OSError):
- _testcapi.py_fopen(__file__, b"\xc2\x80")
+ py_fopen(__file__, b"\xc2\x80")
with self.assertRaises(OSError):
# \x98 is invalid in cp1250, cp1251, cp1257
# \x9d is invalid in cp1252-cp1255, cp1258
- _testcapi.py_fopen(__file__, b"\xc2\x98\xc2\x9d")
+ py_fopen(__file__, b"\xc2\x98\xc2\x9d")
# UnicodeDecodeError can come from the audit hook code
with self.assertRaises((UnicodeDecodeError, OSError)):
- _testcapi.py_fopen(__file__, b"\x98\x9d")
+ py_fopen(__file__, b"\x98\x9d")
# invalid filename type
for invalid_type in (123, object()):
with self.subTest(filename=invalid_type):
with self.assertRaises(TypeError):
- _testcapi.py_fopen(invalid_type, "rb")
+ py_fopen(invalid_type, "rb")
if support.MS_WINDOWS:
with self.assertRaises(OSError):
# On Windows, the file mode is limited to 10 characters
- _testcapi.py_fopen(__file__, "rt+, ccs=UTF-8")
+ py_fopen(__file__, "rt+, ccs=UTF-8")
+
+ # CRASHES py_fopen(NULL, 'rb')
+ # CRASHES py_fopen(__file__, NULL)
+
+ # TODO: Test Py_UniversalNewlineFgets()
- # CRASHES _testcapi.py_fopen(NULL, 'rb')
- # CRASHES _testcapi.py_fopen(__file__, NULL)
+ # PyFile_SetOpenCodeHook() and PyFile_OpenCode() are tested by
+ # test_embed.test_open_code_hook()
if __name__ == "__main__":
diff --git a/Lib/test/test_embed.py b/Lib/test/test_embed.py
index f41c45ec4d9cdd..72221379a00051 100644
--- a/Lib/test/test_embed.py
+++ b/Lib/test/test_embed.py
@@ -1985,56 +1985,5 @@ def test_presite(self):
self.assertIn("unique-python-message", out)
-class StdPrinterTests(EmbeddingTestsMixin, unittest.TestCase):
- # Test PyStdPrinter_Type which is used by _PySys_SetPreliminaryStderr():
- # "Set up a preliminary stderr printer until we have enough
- # infrastructure for the io module in place."
-
- STDOUT_FD = 1
-
- def create_printer(self, fd):
- ctypes = import_helper.import_module('ctypes')
- PyFile_NewStdPrinter = ctypes.pythonapi.PyFile_NewStdPrinter
- PyFile_NewStdPrinter.argtypes = (ctypes.c_int,)
- PyFile_NewStdPrinter.restype = ctypes.py_object
- return PyFile_NewStdPrinter(fd)
-
- def test_write(self):
- message = "unicode:\xe9-\u20ac-\udc80!\n"
-
- stdout_fd = self.STDOUT_FD
- stdout_fd_copy = os.dup(stdout_fd)
- self.addCleanup(os.close, stdout_fd_copy)
-
- rfd, wfd = os.pipe()
- self.addCleanup(os.close, rfd)
- self.addCleanup(os.close, wfd)
- try:
- # PyFile_NewStdPrinter() only accepts fileno(stdout)
- # or fileno(stderr) file descriptor.
- os.dup2(wfd, stdout_fd)
-
- printer = self.create_printer(stdout_fd)
- printer.write(message)
- finally:
- os.dup2(stdout_fd_copy, stdout_fd)
-
- data = os.read(rfd, 100)
- self.assertEqual(data, message.encode('utf8', 'backslashreplace'))
-
- def test_methods(self):
- fd = self.STDOUT_FD
- printer = self.create_printer(fd)
- self.assertEqual(printer.fileno(), fd)
- self.assertEqual(printer.isatty(), os.isatty(fd))
- printer.flush() # noop
- printer.close() # noop
-
- def test_disallow_instantiation(self):
- fd = self.STDOUT_FD
- printer = self.create_printer(fd)
- support.check_disallow_instantiation(self, type(printer))
-
-
if __name__ == "__main__":
unittest.main()
diff --git a/Modules/Setup.stdlib.in b/Modules/Setup.stdlib.in
index 563bbc1bda6223..b31bf2734e4fd1 100644
--- a/Modules/Setup.stdlib.in
+++ b/Modules/Setup.stdlib.in
@@ -163,7 +163,7 @@
@MODULE__TESTBUFFER_TRUE@_testbuffer _testbuffer.c
@MODULE__TESTINTERNALCAPI_TRUE@_testinternalcapi _testinternalcapi.c _testinternalcapi/test_lock.c _testinternalcapi/pytime.c _testinternalcapi/set.c _testinternalcapi/test_critical_sections.c
@MODULE__TESTCAPI_TRUE@_testcapi _testcapimodule.c _testcapi/vectorcall.c _testcapi/heaptype.c _testcapi/abstract.c _testcapi/unicode.c _testcapi/dict.c _testcapi/set.c _testcapi/list.c _testcapi/tuple.c _testcapi/getargs.c _testcapi/datetime.c _testcapi/docstring.c _testcapi/mem.c _testcapi/watchers.c _testcapi/long.c _testcapi/float.c _testcapi/complex.c _testcapi/numbers.c _testcapi/structmember.c _testcapi/exceptions.c _testcapi/code.c _testcapi/buffer.c _testcapi/pyatomic.c _testcapi/run.c _testcapi/file.c _testcapi/codec.c _testcapi/immortal.c _testcapi/gc.c _testcapi/hash.c _testcapi/time.c _testcapi/bytes.c _testcapi/object.c _testcapi/monitoring.c _testcapi/config.c _testcapi/import.c
-@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c
+@MODULE__TESTLIMITEDCAPI_TRUE@_testlimitedcapi _testlimitedcapi.c _testlimitedcapi/abstract.c _testlimitedcapi/bytearray.c _testlimitedcapi/bytes.c _testlimitedcapi/codec.c _testlimitedcapi/complex.c _testlimitedcapi/dict.c _testlimitedcapi/eval.c _testlimitedcapi/float.c _testlimitedcapi/heaptype_relative.c _testlimitedcapi/import.c _testlimitedcapi/list.c _testlimitedcapi/long.c _testlimitedcapi/object.c _testlimitedcapi/pyos.c _testlimitedcapi/set.c _testlimitedcapi/sys.c _testlimitedcapi/tuple.c _testlimitedcapi/unicode.c _testlimitedcapi/vectorcall_limited.c _testlimitedcapi/version.c _testlimitedcapi/file.c
@MODULE__TESTCLINIC_TRUE@_testclinic _testclinic.c
@MODULE__TESTCLINIC_LIMITED_TRUE@_testclinic_limited _testclinic_limited.c
diff --git a/Modules/_testcapi/clinic/file.c.h b/Modules/_testcapi/clinic/file.c.h
index fddbf48071bd3b..6efb6b47353443 100644
--- a/Modules/_testcapi/clinic/file.c.h
+++ b/Modules/_testcapi/clinic/file.c.h
@@ -4,6 +4,33 @@ preserve
#include "pycore_modsupport.h" // _PyArg_CheckPositional()
+PyDoc_STRVAR(_testcapi_pyfile_newstdprinter__doc__,
+"pyfile_newstdprinter($module, fd, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF \
+ {"pyfile_newstdprinter", (PyCFunction)_testcapi_pyfile_newstdprinter, METH_O, _testcapi_pyfile_newstdprinter__doc__},
+
+static PyObject *
+_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd);
+
+static PyObject *
+_testcapi_pyfile_newstdprinter(PyObject *module, PyObject *arg)
+{
+ PyObject *return_value = NULL;
+ int fd;
+
+ fd = PyLong_AsInt(arg);
+ if (fd == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_newstdprinter_impl(module, fd);
+
+exit:
+ return return_value;
+}
+
PyDoc_STRVAR(_testcapi_py_fopen__doc__,
"py_fopen($module, path, mode, /)\n"
"--\n"
@@ -34,4 +61,4 @@ _testcapi_py_fopen(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
exit:
return return_value;
}
-/*[clinic end generated code: output=c4dc92400306c3eb input=a9049054013a1b77]*/
+/*[clinic end generated code: output=e943bbd7f181d079 input=a9049054013a1b77]*/
diff --git a/Modules/_testcapi/file.c b/Modules/_testcapi/file.c
index d15173fc7959e5..060e0f50598d7e 100644
--- a/Modules/_testcapi/file.c
+++ b/Modules/_testcapi/file.c
@@ -5,11 +5,29 @@
#include "util.h"
#include "clinic/file.c.h"
+
/*[clinic input]
module _testcapi
[clinic start generated code]*/
/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/
+
+/*[clinic input]
+_testcapi.pyfile_newstdprinter
+
+ fd: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_newstdprinter_impl(PyObject *module, int fd)
+/*[clinic end generated code: output=8a2d1c57b6892db3 input=442f1824142262ea]*/
+{
+ return PyFile_NewStdPrinter(fd);
+}
+
+
/*[clinic input]
_testcapi.py_fopen
@@ -38,7 +56,9 @@ _testcapi_py_fopen_impl(PyObject *module, PyObject *path, const char *mode,
return PyBytes_FromStringAndSize(buffer, size);
}
+
static PyMethodDef test_methods[] = {
+ _TESTCAPI_PYFILE_NEWSTDPRINTER_METHODDEF
_TESTCAPI_PY_FOPEN_METHODDEF
{NULL},
};
@@ -46,9 +66,5 @@ static PyMethodDef test_methods[] = {
int
_PyTestCapi_Init_File(PyObject *m)
{
- if (PyModule_AddFunctions(m, test_methods) < 0){
- return -1;
- }
-
- return 0;
+ return PyModule_AddFunctions(m, test_methods);
}
diff --git a/Modules/_testlimitedcapi.c b/Modules/_testlimitedcapi.c
index 82dac1c999470f..4dae99ec92a085 100644
--- a/Modules/_testlimitedcapi.c
+++ b/Modules/_testlimitedcapi.c
@@ -89,5 +89,8 @@ PyInit__testlimitedcapi(void)
if (_PyTestLimitedCAPI_Init_Version(mod) < 0) {
return NULL;
}
+ if (_PyTestLimitedCAPI_Init_File(mod) < 0) {
+ return NULL;
+ }
return mod;
}
diff --git a/Modules/_testlimitedcapi/clinic/file.c.h b/Modules/_testlimitedcapi/clinic/file.c.h
new file mode 100644
index 00000000000000..663619eead2a3a
--- /dev/null
+++ b/Modules/_testlimitedcapi/clinic/file.c.h
@@ -0,0 +1,81 @@
+/*[clinic input]
+preserve
+[clinic start generated code]*/
+
+PyDoc_STRVAR(_testcapi_pyfile_getline__doc__,
+"pyfile_getline($module, file, n, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_GETLINE_METHODDEF \
+ {"pyfile_getline", (PyCFunction)(void(*)(void))_testcapi_pyfile_getline, METH_FASTCALL, _testcapi_pyfile_getline__doc__},
+
+static PyObject *
+_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n);
+
+static PyObject *
+_testcapi_pyfile_getline(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *file;
+ int n;
+
+ if (nargs != 2) {
+ PyErr_Format(PyExc_TypeError, "pyfile_getline expected 2 arguments, got %zd", nargs);
+ goto exit;
+ }
+ file = args[0];
+ n = PyLong_AsInt(args[1]);
+ if (n == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_getline_impl(module, file, n);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_testcapi_pyfile_writeobject__doc__,
+"pyfile_writeobject($module, obj, file, flags, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF \
+ {"pyfile_writeobject", (PyCFunction)(void(*)(void))_testcapi_pyfile_writeobject, METH_FASTCALL, _testcapi_pyfile_writeobject__doc__},
+
+static PyObject *
+_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj,
+ PyObject *file, int flags);
+
+static PyObject *
+_testcapi_pyfile_writeobject(PyObject *module, PyObject *const *args, Py_ssize_t nargs)
+{
+ PyObject *return_value = NULL;
+ PyObject *obj;
+ PyObject *file;
+ int flags;
+
+ if (nargs != 3) {
+ PyErr_Format(PyExc_TypeError, "pyfile_writeobject expected 3 arguments, got %zd", nargs);
+ goto exit;
+ }
+ obj = args[0];
+ file = args[1];
+ flags = PyLong_AsInt(args[2]);
+ if (flags == -1 && PyErr_Occurred()) {
+ goto exit;
+ }
+ return_value = _testcapi_pyfile_writeobject_impl(module, obj, file, flags);
+
+exit:
+ return return_value;
+}
+
+PyDoc_STRVAR(_testcapi_pyobject_asfiledescriptor__doc__,
+"pyobject_asfiledescriptor($module, obj, /)\n"
+"--\n"
+"\n");
+
+#define _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF \
+ {"pyobject_asfiledescriptor", (PyCFunction)_testcapi_pyobject_asfiledescriptor, METH_O, _testcapi_pyobject_asfiledescriptor__doc__},
+/*[clinic end generated code: output=ea572aaaa01aec7b input=a9049054013a1b77]*/
diff --git a/Modules/_testlimitedcapi/file.c b/Modules/_testlimitedcapi/file.c
new file mode 100644
index 00000000000000..e082e3c6700ee7
--- /dev/null
+++ b/Modules/_testlimitedcapi/file.c
@@ -0,0 +1,128 @@
+#include "pyconfig.h" // Py_GIL_DISABLED
+#ifndef Py_GIL_DISABLED
+ // Need limited C API 3.13 for PyLong_AsInt()
+# define Py_LIMITED_API 0x030d0000
+#endif
+
+#include "parts.h"
+#include "util.h"
+#include "clinic/file.c.h"
+
+
+/*[clinic input]
+module _testcapi
+[clinic start generated code]*/
+/*[clinic end generated code: output=da39a3ee5e6b4b0d input=6361033e795369fc]*/
+
+
+static PyObject *
+pyfile_fromfd(PyObject *module, PyObject *args)
+{
+ int fd;
+ const char *name;
+ Py_ssize_t size;
+ const char *mode;
+ int buffering;
+ const char *encoding;
+ const char *errors;
+ const char *newline;
+ int closefd;
+ if (!PyArg_ParseTuple(args,
+ "iz#z#"
+ "iz#z#"
+ "z#i",
+ &fd, &name, &size, &mode, &size,
+ &buffering, &encoding, &size, &errors, &size,
+ &newline, &size, &closefd)) {
+ return NULL;
+ }
+
+ return PyFile_FromFd(fd, name, mode, buffering,
+ encoding, errors, newline, closefd);
+}
+
+
+/*[clinic input]
+_testcapi.pyfile_getline
+
+ file: object
+ n: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_getline_impl(PyObject *module, PyObject *file, int n)
+/*[clinic end generated code: output=137fde2774563266 input=df26686148b3657e]*/
+{
+ return PyFile_GetLine(file, n);
+}
+
+
+/*[clinic input]
+_testcapi.pyfile_writeobject
+
+ obj: object
+ file: object
+ flags: int
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyfile_writeobject_impl(PyObject *module, PyObject *obj,
+ PyObject *file, int flags)
+/*[clinic end generated code: output=ebb4d802e3db489c input=64a34a3e75b9935a]*/
+{
+ NULLABLE(obj);
+ NULLABLE(file);
+ RETURN_INT(PyFile_WriteObject(obj, file, flags));
+}
+
+
+static PyObject *
+pyfile_writestring(PyObject *module, PyObject *args)
+{
+ const char *str;
+ Py_ssize_t size;
+ PyObject *file;
+ if (!PyArg_ParseTuple(args, "z#O", &str, &size, &file)) {
+ return NULL;
+ }
+ NULLABLE(file);
+
+ RETURN_INT(PyFile_WriteString(str, file));
+}
+
+
+/*[clinic input]
+_testcapi.pyobject_asfiledescriptor
+
+ obj: object
+ /
+
+[clinic start generated code]*/
+
+static PyObject *
+_testcapi_pyobject_asfiledescriptor(PyObject *module, PyObject *obj)
+/*[clinic end generated code: output=2d640c6a1970c721 input=45fa1171d62b18d7]*/
+{
+ NULLABLE(obj);
+ RETURN_INT(PyObject_AsFileDescriptor(obj));
+}
+
+
+static PyMethodDef test_methods[] = {
+ {"pyfile_fromfd", pyfile_fromfd, METH_VARARGS},
+ _TESTCAPI_PYFILE_GETLINE_METHODDEF
+ _TESTCAPI_PYFILE_WRITEOBJECT_METHODDEF
+ {"pyfile_writestring", pyfile_writestring, METH_VARARGS},
+ _TESTCAPI_PYOBJECT_ASFILEDESCRIPTOR_METHODDEF
+ {NULL},
+};
+
+int
+_PyTestLimitedCAPI_Init_File(PyObject *m)
+{
+ return PyModule_AddFunctions(m, test_methods);
+}
diff --git a/Modules/_testlimitedcapi/parts.h b/Modules/_testlimitedcapi/parts.h
index 9efcd8dcb71e5b..60f6f03011a65c 100644
--- a/Modules/_testlimitedcapi/parts.h
+++ b/Modules/_testlimitedcapi/parts.h
@@ -42,5 +42,6 @@ int _PyTestLimitedCAPI_Init_Tuple(PyObject *module);
int _PyTestLimitedCAPI_Init_Unicode(PyObject *module);
int _PyTestLimitedCAPI_Init_VectorcallLimited(PyObject *module);
int _PyTestLimitedCAPI_Init_Version(PyObject *module);
+int _PyTestLimitedCAPI_Init_File(PyObject *module);
#endif // Py_TESTLIMITEDCAPI_PARTS_H
diff --git a/PCbuild/_testlimitedcapi.vcxproj b/PCbuild/_testlimitedcapi.vcxproj
index 87abff52493098..36c41fc9824fda 100644
--- a/PCbuild/_testlimitedcapi.vcxproj
+++ b/PCbuild/_testlimitedcapi.vcxproj
@@ -114,6 +114,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
+ <ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="..\PC\python_nt.rc" />
diff --git a/PCbuild/_testlimitedcapi.vcxproj.filters b/PCbuild/_testlimitedcapi.vcxproj.filters
index a975a508506905..62ecb2f70ffa2d 100644
--- a/PCbuild/_testlimitedcapi.vcxproj.filters
+++ b/PCbuild/_testlimitedcapi.vcxproj.filters
@@ -30,6 +30,7 @@
<ClCompile Include="..\Modules\_testlimitedcapi\unicode.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\vectorcall_limited.c" />
<ClCompile Include="..\Modules\_testlimitedcapi\version.c" />
+ <ClCompile Include="..\Modules\_testlimitedcapi\file.c" />
<ClCompile Include="..\Modules\_testlimitedcapi.c" />
</ItemGroup>
<ItemGroup>
1
0
https://github.com/python/cpython/commit/4e47e05045b7b05c7e166bda2afd601913…
commit: 4e47e05045b7b05c7e166bda2afd60191314e326
branch: main
author: Victor Stinner <vstinner(a)python.org>
committer: vstinner <vstinner(a)python.org>
date: 2025-01-30T16:09:38+01:00
summary:
gh-129354: Use PyErr_FormatUnraisable() function (#129435)
Replace PyErr_WriteUnraisable() with PyErr_FormatUnraisable().
files:
M Modules/_datetimemodule.c
M Modules/_testcapi/watchers.c
M Modules/_winapi.c
M Modules/atexitmodule.c
M Modules/overlapped.c
M Modules/signalmodule.c
M Objects/dictobject.c
M Objects/weakrefobject.c
M Python/jit.c
diff --git a/Modules/_datetimemodule.c b/Modules/_datetimemodule.c
index a486af7833c2c6..9d2abce203be7d 100644
--- a/Modules/_datetimemodule.c
+++ b/Modules/_datetimemodule.c
@@ -226,7 +226,7 @@ clear_current_module(PyInterpreterState *interp, PyObject *expected)
goto finally;
error:
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored when clearing _datetime module");
finally:
PyErr_SetRaisedException(exc);
diff --git a/Modules/_testcapi/watchers.c b/Modules/_testcapi/watchers.c
index 321d3aeffb6ad1..17a30355a266f0 100644
--- a/Modules/_testcapi/watchers.c
+++ b/Modules/_testcapi/watchers.c
@@ -428,7 +428,8 @@ allocate_too_many_code_watchers(PyObject *self, PyObject *args)
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < num_watchers; i++) {
if (PyCode_ClearWatcher(watcher_ids[i]) < 0) {
- PyErr_WriteUnraisable(Py_None);
+ PyErr_FormatUnraisable("Exception ignored when "
+ "clearing code watcher");
break;
}
}
@@ -609,7 +610,8 @@ allocate_too_many_func_watchers(PyObject *self, PyObject *args)
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < num_watchers; i++) {
if (PyFunction_ClearWatcher(watcher_ids[i]) < 0) {
- PyErr_WriteUnraisable(Py_None);
+ PyErr_FormatUnraisable("Exception ignored when "
+ "clearing function watcher");
break;
}
}
@@ -755,7 +757,8 @@ allocate_too_many_context_watchers(PyObject *self, PyObject *args)
PyObject *exc = PyErr_GetRaisedException();
for (int i = 0; i < num_watchers; i++) {
if (PyContext_ClearWatcher(watcher_ids[i]) < 0) {
- PyErr_WriteUnraisable(Py_None);
+ PyErr_FormatUnraisable("Exception ignored when "
+ "clearing context watcher");
break;
}
}
diff --git a/Modules/_winapi.c b/Modules/_winapi.c
index 260cab48091c16..56dd38401cb273 100644
--- a/Modules/_winapi.c
+++ b/Modules/_winapi.c
@@ -171,17 +171,16 @@ overlapped_dealloc(OverlappedObject *self)
{
/* The operation is no longer pending -- nothing to do. */
}
- else if (_Py_IsInterpreterFinalizing(_PyInterpreterState_GET()))
- {
+ else if (_Py_IsInterpreterFinalizing(_PyInterpreterState_GET())) {
/* The operation is still pending -- give a warning. This
will probably only happen on Windows XP. */
PyErr_SetString(PyExc_PythonFinalizationError,
"I/O operations still in flight while destroying "
"Overlapped object, the process may crash");
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored when deallocating "
+ "overlapped operation %R", self);
}
- else
- {
+ else {
/* The operation is still pending, but the process is
probably about to exit, so we need not worry too much
about memory leaks. Leaking self prevents a potential
diff --git a/Modules/atexitmodule.c b/Modules/atexitmodule.c
index 1b89b32ba907d7..1dbcc1089c32ab 100644
--- a/Modules/atexitmodule.c
+++ b/Modules/atexitmodule.c
@@ -110,7 +110,8 @@ atexit_callfuncs(struct atexit_state *state)
PyObject *copy = PyList_GetSlice(state->callbacks, 0, PyList_GET_SIZE(state->callbacks));
if (copy == NULL)
{
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored when "
+ "copying atexit callbacks");
return;
}
diff --git a/Modules/overlapped.c b/Modules/overlapped.c
index 308a0dab7fab1a..525b288f3ff54d 100644
--- a/Modules/overlapped.c
+++ b/Modules/overlapped.c
@@ -759,7 +759,8 @@ Overlapped_dealloc(OverlappedObject *self)
PyExc_RuntimeError,
"%R still has pending operation at "
"deallocation, the process may crash", self);
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored when deallocating "
+ "overlapped operation %R", self);
}
}
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index 0e53a36bca55f0..0cc9b35300dcca 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -1837,7 +1837,8 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate)
PyErr_Format(PyExc_OSError,
"Signal %i ignored due to race condition",
i);
- PyErr_WriteUnraisable(Py_None);
+ PyErr_FormatUnraisable("Exception ignored when "
+ "calling signal handler");
continue;
}
PyObject *arglist = NULL;
diff --git a/Objects/dictobject.c b/Objects/dictobject.c
index 733a10a2e80b18..a05359ca0b16ef 100644
--- a/Objects/dictobject.c
+++ b/Objects/dictobject.c
@@ -7351,7 +7351,8 @@ PyObject_ClearManagedDict(PyObject *obj)
if (set_or_clear_managed_dict(obj, NULL, true) < 0) {
/* Must be out of memory */
assert(PyErr_Occurred() == PyExc_MemoryError);
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored when "
+ "clearing an object managed dict");
/* Clear the dict */
PyDictObject *dict = _PyObject_GetManagedDict(obj);
Py_BEGIN_CRITICAL_SECTION2(dict, obj);
diff --git a/Objects/weakrefobject.c b/Objects/weakrefobject.c
index 0ee64ed70a63cd..8ced82ef36e30d 100644
--- a/Objects/weakrefobject.c
+++ b/Objects/weakrefobject.c
@@ -1042,7 +1042,7 @@ PyObject_ClearWeakRefs(PyObject *object)
PyObject *tuple = PyTuple_New(num_weakrefs * 2);
if (tuple == NULL) {
_PyWeakref_ClearWeakRefsNoCallbacks(object);
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored when clearing object weakrefs");
PyErr_SetRaisedException(exc);
return;
}
diff --git a/Python/jit.c b/Python/jit.c
index 7dd0da7a45055a..4b01112f9b0a5a 100644
--- a/Python/jit.c
+++ b/Python/jit.c
@@ -563,7 +563,7 @@ _PyJIT_Free(_PyExecutorObject *executor)
executor->jit_side_entry = NULL;
executor->jit_size = 0;
if (jit_free(memory, size)) {
- PyErr_WriteUnraisable(NULL);
+ PyErr_FormatUnraisable("Exception ignored when freeing JIT memory");
}
}
}
1
0