diff --git a/Lib/inspect.py b/Lib/inspect.py index d6bd8cd..fa8787b 100644 --- a/Lib/inspect.py +++ b/Lib/inspect.py @@ -1703,6 +1703,16 @@ class Parameter: return '<{} at {:#x} {!r}>'.format(self.__class__.__name__, id(self), self.name) + def __hash__(self): + hash_tuple = (self.name, int(self.kind), self._partial_kwarg) + + 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 @@ -2092,6 +2102,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 1bfe724..0fd61c3 100644 --- a/Lib/test/test_inspect.py +++ b/Lib/test/test_inspect.py @@ -2051,11 +2051,18 @@ 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) - with self.assertRaisesRegex(TypeError, 'unhashable type'): - hash(sig) + auto_sig = inspect.signature(foo) + + manual_sig = S(parameters=[P('a', P.POSITIONAL_OR_KEYWORD)]) + + self.assertEqual(hash(auto_sig), hash(manual_sig)) + self.assertNotEqual(hash(auto_sig), + hash(manual_sig.replace(return_annotation='spam'))) def test_signature_str(self): def foo(a:int=1, *, b, c=None, **kwargs) -> 42: @@ -2149,6 +2156,15 @@ class TestParameterObject(unittest.TestCase): self.assertTrue(repr(p).startswith('