diff --git a/Doc/library/os.rst b/Doc/library/os.rst index 1ac87b32badd78..c3ed6a976926fd 100644 --- a/Doc/library/os.rst +++ b/Doc/library/os.rst @@ -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 diff --git a/Doc/library/tkinter.rst b/Doc/library/tkinter.rst index f284988daf2d4e..be26af02bcee0d 100644 --- a/Doc/library/tkinter.rst +++ b/Doc/library/tkinter.rst @@ -392,7 +392,7 @@ by spaces. Without getting into too many details, notice the following: * Operations which are implemented as separate *commands* in Tcl (like ``grid`` or ``destroy``) are represented as *methods* on Tkinter widget objects. As you'll see shortly, at other times Tcl uses what appear to be - method calls on widget objects, which more closely mirror what would is + method calls on widget objects, which more closely mirror what is used in Tkinter. diff --git a/Lib/test/test_os/test_os.py b/Lib/test/test_os/test_os.py index dd6f89e81aac87..9bb4cb7e526b4f 100644 --- a/Lib/test/test_os/test_os.py +++ b/Lib/test/test_os/test_os.py @@ -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): diff --git a/Lib/test/test_site.py b/Lib/test/test_site.py index 32fcf3162e8efd..27ae3539b554ef 100644 --- a/Lib/test/test_site.py +++ b/Lib/test/test_site.py @@ -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) diff --git a/Lib/test/test_typing.py b/Lib/test/test_typing.py index bc7f14f90a7cf9..db0501d70e3442 100644 --- a/Lib/test/test_typing.py +++ b/Lib/test/test_typing.py @@ -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) diff --git a/Lib/typing.py b/Lib/typing.py index 03d5357d4cf51a..25234d2d707dd2 100644 --- a/Lib/typing.py +++ b/Lib/typing.py @@ -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. diff --git a/Misc/NEWS.d/next/Library/2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst b/Misc/NEWS.d/next/Library/2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst new file mode 100644 index 00000000000000..a5d4dd042fcd5b --- /dev/null +++ b/Misc/NEWS.d/next/Library/2025-09-13-12-19-17.gh-issue-138859.PxjIoN.rst @@ -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. diff --git a/Modules/posixmodule.c b/Modules/posixmodule.c index e2b7146237feb6..1338be22e18678 100644 --- a/Modules/posixmodule.c +++ b/Modules/posixmodule.c @@ -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(×_result_desc); if (PyModule_AddObjectRef(m, "times_result", state->TimesResultType) < 0) { return -1; diff --git a/Objects/typevarobject.c b/Objects/typevarobject.c index 8a3a1e9834583a..75a69d4bc3e019 100644 --- a/Objects/typevarobject.c +++ b/Objects/typevarobject.c @@ -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); diff --git a/configure b/configure index 28005cd1924be7..267981250cd7e2 100755 --- a/configure +++ b/configure @@ -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} diff --git a/configure.ac b/configure.ac index d20f6f8c40abeb..382591952ef68f 100644 --- a/configure.ac +++ b/configure.ac @@ -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 ]]) +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 ]], [[return altzone;]])], [ac_cv_header_time_altzone=yes], diff --git a/pyconfig.h.in b/pyconfig.h.in index 611408d88f05b5..092f96e7152e11 100644 --- a/pyconfig.h.in +++ b/pyconfig.h.in @@ -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