summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorEthan Furman <ethan@stoneleaf.us>2022-12-16 20:30:47 (GMT)
committerGitHub <noreply@github.com>2022-12-16 20:30:47 (GMT)
commita5a7cea202d34ca699d9594d428ba527ef7ff2ce (patch)
tree5176c390d975ad480718c43245f02a78a55a2b39
parent5234e1cbea686e38392f113707db322ad8405048 (diff)
downloadcpython-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.py9
-rw-r--r--Lib/inspect.py10
-rw-r--r--Lib/test/test_enum.py25
-rw-r--r--Lib/test/test_inspect.py32
-rw-r--r--Misc/NEWS.d/next/Library/2022-12-10-20-52-28.gh-issue-100039.zDqjT4.rst1
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.