diff options
author | Yury Selivanov <yselivanov@sprymix.com> | 2014-04-08 15:46:50 (GMT) |
---|---|---|
committer | Yury Selivanov <yselivanov@sprymix.com> | 2014-04-08 15:46:50 (GMT) |
commit | 67ae50ee1c723db3a0d9fb22fc182dc1854fb137 (patch) | |
tree | abda69bbb3afcac6d6e386bd2ff2fc2f27c48336 | |
parent | 3f73ca23cfe4e4058689bc5a46622c68ef1b6aa6 (diff) | |
download | cpython-67ae50ee1c723db3a0d9fb22fc182dc1854fb137.zip cpython-67ae50ee1c723db3a0d9fb22fc182dc1854fb137.tar.gz cpython-67ae50ee1c723db3a0d9fb22fc182dc1854fb137.tar.bz2 |
inspect: Make Signature and Parameter hashable. Issue #20334.
-rw-r--r-- | Doc/library/inspect.rst | 4 | ||||
-rw-r--r-- | Doc/whatsnew/3.5.rst | 3 | ||||
-rw-r--r-- | Lib/inspect.py | 16 | ||||
-rw-r--r-- | Lib/test/test_inspect.py | 40 | ||||
-rw-r--r-- | Misc/NEWS | 2 |
5 files changed, 52 insertions, 13 deletions
diff --git a/Doc/library/inspect.rst b/Doc/library/inspect.rst index f8c0317..21408f4 100644 --- a/Doc/library/inspect.rst +++ b/Doc/library/inspect.rst @@ -463,7 +463,7 @@ function. modified copy. .. versionchanged:: 3.5 - Signature objects are picklable. + Signature objects are picklable and hashable. .. attribute:: Signature.empty @@ -530,7 +530,7 @@ function. you can use :meth:`Parameter.replace` to create a modified copy. .. versionchanged:: 3.5 - Parameter objects are picklable. + Parameter objects are picklable and hashable. .. attribute:: Parameter.empty diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst index 76b65a4..7050e0a 100644 --- a/Doc/whatsnew/3.5.rst +++ b/Doc/whatsnew/3.5.rst @@ -143,7 +143,8 @@ Improved Modules (contributed by Claudiu Popa in :issue:`20627`). * :class:`inspect.Signature` and :class:`inspect.Parameter` are now - picklable (contributed by Yury Selivanov in :issue:`20726`). + picklable and hashable (contributed by Yury Selivanov in :issue:`20726` + and :issue:`20334`). * New class method :meth:`inspect.Signature.from_callable`, which makes subclassing of :class:`~inspect.Signature` easier (contributed diff --git a/Lib/inspect.py b/Lib/inspect.py index eef8dc9..4ac76b1 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -2231,6 +2231,16 @@ class Parameter: return '<{} at {:#x} "{}">'.format(self.__class__.__name__, id(self), self) + def __hash__(self): + hash_tuple = (self.name, int(self.kind)) + + if self._annotation is not _empty: + hash_tuple += (self._annotation,) + if self._default is not _empty: + hash_tuple += (self._default,) + + return hash(hash_tuple) + def __eq__(self, other): return (issubclass(other.__class__, Parameter) and self._name == other._name and @@ -2524,6 +2534,12 @@ class Signature: return type(self)(parameters, return_annotation=return_annotation) + def __hash__(self): + hash_tuple = tuple(self.parameters.values()) + if self._return_annotation is not _empty: + hash_tuple += (self._return_annotation,) + return hash(hash_tuple) + def __eq__(self, other): if (not issubclass(type(other), Signature) or self.return_annotation != other.return_annotation or diff --git a/Lib/test/test_inspect.py b/Lib/test/test_inspect.py index 7f1af7a..7ad190b 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -2513,11 +2513,29 @@ class TestSignatureObject(unittest.TestCase): def bar(pos, *args, c, b, a=42, **kwargs:int): pass self.assertEqual(inspect.signature(foo), inspect.signature(bar)) - def test_signature_unhashable(self): + def test_signature_hashable(self): + S = inspect.Signature + P = inspect.Parameter + def foo(a): pass - sig = inspect.signature(foo) + foo_sig = inspect.signature(foo) + + manual_sig = S(parameters=[P('a', P.POSITIONAL_OR_KEYWORD)]) + + self.assertEqual(hash(foo_sig), hash(manual_sig)) + self.assertNotEqual(hash(foo_sig), + hash(manual_sig.replace(return_annotation='spam'))) + + def bar(a) -> 1: pass + self.assertNotEqual(hash(foo_sig), hash(inspect.signature(bar))) + + def foo(a={}): pass with self.assertRaisesRegex(TypeError, 'unhashable type'): - hash(sig) + hash(inspect.signature(foo)) + + def foo(a) -> {}: pass + with self.assertRaisesRegex(TypeError, 'unhashable type'): + hash(inspect.signature(foo)) def test_signature_str(self): def foo(a:int=1, *, b, c=None, **kwargs) -> 42: @@ -2651,6 +2669,15 @@ class TestParameterObject(unittest.TestCase): self.assertTrue(repr(p).startswith('<Parameter')) self.assertTrue('"a=42"' in repr(p)) + def test_signature_parameter_hashable(self): + P = inspect.Parameter + foo = P('foo', kind=P.POSITIONAL_ONLY) + self.assertEqual(hash(foo), hash(P('foo', kind=P.POSITIONAL_ONLY))) + self.assertNotEqual(hash(foo), hash(P('foo', kind=P.POSITIONAL_ONLY, + default=42))) + self.assertNotEqual(hash(foo), + hash(foo.replace(kind=P.VAR_POSITIONAL))) + def test_signature_parameter_equality(self): P = inspect.Parameter p = P('foo', default=42, kind=inspect.Parameter.KEYWORD_ONLY) @@ -2661,13 +2688,6 @@ class TestParameterObject(unittest.TestCase): self.assertEqual(p, P('foo', default=42, kind=inspect.Parameter.KEYWORD_ONLY)) - def test_signature_parameter_unhashable(self): - p = inspect.Parameter('foo', default=42, - kind=inspect.Parameter.KEYWORD_ONLY) - - with self.assertRaisesRegex(TypeError, 'unhashable type'): - hash(p) - def test_signature_parameter_replace(self): p = inspect.Parameter('foo', default=42, kind=inspect.Parameter.KEYWORD_ONLY) @@ -154,6 +154,8 @@ Library positional-or-keyword arguments passed as keyword arguments become keyword-only. +- Issue #20334: inspect.Signature and inspect.Parameter are now hashable. + IDLE ---- |