]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-100039: enhance __signature__ to work with str and callables (GH-100168)
authorEthan Furman <ethan@stoneleaf.us>
Fri, 16 Dec 2022 20:30:47 +0000 (12:30 -0800)
committerGitHub <noreply@github.com>
Fri, 16 Dec 2022 20:30:47 +0000 (12:30 -0800)
Callables should be either class- or static-methods.
Enum now uses the classmethod version to greatly improve the help
given for enums and flags.

Lib/enum.py
Lib/inspect.py
Lib/test/test_enum.py
Lib/test/test_inspect.py
Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst [new file with mode: 0644]

index a0cad066dc23f71b1a593e79a4c07cd506efccfa..21f63881d78d4d8bc0d0b06f5166f733e93d94e3 100644 (file)
@@ -685,7 +685,7 @@ class EnumType(type):
                         'member order does not match _order_:\n  %r\n  %r'
                         % (enum_class._member_names_, _order_)
                         )
-
+        #
         return enum_class
 
     def __bool__(cls):
@@ -1083,6 +1083,13 @@ class Enum(metaclass=EnumType):
     attributes -- see the documentation for details.
     """
 
+    @classmethod
+    def __signature__(cls):
+        if cls._member_names_:
+            return '(*values)'
+        else:
+            return '(new_class_name, /, names, *, module=None, qualname=None, type=None, start=1, boundary=None)'
+
     def __new__(cls, value):
         # all enum instances are actually created during class construction
         # without calling this method; this method is called by the metaclass'
index e165937e448a95ccfdecbc4d7c764bb30808a7e8..e92c355220fd8d6364b542e33f971eac942cc4e4 100644 (file)
@@ -2443,10 +2443,18 @@ def _signature_from_callable(obj, *,
         pass
     else:
         if sig is not None:
+            # since __text_signature__ is not writable on classes, __signature__
+            # may contain text (or be a callable that returns text);
+            # if so, convert it
+            o_sig = sig
+            if not isinstance(sig, (Signature, str)) and callable(sig):
+                sig = sig()
+            if isinstance(sig, str):
+                sig = _signature_fromstr(sigcls, obj, sig)
             if not isinstance(sig, Signature):
                 raise TypeError(
                     'unexpected object {!r} in __signature__ '
-                    'attribute'.format(sig))
+                    'attribute'.format(o_sig))
             return sig
 
     try:
index d876c8e5fb7798f63c3e854ab1b611b91985ef76..1e653e94f6b57ab6bc57ce8c1664bf1f650bcf1c 100644 (file)
@@ -4259,7 +4259,7 @@ expected_help_output_with_docs = """\
 Help on class Color in module %s:
 
 class Color(enum.Enum)
- |  Color(value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None)
+ |  Color(*values)
  |
  |  Method resolution order:
  |      Color
@@ -4315,7 +4315,7 @@ expected_help_output_without_docs = """\
 Help on class Color in module %s:
 
 class Color(enum.Enum)
- |  Color(value, names=None, *values, module=None, qualname=None, type=None, start=1)
+ |  Color(*values)
  |
  |  Method resolution order:
  |      Color
@@ -4462,6 +4462,27 @@ class TestStdLib(unittest.TestCase):
         if failed:
             self.fail("result does not equal expected, see print above")
 
+    def test_inspect_signatures(self):
+        from inspect import signature, Signature, Parameter
+        self.assertEqual(
+                signature(Enum),
+                Signature([
+                    Parameter('new_class_name', Parameter.POSITIONAL_ONLY),
+                    Parameter('names', Parameter.POSITIONAL_OR_KEYWORD),
+                    Parameter('module', Parameter.KEYWORD_ONLY, default=None),
+                    Parameter('qualname', Parameter.KEYWORD_ONLY, default=None),
+                    Parameter('type', Parameter.KEYWORD_ONLY, default=None),
+                    Parameter('start', Parameter.KEYWORD_ONLY, default=1),
+                    Parameter('boundary', Parameter.KEYWORD_ONLY, default=None),
+                    ]),
+                )
+        self.assertEqual(
+                signature(enum.FlagBoundary),
+                Signature([
+                    Parameter('values', Parameter.VAR_POSITIONAL),
+                    ]),
+                )
+
     def test_test_simple_enum(self):
         @_simple_enum(Enum)
         class SimpleColor:
index 2b7977b1648f702b3c2df0675be21e0238714af3..0f8217ba3b7550cca37c1249d68591d1cfed71c4 100644 (file)
@@ -3614,6 +3614,38 @@ class TestSignatureObject(unittest.TestCase):
                 self.assertEqual(signature_func(foo), inspect.Signature())
         self.assertEqual(inspect.get_annotations(foo), {})
 
+    def test_signature_as_str(self):
+        self.maxDiff = None
+        class S:
+            __signature__ = '(a, b=2)'
+
+        self.assertEqual(self.signature(S),
+                         ((('a', ..., ..., 'positional_or_keyword'),
+                           ('b', 2, ..., 'positional_or_keyword')),
+                          ...))
+
+    def test_signature_as_callable(self):
+        # __signature__ should be either a staticmethod or a bound classmethod
+        class S:
+            @classmethod
+            def __signature__(cls):
+                return '(a, b=2)'
+
+        self.assertEqual(self.signature(S),
+                         ((('a', ..., ..., 'positional_or_keyword'),
+                           ('b', 2, ..., 'positional_or_keyword')),
+                          ...))
+
+        class S:
+            @staticmethod
+            def __signature__():
+                return '(a, b=2)'
+
+        self.assertEqual(self.signature(S),
+                         ((('a', ..., ..., 'positional_or_keyword'),
+                           ('b', 2, ..., 'positional_or_keyword')),
+                          ...))
+
 
 class TestParameterObject(unittest.TestCase):
     def test_signature_parameter_kinds(self):
diff --git a/Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst b/Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst
new file mode 100644 (file)
index 0000000..d56643f
--- /dev/null
@@ -0,0 +1 @@
+Improve signatures for enums and flags.