diff --git a/Doc/library/asyncio-task.rst b/Doc/library/asyncio-task.rst index 689a6a6d2e0bd92..1a13ad387f89b22 100644 --- a/Doc/library/asyncio-task.rst +++ b/Doc/library/asyncio-task.rst @@ -356,7 +356,7 @@ and reliable way to wait for all tasks in the group to finish. The signature matches that of :func:`asyncio.create_task`. If the task group is inactive (e.g. not yet entered, already finished, or in the process of shutting down), - we will close the given ``coro``. + we will close the given ``coro`` and raise :exc:`RuntimeError`. .. versionchanged:: 3.13 diff --git a/Doc/library/ctypes.rst b/Doc/library/ctypes.rst index acd6c2f97348e19..d76c449626fab4e 100644 --- a/Doc/library/ctypes.rst +++ b/Doc/library/ctypes.rst @@ -715,8 +715,11 @@ Specifying function pointers using type annotations and do not have to match the underlying C implementation. If the decorated function does not have a return type annotation, a - :exc:`ValueError` is raised. If the name of the function does not exist - in *dll*, an :exc:`AttributeError` is raised. + :exc:`ValueError` is raised. A :exc:`ValueError` is also raised if it has a + keyword-only, ``*args``, or ``**kwargs`` parameter, since + :attr:`~ctypes._CFuncPtr.argtypes` describes positional arguments only. If + the name of the function does not exist in *dll*, an :exc:`AttributeError` + is raised. For example:: diff --git a/Doc/reference/compound_stmts.rst b/Doc/reference/compound_stmts.rst index e74f3262ed540cd..28850ba801138f0 100644 --- a/Doc/reference/compound_stmts.rst +++ b/Doc/reference/compound_stmts.rst @@ -1235,7 +1235,7 @@ A function definition defines a user-defined function object (see section : | `parameter_list_no_posonly` parameter_list_no_posonly: `defparameter` ("," `defparameter`)* ["," [`parameter_list_starargs`]] : | `parameter_list_starargs` - parameter_list_starargs: "*" [`star_parameter`] ("," `defparameter`)* ["," [`parameter_star_kwargs`]] + parameter_list_starargs: "*" `star_parameter` ("," `defparameter`)* ["," [`parameter_star_kwargs`]] : | "*" ("," `defparameter`)+ ["," [`parameter_star_kwargs`]] : | `parameter_star_kwargs` parameter_star_kwargs: "**" `parameter` [","] diff --git a/Doc/whatsnew/3.14.rst b/Doc/whatsnew/3.14.rst index 64c6888d9947702..6e3e6331a1b7e88 100644 --- a/Doc/whatsnew/3.14.rst +++ b/Doc/whatsnew/3.14.rst @@ -648,7 +648,7 @@ Improved error messages :pep:`784`: Zstandard support in the standard library ----------------------------------------------------- -The new :mod:`!compression` package contains modules :mod:`!compression.lzma`, +The new :mod:`compression` package contains modules :mod:`!compression.lzma`, :mod:`!compression.bz2`, :mod:`!compression.gzip` and :mod:`!compression.zlib` which re-export the :mod:`lzma`, :mod:`bz2`, :mod:`gzip` and :mod:`zlib` modules respectively. The new import names under :mod:`!compression` are the @@ -657,7 +657,7 @@ the existing modules names have not been deprecated. Any deprecation or removal of the existing compression modules will occur no sooner than five years after the release of 3.14. -The new :mod:`!compression.zstd` module provides compression and decompression +The new :mod:`compression.zstd` module provides compression and decompression APIs for the Zstandard format via bindings to `Meta's zstd library `__. Zstandard is a widely adopted, highly efficient, and fast compression format. In addition to the APIs introduced in diff --git a/Lib/ctypes/util.py b/Lib/ctypes/util.py index 141d3fbe9382472..2b01506f3d4ffab 100644 --- a/Lib/ctypes/util.py +++ b/Lib/ctypes/util.py @@ -4,6 +4,7 @@ from dataclasses import dataclass lazy import functools +lazy import inspect lazy import shutil lazy import subprocess @@ -509,6 +510,13 @@ def decorator(func): except KeyError as error: raise ValueError(f"{name!r} missing return type annotation") from error + for param in inspect.signature(func).parameters.values(): + if param.kind not in (param.POSITIONAL_ONLY, + param.POSITIONAL_OR_KEYWORD): + raise ValueError(f"{name!r} has non-positional parameter " + f"{param.name!r}; argtypes describes " + f"positional arguments only") + ptr.restype = restype ptr.argtypes = tuple(annotations.values()) functools.update_wrapper(ptr, func, updated=()) diff --git a/Lib/test/.ruff.toml b/Lib/test/.ruff.toml index dca74eb6e14bbd0..da4f32a01bccf5d 100644 --- a/Lib/test/.ruff.toml +++ b/Lib/test/.ruff.toml @@ -14,10 +14,8 @@ extend-exclude = [ # New grammar constructions may not yet be recognized by Ruff, # and tests re-use the same names as only the grammar is being checked. "test_grammar.py", - # Lazy import syntax (PEP 810) is not yet supported by Ruff - "test_lazy_import/__init__.py", - "test_lazy_import/data/*.py", - "test_lazy_import/data/**/*.py", + # Intentional bad lazy import syntax + "test_lazy_import/data/badsyntax/*.py", # Unary plus literal pattern is not yet supported by Ruff (GH-145239) "test_patma.py", ] @@ -32,4 +30,5 @@ select = [ "*/**/__main__.py" = ["F401"] # Unused import "test_import/*.py" = ["F401"] # Unused import "test_importlib/*.py" = ["F401"] # Unused import +"test_lazy_import/**/*.py" = ["F401"] # Unused import "typinganndata/partialexecution/*.py" = ["F401"] # Unused import diff --git a/Lib/test/test_ctypes/test_funcptr.py b/Lib/test/test_ctypes/test_funcptr.py index 28ff34f9048454d..b4c27809b5333d5 100644 --- a/Lib/test/test_ctypes/test_funcptr.py +++ b/Lib/test/test_ctypes/test_funcptr.py @@ -153,6 +153,47 @@ def noexist(): def PyObject_GetAttrString(op: ctypes.py_object, attr: ctypes.c_char_p): pass + def test_wrap_dll_function_non_positional(self): + # argtypes describes positional arguments only, so a parameter that + # cannot be passed positionally is rejected. + regex = "'PyObject_GetAttr' has non-positional parameter" + + with self.assertRaisesRegex(ValueError, regex): + @wrap_dll_function(ctypes.pythonapi) + def PyObject_GetAttr(op: ctypes.py_object, attr: ctypes.py_object, + *args: ctypes.c_int) -> ctypes.py_object: + pass + + with self.assertRaisesRegex(ValueError, regex): + @wrap_dll_function(ctypes.pythonapi) + def PyObject_GetAttr(op: ctypes.py_object, attr: ctypes.py_object, + **kwargs: ctypes.c_int) -> ctypes.py_object: + pass + + with self.assertRaisesRegex(ValueError, regex): + @wrap_dll_function(ctypes.pythonapi) + def PyObject_GetAttr(op: ctypes.py_object, attr: ctypes.py_object, + *, kwonly: ctypes.c_int) -> ctypes.py_object: + pass + + with self.assertRaisesRegex(ValueError, regex): + @wrap_dll_function(ctypes.pythonapi) + def PyObject_GetAttr(op: ctypes.py_object, attr: ctypes.py_object, + *, kwonly) -> ctypes.py_object: + pass + + # Positional-only parameters have a positional counterpart, so they + # are accepted. + @wrap_dll_function(ctypes.pythonapi) + def PyObject_GetAttr(op: ctypes.py_object, attr: ctypes.py_object, + /) -> ctypes.py_object: + pass + + class Foo: + a = "abc" + + self.assertEqual(PyObject_GetAttr(Foo, "a"), "abc") + def test_wrap_dll_function_str_ann(self): from test.test_ctypes import wrap_str_ann version = wrap_str_ann.Py_GetVersion()