Compare commits

..

6 commits

Author SHA1 Message Date
bzoracler
379fd020a0
gh-138859: Account for ParamSpec defaults that are not lists … (#138868) 2025-10-16 05:30:36 -07:00
Jeffrey Bosboom
5a31024da4
gh-83714: Check for struct statx.stx_atomic_write_unit_max_opt in configure (#140185)
stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled
by the STATX_WRITE_ATOMIC mask bit added in Linux 6.11.  That's safe at
runtime because all kernels clear the reserved space in struct statx and
zero is a valid value for stx_atomic_write_unit_max_opt, and it avoids
allocating another mask bit, which are a limited resource.  But it also
means the kernel headers don't provide a way to check whether
stx_atomic_write_unit_max_opt exists, so add a configure check.
2025-10-16 13:40:47 +02:00
Petr Viktorin
ea4cc585cd
gh-139817: Fix refleak in TypeAliasType(qualname=non_string) (GH-140197) 2025-10-16 11:04:04 +00:00
Victor Stinner
4641925bf2
Set type names earlier in posixmodule.c (#140168) 2025-10-16 12:54:57 +02:00
Stan Ulbrych
7ac94fcb1d
gh-140170: Fix test_site with -s flag (GH-140179)
Commit
2025-10-16 12:49:08 +03:00
Serhiy Storchaka
2a2bc82cef
gh-130567: Remove optimistic allocation in locale.strxfrm() (GH-137143)
On modern systems, the result of wcsxfrm() is much larger the size of
the input string (from 4+2*n on Windows to 4+5*n on Linux for simple
ASCII strings), so optimistic allocation of the buffer of the same size
never works.

The exception is if the locale is "C" (or unset), but in that case the `wcsxfrm`
call should be fast (and calling `locale.strxfrm()` doesn't make too much
sense in the first place).
2025-10-16 09:54:41 +02:00
12 changed files with 81 additions and 44 deletions

View file

@ -3496,7 +3496,7 @@ features:
Maximum optimized size for direct I/O with torn-write protection.
.. availability:: Linux >= 4.11 with glibc >= 2.28 and build-time kernel
userspace API headers >= 6.11.
userspace API headers >= 6.16.
.. attribute:: stx_atomic_write_segments_max

View file

@ -164,6 +164,20 @@ def test_getcwdb(self):
self.assertIsInstance(cwd, bytes)
self.assertEqual(os.fsdecode(cwd), os.getcwd())
def test_type_fqdn(self):
def fqdn(obj):
return (obj.__module__, obj.__qualname__)
native = os.name
self.assertEqual(fqdn(os.stat_result), ("os", "stat_result"))
self.assertEqual(fqdn(os.times_result), (native, "times_result"))
if hasattr(os, "statvfs_result"):
self.assertEqual(fqdn(os.statvfs_result), ("os", "statvfs_result"))
if hasattr(os, "sched_param"):
self.assertEqual(fqdn(os.sched_param), (native, "sched_param"))
if hasattr(os, "waitid_result"):
self.assertEqual(fqdn(os.waitid_result), (native, "waitid_result"))
# Tests creating TESTFN
class FileTests(unittest.TestCase):

View file

@ -855,12 +855,15 @@ def get_excepted_output(self, *args):
return 10, None
def invoke_command_line(self, *args):
args = ["-m", "site", *args]
cmd_args = []
if sys.flags.no_user_site:
cmd_args.append("-s")
cmd_args.extend(["-m", "site", *args])
with EnvironmentVarGuard() as env:
env["PYTHONUTF8"] = "1"
env["PYTHONIOENCODING"] = "utf-8"
proc = spawn_python(*args, text=True, env=env,
proc = spawn_python(*cmd_args, text=True, env=env,
encoding='utf-8', errors='replace')
output = kill_python(proc)

View file

@ -762,6 +762,16 @@ class A(Generic[T, P, U]): ...
self.assertEqual(A[float, [range]].__args__, (float, (range,), float))
self.assertEqual(A[float, [range], int].__args__, (float, (range,), int))
def test_paramspec_and_typevar_specialization_2(self):
T = TypeVar("T")
P = ParamSpec('P', default=...)
U = TypeVar("U", default=float)
self.assertEqual(P.__default__, ...)
class A(Generic[T, P, U]): ...
self.assertEqual(A[float].__args__, (float, ..., float))
self.assertEqual(A[float, [range]].__args__, (float, (range,), float))
self.assertEqual(A[float, [range], int].__args__, (float, (range,), int))
def test_typevartuple_none(self):
U = TypeVarTuple('U')
U_None = TypeVarTuple('U_None', default=None)

View file

@ -1113,7 +1113,7 @@ def _paramspec_prepare_subst(self, alias, args):
params = alias.__parameters__
i = params.index(self)
if i == len(args) and self.has_default():
args = [*args, self.__default__]
args = (*args, self.__default__)
if i >= len(args):
raise TypeError(f"Too few arguments for {alias}")
# Special case where Z[[int, str, bool]] == Z[int, str, bool] in PEP 612.

View file

@ -0,0 +1 @@
Fix generic type parameterization raising a :exc:`TypeError` when omitting a :class:`ParamSpec` that has a default which is not a list of types.

View file

@ -455,35 +455,23 @@ _locale_strxfrm_impl(PyObject *module, PyObject *str)
goto exit;
}
/* assume no change in size, first */
n1 = n1 + 1;
/* Yet another +1 is needed to work around a platform bug in wcsxfrm()
* on macOS. See gh-130567. */
buf = PyMem_New(wchar_t, n1+1);
if (!buf) {
PyErr_NoMemory();
goto exit;
}
errno = 0;
n2 = wcsxfrm(buf, s, n1);
n2 = wcsxfrm(NULL, s, 0);
if (errno && errno != ERANGE) {
PyErr_SetFromErrno(PyExc_OSError);
goto exit;
}
if (n2 >= (size_t)n1) {
/* more space needed */
wchar_t * new_buf = PyMem_Realloc(buf, (n2+1)*sizeof(wchar_t));
if (!new_buf) {
PyErr_NoMemory();
goto exit;
}
buf = new_buf;
errno = 0;
n2 = wcsxfrm(buf, s, n2+1);
if (errno) {
PyErr_SetFromErrno(PyExc_OSError);
goto exit;
}
buf = PyMem_New(wchar_t, n2+1);
if (!buf) {
PyErr_NoMemory();
goto exit;
}
errno = 0;
n2 = wcsxfrm(buf, s, n2+1);
if (errno) {
PyErr_SetFromErrno(PyExc_OSError);
goto exit;
}
/* The result is just a sequence of integers, they are not necessary
Unicode code points, so PyUnicode_FromWideChar() cannot be used

View file

@ -2471,7 +2471,7 @@ static PyStructSequence_Field stat_result_fields[] = {
#endif
static PyStructSequence_Desc stat_result_desc = {
"stat_result", /* name */
"os.stat_result", /* name; see issue gh-63408 */
stat_result__doc__, /* doc */
stat_result_fields,
10
@ -2501,7 +2501,7 @@ static PyStructSequence_Field statvfs_result_fields[] = {
};
static PyStructSequence_Desc statvfs_result_desc = {
"statvfs_result", /* name */
"os.statvfs_result", /* name; see issue gh-63408 */
statvfs_result__doc__, /* doc */
statvfs_result_fields,
10
@ -2526,7 +2526,7 @@ static PyStructSequence_Field waitid_result_fields[] = {
};
static PyStructSequence_Desc waitid_result_desc = {
"waitid_result", /* name */
MODNAME ".waitid_result", /* name */
waitid_result__doc__, /* doc */
waitid_result_fields,
5
@ -3369,7 +3369,7 @@ static PyMemberDef pystatx_result_members[] = {
MM(stx_atomic_write_segments_max, Py_T_UINT, atomic_write_segments_max,
"maximum iovecs for direct I/O with torn-write protection"),
#endif
#if 0
#ifdef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
MM(stx_atomic_write_unit_max_opt, Py_T_UINT, atomic_write_unit_max_opt,
"maximum optimized size for direct I/O with torn-write protection"),
#endif
@ -8663,7 +8663,7 @@ static PyStructSequence_Field sched_param_fields[] = {
};
static PyStructSequence_Desc sched_param_desc = {
"sched_param", /* name */
MODNAME ".sched_param", /* name */
os_sched_param__doc__, /* doc */
sched_param_fields,
1
@ -11057,7 +11057,7 @@ and elapsed.\n\
See os.times for more information.");
static PyStructSequence_Desc times_result_desc = {
"times_result", /* name */
MODNAME ".times_result", /* name */
times_result__doc__, /* doc */
times_result_fields,
5
@ -18584,14 +18584,12 @@ posixmodule_exec(PyObject *m)
}
#if defined(HAVE_WAITID)
waitid_result_desc.name = MODNAME ".waitid_result";
state->WaitidResultType = (PyObject *)PyStructSequence_NewType(&waitid_result_desc);
if (PyModule_AddObjectRef(m, "waitid_result", state->WaitidResultType) < 0) {
return -1;
}
#endif
stat_result_desc.name = "os.stat_result"; /* see issue #19209 */
stat_result_desc.fields[7].name = PyStructSequence_UnnamedField;
stat_result_desc.fields[8].name = PyStructSequence_UnnamedField;
stat_result_desc.fields[9].name = PyStructSequence_UnnamedField;
@ -18602,14 +18600,12 @@ posixmodule_exec(PyObject *m)
state->statresult_new_orig = ((PyTypeObject *)state->StatResultType)->tp_new;
((PyTypeObject *)state->StatResultType)->tp_new = statresult_new;
statvfs_result_desc.name = "os.statvfs_result"; /* see issue #19209 */
state->StatVFSResultType = (PyObject *)PyStructSequence_NewType(&statvfs_result_desc);
if (PyModule_AddObjectRef(m, "statvfs_result", state->StatVFSResultType) < 0) {
return -1;
}
#if defined(HAVE_SCHED_SETPARAM) || defined(HAVE_SCHED_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDULER) || defined(POSIX_SPAWN_SETSCHEDPARAM)
sched_param_desc.name = MODNAME ".sched_param";
state->SchedParamType = (PyObject *)PyStructSequence_NewType(&sched_param_desc);
if (PyModule_AddObjectRef(m, "sched_param", state->SchedParamType) < 0) {
return -1;
@ -18641,7 +18637,6 @@ posixmodule_exec(PyObject *m)
return -1;
}
times_result_desc.name = MODNAME ".times_result";
state->TimesResultType = (PyObject *)PyStructSequence_NewType(&times_result_desc);
if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) {
return -1;

View file

@ -2123,11 +2123,6 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value,
return NULL;
}
PyObject *module = caller();
if (module == NULL) {
return NULL;
}
if (qualname == NULL || qualname == Py_None) {
// If qualname was not set directly, we use name instead.
qualname = name;
@ -2138,6 +2133,11 @@ typealias_new_impl(PyTypeObject *type, PyObject *name, PyObject *value,
}
}
PyObject *module = caller();
if (module == NULL) {
return NULL;
}
PyObject *ta = (PyObject *)typealias_alloc(
name, qualname, checked_params, NULL, value, module);
Py_DECREF(module);

15
configure generated vendored
View file

@ -25133,6 +25133,21 @@ printf "%s\n" "#define HAVE_SIGINFO_T_SI_BAND 1" >>confdefs.h
fi
if test "$ac_cv_func_statx" = yes; then
# stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by
# the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit
# doesn't imply having the member.
ac_fn_c_check_member "$LINENO" "struct statx" "stx_atomic_write_unit_max_opt" "ac_cv_member_struct_statx_stx_atomic_write_unit_max_opt" "$ac_includes_default"
if test "x$ac_cv_member_struct_statx_stx_atomic_write_unit_max_opt" = xyes
then :
printf "%s\n" "#define HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT 1" >>confdefs.h
fi
fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for time.h that defines altzone" >&5
printf %s "checking for time.h that defines altzone... " >&6; }
if test ${ac_cv_header_time_altzone+y}

View file

@ -5819,6 +5819,13 @@ AC_CHECK_MEMBERS([struct passwd.pw_gecos, struct passwd.pw_passwd], [], [], [[
# Issue #21085: In Cygwin, siginfo_t does not have si_band field.
AC_CHECK_MEMBERS([siginfo_t.si_band], [], [], [[@%:@include <signal.h>]])
if test "$ac_cv_func_statx" = yes; then
# stx_atomic_write_unit_max_opt was added in Linux 6.16, but is controlled by
# the STATX_WRITE_ATOMIC mask bit added in Linux 6.11, so having the mask bit
# doesn't imply having the member.
AC_CHECK_MEMBERS([struct statx.stx_atomic_write_unit_max_opt])
fi
AC_CACHE_CHECK([for time.h that defines altzone], [ac_cv_header_time_altzone], [
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[@%:@include <time.h>]], [[return altzone;]])],
[ac_cv_header_time_altzone=yes],

View file

@ -1330,6 +1330,10 @@
/* Define to 1 if 'pw_passwd' is a member of 'struct passwd'. */
#undef HAVE_STRUCT_PASSWD_PW_PASSWD
/* Define to 1 if 'stx_atomic_write_unit_max_opt' is a member of 'struct
statx'. */
#undef HAVE_STRUCT_STATX_STX_ATOMIC_WRITE_UNIT_MAX_OPT
/* Define to 1 if 'st_birthtime' is a member of 'struct stat'. */
#undef HAVE_STRUCT_STAT_ST_BIRTHTIME