mirror of
				https://github.com/python/cpython.git
				synced 2025-11-03 23:21:29 +00:00 
			
		
		
		
	inspect.Signature: Fix discrepancy between __eq__ and __hash__.
Issue #20334. Thanks to Antony Lee for bug report & initial patch.
This commit is contained in:
		
							parent
							
								
									f1a8df0ac9
								
							
						
					
					
						commit
						08d4a4f488
					
				
					 3 changed files with 40 additions and 40 deletions
				
			
		| 
						 | 
				
			
			@ -2239,14 +2239,7 @@ def __repr__(self):
 | 
			
		|||
                                           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)
 | 
			
		||||
        return hash((self.name, self.kind, self.annotation, self.default))
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        return (issubclass(other.__class__, Parameter) and
 | 
			
		||||
| 
						 | 
				
			
			@ -2541,41 +2534,23 @@ def replace(self, *, parameters=_void, return_annotation=_void):
 | 
			
		|||
        return type(self)(parameters,
 | 
			
		||||
                          return_annotation=return_annotation)
 | 
			
		||||
 | 
			
		||||
    def _hash_basis(self):
 | 
			
		||||
        params = tuple(param for param in self.parameters.values()
 | 
			
		||||
                             if param.kind != _KEYWORD_ONLY)
 | 
			
		||||
 | 
			
		||||
        kwo_params = {param.name: param for param in self.parameters.values()
 | 
			
		||||
                                        if param.kind == _KEYWORD_ONLY}
 | 
			
		||||
 | 
			
		||||
        return params, kwo_params, self.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)
 | 
			
		||||
        params, kwo_params, return_annotation = self._hash_basis()
 | 
			
		||||
        kwo_params = frozenset(kwo_params.values())
 | 
			
		||||
        return hash((params, kwo_params, return_annotation))
 | 
			
		||||
 | 
			
		||||
    def __eq__(self, other):
 | 
			
		||||
        if (not issubclass(type(other), Signature) or
 | 
			
		||||
                    self.return_annotation != other.return_annotation or
 | 
			
		||||
                    len(self.parameters) != len(other.parameters)):
 | 
			
		||||
            return False
 | 
			
		||||
 | 
			
		||||
        other_positions = {param: idx
 | 
			
		||||
                           for idx, param in enumerate(other.parameters.keys())}
 | 
			
		||||
 | 
			
		||||
        for idx, (param_name, param) in enumerate(self.parameters.items()):
 | 
			
		||||
            if param.kind == _KEYWORD_ONLY:
 | 
			
		||||
                try:
 | 
			
		||||
                    other_param = other.parameters[param_name]
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    return False
 | 
			
		||||
                else:
 | 
			
		||||
                    if param != other_param:
 | 
			
		||||
                        return False
 | 
			
		||||
            else:
 | 
			
		||||
                try:
 | 
			
		||||
                    other_idx = other_positions[param_name]
 | 
			
		||||
                except KeyError:
 | 
			
		||||
                    return False
 | 
			
		||||
                else:
 | 
			
		||||
                    if (idx != other_idx or
 | 
			
		||||
                                    param != other.parameters[param_name]):
 | 
			
		||||
                        return False
 | 
			
		||||
 | 
			
		||||
        return True
 | 
			
		||||
        return (isinstance(other, Signature) and
 | 
			
		||||
                self._hash_basis() == other._hash_basis())
 | 
			
		||||
 | 
			
		||||
    def __ne__(self, other):
 | 
			
		||||
        return not self.__eq__(other)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -2535,43 +2535,67 @@ def foo(a, *, b:int) -> float: pass
 | 
			
		|||
 | 
			
		||||
        def bar(a, *, b:int) -> float: pass
 | 
			
		||||
        self.assertEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def bar(a, *, b:int) -> int: pass
 | 
			
		||||
        self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertNotEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def bar(a, *, b:int): pass
 | 
			
		||||
        self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertNotEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def bar(a, *, b:int=42) -> float: pass
 | 
			
		||||
        self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertNotEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def bar(a, *, c) -> float: pass
 | 
			
		||||
        self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertNotEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def bar(a, b:int) -> float: pass
 | 
			
		||||
        self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertNotEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
        def spam(b:int, a) -> float: pass
 | 
			
		||||
        self.assertNotEqual(inspect.signature(spam), inspect.signature(bar))
 | 
			
		||||
        self.assertNotEqual(
 | 
			
		||||
            hash(inspect.signature(spam)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def foo(*, a, b, c): pass
 | 
			
		||||
        def bar(*, c, b, a): pass
 | 
			
		||||
        self.assertEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def foo(*, a=1, b, c): pass
 | 
			
		||||
        def bar(*, c, b, a=1): pass
 | 
			
		||||
        self.assertEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def foo(pos, *, a=1, b, c): pass
 | 
			
		||||
        def bar(pos, *, c, b, a=1): pass
 | 
			
		||||
        self.assertEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def foo(pos, *, a, b, c): pass
 | 
			
		||||
        def bar(pos, *, c, b, a=1): pass
 | 
			
		||||
        self.assertNotEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertNotEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
        def foo(pos, *args, a=42, b, c, **kwargs:int): pass
 | 
			
		||||
        def bar(pos, *args, c, b, a=42, **kwargs:int): pass
 | 
			
		||||
        self.assertEqual(inspect.signature(foo), inspect.signature(bar))
 | 
			
		||||
        self.assertEqual(
 | 
			
		||||
            hash(inspect.signature(foo)), hash(inspect.signature(bar)))
 | 
			
		||||
 | 
			
		||||
    def test_signature_hashable(self):
 | 
			
		||||
        S = inspect.Signature
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -814,6 +814,7 @@ _ Issue #21597: The separator between the turtledemo text pane and the drawing
 | 
			
		|||
  keyword-only.
 | 
			
		||||
 | 
			
		||||
- Issue #20334: inspect.Signature and inspect.Parameter are now hashable.
 | 
			
		||||
  Thanks to Antony Lee for bug reports and suggestions.
 | 
			
		||||
 | 
			
		||||
- Issue #15916: doctest.DocTestSuite returns an empty unittest.TestSuite instead
 | 
			
		||||
  of raising ValueError if it finds no tests
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue