forked from VimPlug/jedi
Remove the precedence module in favor of the syntax tree module.
This commit is contained in:
@@ -29,7 +29,7 @@ from jedi.evaluate import compiled
|
|||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
from jedi.evaluate import pep0484
|
from jedi.evaluate import pep0484
|
||||||
from jedi.evaluate import precedence
|
from jedi.evaluate.syntax_tree import is_string
|
||||||
from jedi.evaluate import recursion
|
from jedi.evaluate import recursion
|
||||||
from jedi.evaluate.cache import evaluator_method_cache
|
from jedi.evaluate.cache import evaluator_method_cache
|
||||||
from jedi.evaluate.filters import DictFilter, AbstractNameDefinition, \
|
from jedi.evaluate.filters import DictFilter, AbstractNameDefinition, \
|
||||||
@@ -462,7 +462,7 @@ class SequenceLiteralContext(ArrayMixin, AbstractSequence):
|
|||||||
"""
|
"""
|
||||||
for key_node, value in self._items():
|
for key_node, value in self._items():
|
||||||
for key in self._defining_context.eval_node(key_node):
|
for key in self._defining_context.eval_node(key_node):
|
||||||
if precedence.is_string(key):
|
if is_string(key):
|
||||||
yield key.obj, context.LazyTreeContext(self._defining_context, value)
|
yield key.obj, context.LazyTreeContext(self._defining_context, value)
|
||||||
|
|
||||||
def __repr__(self):
|
def __repr__(self):
|
||||||
|
|||||||
@@ -1,151 +0,0 @@
|
|||||||
"""
|
|
||||||
Handles operator precedence.
|
|
||||||
"""
|
|
||||||
import operator as op
|
|
||||||
|
|
||||||
from jedi._compatibility import unicode
|
|
||||||
from jedi.evaluate.compiled import CompiledObject, create, builtin_from_name
|
|
||||||
from jedi.evaluate import analysis
|
|
||||||
from jedi.evaluate.context import ContextSet, NO_CONTEXTS, iterator_to_context_set
|
|
||||||
|
|
||||||
# Maps Python syntax to the operator module.
|
|
||||||
COMPARISON_OPERATORS = {
|
|
||||||
'==': op.eq,
|
|
||||||
'!=': op.ne,
|
|
||||||
'is': op.is_,
|
|
||||||
'is not': op.is_not,
|
|
||||||
'<': op.lt,
|
|
||||||
'<=': op.le,
|
|
||||||
'>': op.gt,
|
|
||||||
'>=': op.ge,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def literals_to_types(evaluator, result):
|
|
||||||
# Changes literals ('a', 1, 1.0, etc) to its type instances (str(),
|
|
||||||
# int(), float(), etc).
|
|
||||||
new_result = NO_CONTEXTS
|
|
||||||
for typ in result:
|
|
||||||
if is_literal(typ):
|
|
||||||
# Literals are only valid as long as the operations are
|
|
||||||
# correct. Otherwise add a value-free instance.
|
|
||||||
cls = builtin_from_name(evaluator, typ.name.string_name)
|
|
||||||
new_result |= cls.execute_evaluated()
|
|
||||||
else:
|
|
||||||
new_result |= ContextSet(typ)
|
|
||||||
return new_result
|
|
||||||
|
|
||||||
|
|
||||||
def calculate(evaluator, context, left_contexts, operator, right_contexts):
|
|
||||||
if not left_contexts or not right_contexts:
|
|
||||||
# illegal slices e.g. cause left/right_result to be None
|
|
||||||
result = (left_contexts or NO_CONTEXTS) | (right_contexts or NO_CONTEXTS)
|
|
||||||
return literals_to_types(evaluator, result)
|
|
||||||
else:
|
|
||||||
# I don't think there's a reasonable chance that a string
|
|
||||||
# operation is still correct, once we pass something like six
|
|
||||||
# objects.
|
|
||||||
if len(left_contexts) * len(right_contexts) > 6:
|
|
||||||
return literals_to_types(evaluator, left_contexts | right_contexts)
|
|
||||||
else:
|
|
||||||
return ContextSet.from_sets(
|
|
||||||
_element_calculate(evaluator, context, left, operator, right)
|
|
||||||
for left in left_contexts
|
|
||||||
for right in right_contexts
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
@iterator_to_context_set
|
|
||||||
def factor_calculate(evaluator, types, operator):
|
|
||||||
"""
|
|
||||||
Calculates `+`, `-`, `~` and `not` prefixes.
|
|
||||||
"""
|
|
||||||
for typ in types:
|
|
||||||
if operator == '-':
|
|
||||||
if _is_number(typ):
|
|
||||||
yield create(evaluator, -typ.obj)
|
|
||||||
elif operator == 'not':
|
|
||||||
value = typ.py__bool__()
|
|
||||||
if value is None: # Uncertainty.
|
|
||||||
return
|
|
||||||
yield create(evaluator, not value)
|
|
||||||
else:
|
|
||||||
yield typ
|
|
||||||
|
|
||||||
|
|
||||||
def _is_number(obj):
|
|
||||||
return isinstance(obj, CompiledObject) \
|
|
||||||
and isinstance(obj.obj, (int, float))
|
|
||||||
|
|
||||||
|
|
||||||
def is_string(obj):
|
|
||||||
return isinstance(obj, CompiledObject) \
|
|
||||||
and isinstance(obj.obj, (str, unicode))
|
|
||||||
|
|
||||||
|
|
||||||
def is_literal(obj):
|
|
||||||
return _is_number(obj) or is_string(obj)
|
|
||||||
|
|
||||||
|
|
||||||
def _is_tuple(obj):
|
|
||||||
from jedi.evaluate import iterable
|
|
||||||
return isinstance(obj, iterable.AbstractSequence) and obj.array_type == 'tuple'
|
|
||||||
|
|
||||||
|
|
||||||
def _is_list(obj):
|
|
||||||
from jedi.evaluate import iterable
|
|
||||||
return isinstance(obj, iterable.AbstractSequence) and obj.array_type == 'list'
|
|
||||||
|
|
||||||
|
|
||||||
def _element_calculate(evaluator, context, left, operator, right):
|
|
||||||
from jedi.evaluate import iterable, instance
|
|
||||||
l_is_num = _is_number(left)
|
|
||||||
r_is_num = _is_number(right)
|
|
||||||
if operator == '*':
|
|
||||||
# for iterables, ignore * operations
|
|
||||||
if isinstance(left, iterable.AbstractSequence) or is_string(left):
|
|
||||||
return ContextSet(left)
|
|
||||||
elif isinstance(right, iterable.AbstractSequence) or is_string(right):
|
|
||||||
return ContextSet(right)
|
|
||||||
elif operator == '+':
|
|
||||||
if l_is_num and r_is_num or is_string(left) and is_string(right):
|
|
||||||
return ContextSet(create(evaluator, left.obj + right.obj))
|
|
||||||
elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right):
|
|
||||||
return ContextSet(iterable.MergedArray(evaluator, (left, right)))
|
|
||||||
elif operator == '-':
|
|
||||||
if l_is_num and r_is_num:
|
|
||||||
return ContextSet(create(evaluator, left.obj - right.obj))
|
|
||||||
elif operator == '%':
|
|
||||||
# With strings and numbers the left type typically remains. Except for
|
|
||||||
# `int() % float()`.
|
|
||||||
return ContextSet(left)
|
|
||||||
elif operator in COMPARISON_OPERATORS:
|
|
||||||
operation = COMPARISON_OPERATORS[operator]
|
|
||||||
if isinstance(left, CompiledObject) and isinstance(right, CompiledObject):
|
|
||||||
# Possible, because the return is not an option. Just compare.
|
|
||||||
left = left.obj
|
|
||||||
right = right.obj
|
|
||||||
|
|
||||||
try:
|
|
||||||
result = operation(left, right)
|
|
||||||
except TypeError:
|
|
||||||
# Could be True or False.
|
|
||||||
return ContextSet(create(evaluator, True), create(evaluator, False))
|
|
||||||
else:
|
|
||||||
return ContextSet(create(evaluator, result))
|
|
||||||
elif operator == 'in':
|
|
||||||
return NO_CONTEXTS
|
|
||||||
|
|
||||||
def check(obj):
|
|
||||||
"""Checks if a Jedi object is either a float or an int."""
|
|
||||||
return isinstance(obj, instance.CompiledInstance) and \
|
|
||||||
obj.name.string_name in ('int', 'float')
|
|
||||||
|
|
||||||
# Static analysis, one is a number, the other one is not.
|
|
||||||
if operator in ('+', '-') and l_is_num != r_is_num \
|
|
||||||
and not (check(left) or check(right)):
|
|
||||||
message = "TypeError: unsupported operand type(s) for +: %s and %s"
|
|
||||||
analysis.add(context, 'type-error-operation', operator,
|
|
||||||
message % (left, right))
|
|
||||||
|
|
||||||
return ContextSet(left, right)
|
|
||||||
@@ -20,11 +20,11 @@ from jedi.evaluate.instance import InstanceFunctionExecution, \
|
|||||||
AnonymousInstanceFunctionExecution
|
AnonymousInstanceFunctionExecution
|
||||||
from jedi.evaluate import iterable
|
from jedi.evaluate import iterable
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi.evaluate import precedence
|
|
||||||
from jedi.evaluate import param
|
from jedi.evaluate import param
|
||||||
from jedi.evaluate import analysis
|
from jedi.evaluate import analysis
|
||||||
from jedi.evaluate.context import LazyTreeContext, ContextualizedNode, \
|
from jedi.evaluate.context import LazyTreeContext, ContextualizedNode, \
|
||||||
NO_CONTEXTS, ContextSet
|
NO_CONTEXTS, ContextSet
|
||||||
|
from jedi.evaluate.syntax_tree import is_string
|
||||||
|
|
||||||
# Now this is all part of fake tuples in Jedi. However super doesn't work on
|
# Now this is all part of fake tuples in Jedi. However super doesn't work on
|
||||||
# __init__ and __new__ doesn't work at all. So adding this to nametuples is
|
# __init__ and __new__ doesn't work at all. So adding this to nametuples is
|
||||||
@@ -156,7 +156,7 @@ def builtins_getattr(evaluator, objects, names, defaults=None):
|
|||||||
# follow the first param
|
# follow the first param
|
||||||
for obj in objects:
|
for obj in objects:
|
||||||
for name in names:
|
for name in names:
|
||||||
if precedence.is_string(name):
|
if is_string(name):
|
||||||
return obj.py__getattribute__(name.obj)
|
return obj.py__getattribute__(name.obj)
|
||||||
else:
|
else:
|
||||||
debug.warning('getattr called without str')
|
debug.warning('getattr called without str')
|
||||||
|
|||||||
@@ -2,18 +2,19 @@
|
|||||||
Functions evaluating the syntax tree.
|
Functions evaluating the syntax tree.
|
||||||
"""
|
"""
|
||||||
import copy
|
import copy
|
||||||
|
import operator as op
|
||||||
|
|
||||||
from parso.python import tree
|
from parso.python import tree
|
||||||
|
|
||||||
from jedi import debug
|
from jedi import debug
|
||||||
from jedi import parser_utils
|
from jedi import parser_utils
|
||||||
from jedi.evaluate.context import ContextSet, NO_CONTEXTS, ContextualizedNode, \
|
from jedi.evaluate.context import ContextSet, NO_CONTEXTS, ContextualizedNode, \
|
||||||
ContextualizedName
|
ContextualizedName, iterator_to_context_set
|
||||||
from jedi.evaluate import compiled
|
from jedi.evaluate import compiled
|
||||||
from jedi.evaluate import precedence
|
|
||||||
from jedi.evaluate import pep0484
|
from jedi.evaluate import pep0484
|
||||||
from jedi.evaluate import recursion
|
from jedi.evaluate import recursion
|
||||||
from jedi.evaluate import helpers
|
from jedi.evaluate import helpers
|
||||||
|
from jedi.evaluate import analysis
|
||||||
|
|
||||||
|
|
||||||
def _limit_context_infers(func):
|
def _limit_context_infers(func):
|
||||||
@@ -66,7 +67,7 @@ def eval_node(context, element):
|
|||||||
for trailer in element.children[1:]:
|
for trailer in element.children[1:]:
|
||||||
if trailer == '**': # has a power operation.
|
if trailer == '**': # has a power operation.
|
||||||
right = evaluator.eval_element(context, element.children[2])
|
right = evaluator.eval_element(context, element.children[2])
|
||||||
context_set = precedence.calculate(
|
context_set = calculate(
|
||||||
evaluator,
|
evaluator,
|
||||||
context,
|
context,
|
||||||
context_set,
|
context_set,
|
||||||
@@ -84,7 +85,7 @@ def eval_node(context, element):
|
|||||||
elif typ in ('not_test', 'factor'):
|
elif typ in ('not_test', 'factor'):
|
||||||
context_set = context.eval_node(element.children[-1])
|
context_set = context.eval_node(element.children[-1])
|
||||||
for operator in element.children[:-1]:
|
for operator in element.children[:-1]:
|
||||||
context_set = precedence.factor_calculate(evaluator, context_set, operator)
|
context_set = eval_factor(context_set, operator)
|
||||||
return context_set
|
return context_set
|
||||||
elif typ == 'test':
|
elif typ == 'test':
|
||||||
# `x if foo else y` case.
|
# `x if foo else y` case.
|
||||||
@@ -162,7 +163,7 @@ def eval_atom(context, atom):
|
|||||||
context_set = eval_atom(context, c[0])
|
context_set = eval_atom(context, c[0])
|
||||||
for string in c[1:]:
|
for string in c[1:]:
|
||||||
right = eval_atom(context, string)
|
right = eval_atom(context, string)
|
||||||
context_set = precedence.calculate(context.evaluator, context, context_set, '+', right)
|
context_set = calculate(context.evaluator, context, context_set, '+', right)
|
||||||
return context_set
|
return context_set
|
||||||
# Parentheses without commas are not tuples.
|
# Parentheses without commas are not tuples.
|
||||||
elif c[0] == '(' and not len(c) == 2 \
|
elif c[0] == '(' and not len(c) == 2 \
|
||||||
@@ -249,10 +250,10 @@ def _eval_expr_stmt(context, stmt, seek_name=None):
|
|||||||
dct = {for_stmt.children[1].value: lazy_context.infer()}
|
dct = {for_stmt.children[1].value: lazy_context.infer()}
|
||||||
with helpers.predefine_names(context, for_stmt, dct):
|
with helpers.predefine_names(context, for_stmt, dct):
|
||||||
t = context.eval_node(rhs)
|
t = context.eval_node(rhs)
|
||||||
left = precedence.calculate(context.evaluator, context, left, operator, t)
|
left = calculate(context.evaluator, context, left, operator, t)
|
||||||
context_set = left
|
context_set = left
|
||||||
else:
|
else:
|
||||||
context_set = precedence.calculate(context.evaluator, context, left, operator, context_set)
|
context_set = calculate(context.evaluator, context, left, operator, context_set)
|
||||||
debug.dbg('eval_expr_stmt result %s', context_set)
|
debug.dbg('eval_expr_stmt result %s', context_set)
|
||||||
return context_set
|
return context_set
|
||||||
|
|
||||||
@@ -276,7 +277,152 @@ def eval_or_test(context, or_test):
|
|||||||
types = context.eval_node(right)
|
types = context.eval_node(right)
|
||||||
# Otherwise continue, because of uncertainty.
|
# Otherwise continue, because of uncertainty.
|
||||||
else:
|
else:
|
||||||
types = precedence.calculate(context.evaluator, context, types, operator,
|
types = calculate(context.evaluator, context, types, operator,
|
||||||
context.eval_node(right))
|
context.eval_node(right))
|
||||||
debug.dbg('calculate_children types %s', types)
|
debug.dbg('calculate_children types %s', types)
|
||||||
return types
|
return types
|
||||||
|
|
||||||
|
|
||||||
|
@iterator_to_context_set
|
||||||
|
def eval_factor(context_set, operator):
|
||||||
|
"""
|
||||||
|
Calculates `+`, `-`, `~` and `not` prefixes.
|
||||||
|
"""
|
||||||
|
for context in context_set:
|
||||||
|
if operator == '-':
|
||||||
|
if _is_number(context):
|
||||||
|
yield compiled.create(context.evaluator, -context.obj)
|
||||||
|
elif operator == 'not':
|
||||||
|
value = context.py__bool__()
|
||||||
|
if value is None: # Uncertainty.
|
||||||
|
return
|
||||||
|
yield compiled.create(context.evaluator, not value)
|
||||||
|
else:
|
||||||
|
yield context
|
||||||
|
|
||||||
|
|
||||||
|
# Maps Python syntax to the operator module.
|
||||||
|
COMPARISON_OPERATORS = {
|
||||||
|
'==': op.eq,
|
||||||
|
'!=': op.ne,
|
||||||
|
'is': op.is_,
|
||||||
|
'is not': op.is_not,
|
||||||
|
'<': op.lt,
|
||||||
|
'<=': op.le,
|
||||||
|
'>': op.gt,
|
||||||
|
'>=': op.ge,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def literals_to_types(evaluator, result):
|
||||||
|
# Changes literals ('a', 1, 1.0, etc) to its type instances (str(),
|
||||||
|
# int(), float(), etc).
|
||||||
|
new_result = NO_CONTEXTS
|
||||||
|
for typ in result:
|
||||||
|
if is_literal(typ):
|
||||||
|
# Literals are only valid as long as the operations are
|
||||||
|
# correct. Otherwise add a value-free instance.
|
||||||
|
cls = compiled.builtin_from_name(evaluator, typ.name.string_name)
|
||||||
|
new_result |= cls.execute_evaluated()
|
||||||
|
else:
|
||||||
|
new_result |= ContextSet(typ)
|
||||||
|
return new_result
|
||||||
|
|
||||||
|
|
||||||
|
def calculate(evaluator, context, left_contexts, operator, right_contexts):
|
||||||
|
if not left_contexts or not right_contexts:
|
||||||
|
# illegal slices e.g. cause left/right_result to be None
|
||||||
|
result = (left_contexts or NO_CONTEXTS) | (right_contexts or NO_CONTEXTS)
|
||||||
|
return literals_to_types(evaluator, result)
|
||||||
|
else:
|
||||||
|
# I don't think there's a reasonable chance that a string
|
||||||
|
# operation is still correct, once we pass something like six
|
||||||
|
# objects.
|
||||||
|
if len(left_contexts) * len(right_contexts) > 6:
|
||||||
|
return literals_to_types(evaluator, left_contexts | right_contexts)
|
||||||
|
else:
|
||||||
|
return ContextSet.from_sets(
|
||||||
|
_element_calculate(evaluator, context, left, operator, right)
|
||||||
|
for left in left_contexts
|
||||||
|
for right in right_contexts
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def is_compiled(context):
|
||||||
|
return isinstance(context, compiled.CompiledObject)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_number(context):
|
||||||
|
return is_compiled(context) and isinstance(context.obj, (int, float))
|
||||||
|
|
||||||
|
|
||||||
|
def is_string(context):
|
||||||
|
return is_compiled(context) and isinstance(context.obj, (str, unicode))
|
||||||
|
|
||||||
|
|
||||||
|
def is_literal(context):
|
||||||
|
return _is_number(context) or is_string(context)
|
||||||
|
|
||||||
|
|
||||||
|
def _is_tuple(context):
|
||||||
|
from jedi.evaluate import iterable
|
||||||
|
return isinstance(context, iterable.AbstractSequence) and context.array_type == 'tuple'
|
||||||
|
|
||||||
|
|
||||||
|
def _is_list(context):
|
||||||
|
from jedi.evaluate import iterable
|
||||||
|
return isinstance(context, iterable.AbstractSequence) and context.array_type == 'list'
|
||||||
|
|
||||||
|
|
||||||
|
def _element_calculate(evaluator, context, left, operator, right):
|
||||||
|
from jedi.evaluate import iterable, instance
|
||||||
|
l_is_num = _is_number(left)
|
||||||
|
r_is_num = _is_number(right)
|
||||||
|
if operator == '*':
|
||||||
|
# for iterables, ignore * operations
|
||||||
|
if isinstance(left, iterable.AbstractSequence) or is_string(left):
|
||||||
|
return ContextSet(left)
|
||||||
|
elif isinstance(right, iterable.AbstractSequence) or is_string(right):
|
||||||
|
return ContextSet(right)
|
||||||
|
elif operator == '+':
|
||||||
|
if l_is_num and r_is_num or is_string(left) and is_string(right):
|
||||||
|
return ContextSet(compiled.create(evaluator, left.obj + right.obj))
|
||||||
|
elif _is_tuple(left) and _is_tuple(right) or _is_list(left) and _is_list(right):
|
||||||
|
return ContextSet(iterable.MergedArray(evaluator, (left, right)))
|
||||||
|
elif operator == '-':
|
||||||
|
if l_is_num and r_is_num:
|
||||||
|
return ContextSet(compiled.create(evaluator, left.obj - right.obj))
|
||||||
|
elif operator == '%':
|
||||||
|
# With strings and numbers the left type typically remains. Except for
|
||||||
|
# `int() % float()`.
|
||||||
|
return ContextSet(left)
|
||||||
|
elif operator in COMPARISON_OPERATORS:
|
||||||
|
operation = COMPARISON_OPERATORS[operator]
|
||||||
|
if is_compiled(left) and is_compiled(right):
|
||||||
|
# Possible, because the return is not an option. Just compare.
|
||||||
|
left = left.obj
|
||||||
|
right = right.obj
|
||||||
|
|
||||||
|
try:
|
||||||
|
result = operation(left, right)
|
||||||
|
except TypeError:
|
||||||
|
# Could be True or False.
|
||||||
|
return ContextSet(compiled.create(evaluator, True), compiled.create(evaluator, False))
|
||||||
|
else:
|
||||||
|
return ContextSet(compiled.create(evaluator, result))
|
||||||
|
elif operator == 'in':
|
||||||
|
return NO_CONTEXTS
|
||||||
|
|
||||||
|
def check(obj):
|
||||||
|
"""Checks if a Jedi object is either a float or an int."""
|
||||||
|
return isinstance(obj, instance.CompiledInstance) and \
|
||||||
|
obj.name.string_name in ('int', 'float')
|
||||||
|
|
||||||
|
# Static analysis, one is a number, the other one is not.
|
||||||
|
if operator in ('+', '-') and l_is_num != r_is_num \
|
||||||
|
and not (check(left) or check(right)):
|
||||||
|
message = "TypeError: unsupported operand type(s) for +: %s and %s"
|
||||||
|
analysis.add(context, 'type-error-operation', operator,
|
||||||
|
message % (left, right))
|
||||||
|
|
||||||
|
return ContextSet(left, right)
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ def _paths_from_assignment(module_context, expr_stmt):
|
|||||||
continue
|
continue
|
||||||
|
|
||||||
from jedi.evaluate.iterable import py__iter__
|
from jedi.evaluate.iterable import py__iter__
|
||||||
from jedi.evaluate.precedence import is_string
|
from jedi.evaluate.syntax_tree import is_string
|
||||||
cn = ContextualizedNode(module_context.create_context(expr_stmt), expr_stmt)
|
cn = ContextualizedNode(module_context.create_context(expr_stmt), expr_stmt)
|
||||||
for lazy_context in py__iter__(module_context.evaluator, cn.infer(), cn):
|
for lazy_context in py__iter__(module_context.evaluator, cn.infer(), cn):
|
||||||
for context in lazy_context.infer():
|
for context in lazy_context.infer():
|
||||||
|
|||||||
Reference in New Issue
Block a user