From 40ee61d8570a9a90f72b941121c062a03cccd0da Mon Sep 17 00:00:00 2001
From: sobolevn <mail@sobolevn.me>
Date: Wed, 9 Aug 2023 13:50:13 +0300
Subject: [PATCH 1/6] gh-107805: Fix signatures of module-level generated
 functions in `turtle`

---
 Lib/test/test_turtle.py                       | 27 ++++++++++++
 Lib/turtle.py                                 | 41 ++++++++++---------
 ...-08-09-13-49-37.gh-issue-107805.ezem0k.rst |  1 +
 3 files changed, 50 insertions(+), 19 deletions(-)
 create mode 100644 Misc/NEWS.d/next/Library/2023-08-09-13-49-37.gh-issue-107805.ezem0k.rst

diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py
index 3f9f129a3dd200..5df4568d44aa4b 100644
--- a/Lib/test/test_turtle.py
+++ b/Lib/test/test_turtle.py
@@ -461,5 +461,32 @@ def test_teleport(self):
             self.assertTrue(tpen.isdown())
 
 
+class TestModuleLevel(unittest.TestCase):
+    def test_all_signatures(self):
+        import inspect
+        import types
+
+        known_signatures = {
+            'teleport':
+                '(x=None, y=None, fill_gap: bool = False) -> None',
+            'clear': '()',
+            'reset': '(canvwidth=None, canvheight=None, bg=None)',
+            'bgcolor': '(*args)',
+            'pen': '(pen=None, **pendict)',
+        }
+
+        for name in turtle.__all__:
+            obj = getattr(turtle, name)
+            if not isinstance(obj, types.FunctionType):
+                continue
+
+            with self.subTest(name=name):
+                # All functions must produce correct signatures:
+                sig = inspect.signature(obj)
+
+                if name in known_signatures:
+                    self.assertEqual(str(sig), known_signatures[name])
+
+
 if __name__ == '__main__':
     unittest.main()
diff --git a/Lib/turtle.py b/Lib/turtle.py
index e542bc956897e9..3b1b7f330c1fd3 100644
--- a/Lib/turtle.py
+++ b/Lib/turtle.py
@@ -3920,28 +3920,31 @@ def getmethparlist(ob):
     function definition and the second is suitable for use in function
     call.  The "self" parameter is not included.
     """
-    defText = callText = ""
+    orig_sig = inspect.signature(ob)
     # bit of a hack for methods - turn it into a function
     # but we drop the "self" param.
     # Try and build one for Python defined functions
-    args, varargs, varkw = inspect.getargs(ob.__code__)
-    items2 = args[1:]
-    realArgs = args[1:]
-    defaults = ob.__defaults__ or []
-    defaults = ["=%r" % (value,) for value in defaults]
-    defaults = [""] * (len(realArgs)-len(defaults)) + defaults
-    items1 = [arg + dflt for arg, dflt in zip(realArgs, defaults)]
-    if varargs is not None:
-        items1.append("*" + varargs)
-        items2.append("*" + varargs)
-    if varkw is not None:
-        items1.append("**" + varkw)
-        items2.append("**" + varkw)
-    defText = ", ".join(items1)
-    defText = "(%s)" % defText
-    callText = ", ".join(items2)
-    callText = "(%s)" % callText
-    return defText, callText
+    func_sig = orig_sig.replace(
+        parameters=list(orig_sig.parameters.values())[1:],
+    )
+
+    call_args = []
+    for param in func_sig.parameters.values():
+        kind = param.kind
+        if kind in (
+            inspect.Parameter.POSITIONAL_ONLY,
+            inspect.Parameter.POSITIONAL_OR_KEYWORD,
+        ):
+            call_args.append(param.name)
+        if kind == inspect.Parameter.VAR_POSITIONAL:
+            call_args.append(f'*{param.name}')
+        if kind == inspect.Parameter.KEYWORD_ONLY:
+            call_args.append(f'{param.name}={param.name}')
+        if kind == inspect.Parameter.VAR_KEYWORD:
+            call_args.append(f'**{param.name}')
+    call_text = f'({', '.join(call_args)})'
+
+    return str(func_sig), call_text
 
 def _turtle_docrevise(docstr):
     """To reduce docstrings from RawTurtle class for functions
diff --git a/Misc/NEWS.d/next/Library/2023-08-09-13-49-37.gh-issue-107805.ezem0k.rst b/Misc/NEWS.d/next/Library/2023-08-09-13-49-37.gh-issue-107805.ezem0k.rst
new file mode 100644
index 00000000000000..263df68f8e5c80
--- /dev/null
+++ b/Misc/NEWS.d/next/Library/2023-08-09-13-49-37.gh-issue-107805.ezem0k.rst
@@ -0,0 +1 @@
+Fix signatures of module-level generated functions in :mod:`turtle`.

From 03ea67de37cb2b74ac26ce7a35a88951f229bc1d Mon Sep 17 00:00:00 2001
From: sobolevn <mail@sobolevn.me>
Date: Wed, 9 Aug 2023 14:12:41 +0300
Subject: [PATCH 2/6] Fix CI

---
 Lib/test/test_turtle.py | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py
index 5df4568d44aa4b..4cf294056c153b 100644
--- a/Lib/test/test_turtle.py
+++ b/Lib/test/test_turtle.py
@@ -468,8 +468,8 @@ def test_all_signatures(self):
 
         known_signatures = {
             'teleport':
-                '(x=None, y=None, fill_gap: bool = False) -> None',
-            'clear': '()',
+                '(x=None, y=None, *, fill_gap: bool = False) -> None',
+            'undo': '()',
             'reset': '(canvwidth=None, canvheight=None, bg=None)',
             'bgcolor': '(*args)',
             'pen': '(pen=None, **pendict)',

From 7f71ffdb415062ab8c36aa191183a891272a3f26 Mon Sep 17 00:00:00 2001
From: sobolevn <mail@sobolevn.me>
Date: Wed, 9 Aug 2023 14:32:28 +0300
Subject: [PATCH 3/6] Fix CI

---
 Lib/test/test_turtle.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py
index 4cf294056c153b..c96341891179f9 100644
--- a/Lib/test/test_turtle.py
+++ b/Lib/test/test_turtle.py
@@ -470,7 +470,7 @@ def test_all_signatures(self):
             'teleport':
                 '(x=None, y=None, *, fill_gap: bool = False) -> None',
             'undo': '()',
-            'reset': '(canvwidth=None, canvheight=None, bg=None)',
+            'goto': '(x, y=None)',
             'bgcolor': '(*args)',
             'pen': '(pen=None, **pendict)',
         }

From 43f131f4310ba6b1076f8dedca80f5a357921765 Mon Sep 17 00:00:00 2001
From: sobolevn <mail@sobolevn.me>
Date: Thu, 10 Aug 2023 09:42:14 +0300
Subject: [PATCH 4/6] Use patma

---
 Lib/turtle.py | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/Lib/turtle.py b/Lib/turtle.py
index 3b1b7f330c1fd3..148c124f90165e 100644
--- a/Lib/turtle.py
+++ b/Lib/turtle.py
@@ -3930,18 +3930,20 @@ def getmethparlist(ob):
 
     call_args = []
     for param in func_sig.parameters.values():
-        kind = param.kind
-        if kind in (
-            inspect.Parameter.POSITIONAL_ONLY,
-            inspect.Parameter.POSITIONAL_OR_KEYWORD,
-        ):
-            call_args.append(param.name)
-        if kind == inspect.Parameter.VAR_POSITIONAL:
-            call_args.append(f'*{param.name}')
-        if kind == inspect.Parameter.KEYWORD_ONLY:
-            call_args.append(f'{param.name}={param.name}')
-        if kind == inspect.Parameter.VAR_KEYWORD:
-            call_args.append(f'**{param.name}')
+        match param.kind:
+            case (
+                inspect.Parameter.POSITIONAL_ONLY
+                | inspect.Parameter.POSITIONAL_OR_KEYWORD
+            ):
+                call_args.append(param.name)
+            case inspect.Parameter.VAR_POSITIONAL:
+                call_args.append(f'*{param.name}')
+            case inspect.Parameter.KEYWORD_ONLY:
+                call_args.append(f'{param.name}={param.name}')
+            case inspect.Parameter.VAR_KEYWORD:
+                call_args.append(f'**{param.name}')
+            case _:
+                raise RuntimeError('Unsupported parameter kind')
     call_text = f'({', '.join(call_args)})'
 
     return str(func_sig), call_text

From 51f5bf088837e419724bfca228cd41468f7e74ad Mon Sep 17 00:00:00 2001
From: sobolevn <mail@sobolevn.me>
Date: Thu, 10 Aug 2023 09:44:32 +0300
Subject: [PATCH 5/6] Better error message

---
 Lib/turtle.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Lib/turtle.py b/Lib/turtle.py
index 148c124f90165e..6119b7a447e7bf 100644
--- a/Lib/turtle.py
+++ b/Lib/turtle.py
@@ -3943,7 +3943,7 @@ def getmethparlist(ob):
             case inspect.Parameter.VAR_KEYWORD:
                 call_args.append(f'**{param.name}')
             case _:
-                raise RuntimeError('Unsupported parameter kind')
+                raise RuntimeError('Unsupported parameter kind', param.kind)
     call_text = f'({', '.join(call_args)})'
 
     return str(func_sig), call_text

From 438dcbc9606d4a655e167815ea94c49f3b56c837 Mon Sep 17 00:00:00 2001
From: sobolevn <mail@sobolevn.me>
Date: Mon, 21 Aug 2023 18:00:39 +0300
Subject: [PATCH 6/6] Address review

---
 Lib/test/test_turtle.py | 13 +++----------
 1 file changed, 3 insertions(+), 10 deletions(-)

diff --git a/Lib/test/test_turtle.py b/Lib/test/test_turtle.py
index c96341891179f9..14121a590a5026 100644
--- a/Lib/test/test_turtle.py
+++ b/Lib/test/test_turtle.py
@@ -464,7 +464,6 @@ def test_teleport(self):
 class TestModuleLevel(unittest.TestCase):
     def test_all_signatures(self):
         import inspect
-        import types
 
         known_signatures = {
             'teleport':
@@ -475,17 +474,11 @@ def test_all_signatures(self):
             'pen': '(pen=None, **pendict)',
         }
 
-        for name in turtle.__all__:
-            obj = getattr(turtle, name)
-            if not isinstance(obj, types.FunctionType):
-                continue
-
+        for name in known_signatures:
             with self.subTest(name=name):
-                # All functions must produce correct signatures:
+                obj = getattr(turtle, name)
                 sig = inspect.signature(obj)
-
-                if name in known_signatures:
-                    self.assertEqual(str(sig), known_signatures[name])
+                self.assertEqual(str(sig), known_signatures[name])
 
 
 if __name__ == '__main__':