diff options
author | Ethan Furman <ethan@stoneleaf.us> | 2022-12-16 20:30:47 (GMT) |
---|---|---|
committer | GitHub <noreply@github.com> | 2022-12-16 20:30:47 (GMT) |
commit | a5a7cea202d34ca699d9594d428ba527ef7ff2ce (patch) | |
tree | 5176c390d975ad480718c43245f02a78a55a2b39 | |
parent | 5234e1cbea686e38392f113707db322ad8405048 (diff) | |
download | cpython-a5a7cea202d34ca699d9594d428ba527ef7ff2ce.zip cpython-a5a7cea202d34ca699d9594d428ba527ef7ff2ce.tar.gz cpython-a5a7cea202d34ca699d9594d428ba527ef7ff2ce.tar.bz2 |
gh-100039: enhance __signature__ to work with str and callables (GH-100168)
Callables should be either class- or static-methods.
Enum now uses the classmethod version to greatly improve the help
given for enums and flags.
-rw-r--r-- | Lib/enum.py | 9 | ||||
-rw-r--r-- | Lib/inspect.py | 10 | ||||
-rw-r--r-- | Lib/test/test_enum.py | 25 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 32 | ||||
-rw-r--r-- | Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst | 1 |
5 files changed, 73 insertions, 4 deletions
diff --git a/Lib/enum.py b/Lib/enum.py index a0cad06..21f6388 100644 --- a/Lib/enum.py +++ b/Lib/enum.py @@ -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' diff --git a/Lib/inspect.py b/Lib/inspect.py index e165937..e92c355 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -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: diff --git a/Lib/test/test_enum.py b/Lib/test/test_enum.py index d876c8e..1e653e9 100644 --- a/Lib/test/test_enum.py +++ b/Lib/test/test_enum.py @@ -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: diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 2b7977b..0f8217b 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -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 index 0000000..d56643f --- /dev/null +++ b/Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst @@ -0,0 +1 @@ +Improve signatures for enums and flags. |