Skip to content
Snippets Groups Projects
Commit 18583c26 authored by Bryce Guinta's avatar Bryce Guinta
Browse files

Fix inference tip collisions

Set predicates for transforms which use inference_tips to prevent
a node's _explicit_inference from being overwritten
parent b81a7625
No related branches found
No related tags found
No related merge requests found
Loading
Loading
@@ -8,6 +8,13 @@ Change log for the astroid package (used to be astng)
 
Close #437, #447, #313, PyCQA/pylint#1642, PyCQA/pylint#1805, PyCQA/pylint#1854, PyCQA/pylint#1452
 
* Stop most inference tip overwrites from happening by using
predicates on existing inference_tip transforms.
Close #472
2018-01-23 -- 1.6.1
 
* Fix a crash when __annotations__ access a parent's __init__ that does not have arguments
Loading
Loading
Loading
Loading
@@ -97,18 +97,37 @@ class AsStringRegexpPredicate(object):
# pylint: disable=no-member; github.com/pycqa/astroid/126
return self.regexp.search(node.as_string())
 
def inference_tip(infer_function):
def inference_tip(infer_function, raise_on_overwrite=False):
"""Given an instance specific inference function, return a function to be
given to MANAGER.register_transform to set this inference function.
 
:param bool raise_on_overwrite: Raise an `InferenceOverwriteError`
if the inference tip will overwrite another. Used for debugging
Typical usage
 
.. sourcecode:: python
 
MANAGER.register_transform(Call, inference_tip(infer_named_tuple),
predicate)
.. Note::
Using an inference tip will override
any previously set inference tip for the given
node. Use a predicate in the transform to prevent
excess overwrites.
"""
def transform(node, infer_function=infer_function):
if (raise_on_overwrite
and node._explicit_inference is not None
and node._explicit_inference is not infer_function):
raise InferenceOverwriteError(
"Inference already set to {existing_inference}. "
"Trying to overwrite with {new_inference} for {node}"
.format(existing_inference=infer_function,
new_inference=node._explicit_inference,
node=node))
node._explicit_inference = infer_function
return node
return transform
Loading
Loading
Loading
Loading
@@ -488,14 +488,24 @@ def infer_slice(node, context=None):
 
 
def _infer_object__new__decorator(node, context=None):
# Instantiate class immediately
# since that's what @object.__new__ does
return iter((node.instantiate_class(),))
def _infer_object__new__decorator_check(node):
"""Predicate before inference_tip
Check if the given ClassDef has a @object.__new__ decorator
"""
if not node.decorators:
raise UseInferenceDefault
return False
 
for decorator in node.decorators.nodes:
if isinstance(decorator, nodes.Attribute):
if decorator.as_string() == OBJECT_DUNDER_NEW:
return iter((node.instantiate_class(),))
raise UseInferenceDefault
return True
return False
 
 
# Builtins inference
Loading
Loading
@@ -515,5 +525,6 @@ register_builtin_transform(infer_slice, 'slice')
# Infer object.__new__ calls
MANAGER.register_transform(
nodes.ClassDef,
inference_tip(_infer_object__new__decorator)
inference_tip(_infer_object__new__decorator),
_infer_object__new__decorator_check
)
Loading
Loading
@@ -51,9 +51,6 @@ def infer_typing_namedtuple_class(node, context=None):
"""Infer a subclass of typing.NamedTuple"""
 
# Check if it has the corresponding bases
if not set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES:
raise UseInferenceDefault
annassigns_fields = [
annassign.target.name for annassign in node.body
if isinstance(annassign, nodes.AnnAssign)
Loading
Loading
@@ -69,6 +66,15 @@ def infer_typing_namedtuple_class(node, context=None):
return node.infer(context=context)
 
 
def has_namedtuple_base(node):
"""Predicate for class inference tip
:type node: ClassDef
:rtype: bool
"""
return set(node.basenames) & TYPING_NAMEDTUPLE_BASENAMES
def looks_like_typing_namedtuple(node):
func = node.func
if isinstance(func, nodes.Attribute):
Loading
Loading
@@ -83,7 +89,9 @@ MANAGER.register_transform(
inference_tip(infer_typing_namedtuple),
looks_like_typing_namedtuple
)
MANAGER.register_transform(
nodes.ClassDef,
inference_tip(infer_typing_namedtuple_class)
inference_tip(infer_typing_namedtuple_class),
has_namedtuple_base
)
Loading
Loading
@@ -198,6 +198,13 @@ class AstroidTypeError(AstroidError):
"""Raised when a TypeError would be expected in Python code."""
 
 
class InferenceOverwriteError(AstroidError):
"""Raised when an inference tip is overwritten
Currently only used for debugging.
"""
# Backwards-compatibility aliases
OperationError = util.BadOperationMessage
UnaryOperationError = util.BadUnaryOperationMessage
Loading
Loading
Loading
Loading
@@ -4273,9 +4273,7 @@ class CallSiteTest(unittest.TestCase):
self.assertIn('f', site.duplicated_keywords)
 
 
@unittest.skip
class ObjectDunderNewTest(unittest.TestCase):
def test_object_dunder_new_is_inferred_if_decorator(self):
node = extract_node('''
@object.__new__
Loading
Loading
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment