summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYury Selivanov <yselivanov@sprymix.com>2014-04-08 15:46:50 (GMT)
committerYury Selivanov <yselivanov@sprymix.com>2014-04-08 15:46:50 (GMT)
commit67ae50ee1c723db3a0d9fb22fc182dc1854fb137 (patch)
treeabda69bbb3afcac6d6e386bd2ff2fc2f27c48336
parent3f73ca23cfe4e4058689bc5a46622c68ef1b6aa6 (diff)
downloadcpython-67ae50ee1c723db3a0d9fb22fc182dc1854fb137.zip
cpython-67ae50ee1c723db3a0d9fb22fc182dc1854fb137.tar.gz
cpython-67ae50ee1c723db3a0d9fb22fc182dc1854fb137.tar.bz2
inspect: Make Signature and Parameter hashable. Issue #20334.
-rw-r--r--Doc/library/inspect.rst4
-rw-r--r--Doc/whatsnew/3.5.rst3
-rw-r--r--Lib/inspect.py16
-rw-r--r--Lib/test/test_inspect.py40
-rw-r--r--Misc/NEWS2
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)
diff --git a/Misc/NEWS b/Misc/NEWS
index 023f81a..fd44590 100644
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -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
----