Merge remote-tracking branch 'upstream/dev' into fix_runtime_error

Conflicts:
	jedi/evaluate/imports.py
This commit is contained in:
ColinDuquesnoy
2014-09-29 11:53:35 +02:00
25 changed files with 519 additions and 413 deletions
+41 -23
View File
@@ -69,7 +69,7 @@ backtracking algorithm.
.. todo:: nonlocal statement, needed or can be ignored? (py3k)
"""
import copy
import itertools
from itertools import tee, chain
from jedi._compatibility import next, hasattr, unicode
from jedi.parser import representation as pr
@@ -172,20 +172,7 @@ class Evaluator(object):
return precedence.process_precedence_element(self, p) or []
def eval_statement_element(self, element):
if pr.Array.is_type(element, pr.Array.NOARRAY):
try:
lst_cmp = element[0].expression_list()[0]
if not isinstance(lst_cmp, pr.ListComprehension):
raise IndexError
except IndexError:
r = list(itertools.chain.from_iterable(self.eval_statement(s)
for s in element))
else:
r = [iterable.GeneratorComprehension(self, lst_cmp)]
call_path = element.generate_call_path()
next(call_path, None) # the first one has been used already
return self.follow_path(call_path, r, element.parent)
elif isinstance(element, pr.ListComprehension):
if isinstance(element, pr.ListComprehension):
return self.eval_statement(element.stmt)
elif isinstance(element, pr.Lambda):
return [er.Function(self, element)]
@@ -219,9 +206,20 @@ class Evaluator(object):
current = next(path)
if isinstance(current, pr.Array):
types = [iterable.Array(self, current)]
if current.type == pr.Array.NOARRAY:
try:
lst_cmp = current[0].expression_list()[0]
if not isinstance(lst_cmp, pr.ListComprehension):
raise IndexError
except IndexError:
types = list(chain.from_iterable(self.eval_statement(s)
for s in current))
else:
types = [iterable.GeneratorComprehension(self, lst_cmp)]
else:
types = [iterable.Array(self, current)]
else:
if isinstance(current, pr.NamePart):
if isinstance(current, pr.Name):
# This is the first global lookup.
types = self.find_types(scope, current, position=position,
search_global=True)
@@ -241,7 +239,7 @@ class Evaluator(object):
to follow a call like ``module.a_type.Foo.bar`` (in ``from_somewhere``).
"""
results_new = []
iter_paths = itertools.tee(path, len(types))
iter_paths = tee(path, len(types))
for i, typ in enumerate(types):
fp = self._follow_path(iter_paths[i], typ, call_scope)
@@ -320,12 +318,26 @@ class Evaluator(object):
return types
def goto(self, stmt, call_path):
if isinstance(stmt, pr.Import):
# Nowhere to goto for aliases
if stmt.alias == call_path[0]:
return [call_path[0]]
names = stmt.get_all_import_names()
if stmt.alias:
names = names[:-1]
# Filter names that are after our Name
removed_names = len(names) - names.index(call_path[0]) - 1
i = imports.ImportWrapper(self, stmt, kill_count=removed_names,
nested_resolve=True)
return i.follow(is_goto=True)
# Return the name defined in the call_path, if it's part of the
# statement name definitions. Only return, if it's one name and one
# name only. Otherwise it's a mixture between a definition and a
# reference. In this case it's just a definition. So we stay on it.
if len(call_path) == 1 and isinstance(call_path[0], pr.NamePart) \
and call_path[0] in [d.names[-1] for d in stmt.get_defined_names()]:
if len(call_path) == 1 and isinstance(call_path[0], pr.Name) \
and call_path[0] in stmt.get_defined_names():
# Named params should get resolved to their param definitions.
if pr.Array.is_type(stmt.parent, pr.Array.TUPLE, pr.Array.NOARRAY) \
and stmt.parent.previous:
@@ -337,9 +349,15 @@ class Evaluator(object):
param_names = []
named_param_name = stmt.get_defined_names()[0]
for typ in self.eval_call(call):
for param in typ.params:
if isinstance(typ, er.Class):
params = []
for init_method in typ.py__getattribute__('__init__'):
params += init_method.params
else:
params = typ.params
for param in params:
if unicode(param.get_name()) == unicode(named_param_name):
param_names.append(param.get_name().names[-1])
param_names.append(param.get_name())
return param_names
return [call_path[0]]
@@ -364,7 +382,7 @@ class Evaluator(object):
def filter_private_variable(scope, call_scope, var_name):
"""private variables begin with a double underline `__`"""
var_name = str(var_name) # var_name could be a NamePart
var_name = str(var_name) # var_name could be a Name
if isinstance(var_name, (str, unicode)) and isinstance(scope, er.Instance)\
and var_name.startswith('__') and not var_name.endswith('__'):
s = call_scope.get_parent_until((pr.Class, er.Instance, compiled.CompiledObject))
-1
View File
@@ -240,7 +240,6 @@ class CompiledName(FakeName):
super(CompiledName, self).__init__(name)
self._obj = obj
self.name = name
self.start_pos = 0, 0 # an illegal start_pos, to make sorting easy.
def __repr__(self):
try:
+2 -2
View File
@@ -87,7 +87,7 @@ def search_params(evaluator, param):
# Need to take right index, because there could be a
# func usage before.
call_path_simple = [unicode(d) if isinstance(d, pr.NamePart)
call_path_simple = [unicode(d) if isinstance(d, pr.Name)
else d for d in call_path]
i = listRightIndex(call_path_simple, func_name)
before, after = call_path[:i], call_path[i + 1:]
@@ -121,7 +121,7 @@ def search_params(evaluator, param):
for params in get_posibilities(evaluator, module, func_name):
for p in params:
if str(p) == param_name:
result += evaluator.eval_statement(p.parent)
result += evaluator.eval_statement(p.get_definition())
return result
func = param.get_parent_until(pr.Function)
+19 -15
View File
@@ -42,7 +42,7 @@ class NameFinder(object):
types = self._names_to_types(names, resolve_decorator)
if not names and not types \
and not (isinstance(self.name_str, pr.NamePart)
and not (isinstance(self.name_str, pr.Name)
and isinstance(self.name_str.parent.parent, pr.Param)):
if not isinstance(self.name_str, (str, unicode)): # TODO Remove?
if search_global:
@@ -102,18 +102,18 @@ class NameFinder(object):
or isinstance(scope, compiled.CompiledObject) \
or isinstance(stmt, pr.ExprStmt) and stmt.is_global():
# Always reachable.
names.append(name.names[-1])
names.append(name)
else:
check = flow_analysis.break_check(self._evaluator,
name_list_scope,
er.wrap(self._evaluator, scope),
self.scope)
if check is not flow_analysis.UNREACHABLE:
names.append(name.names[-1])
names.append(name)
if check is flow_analysis.REACHABLE:
break
if names and self._is_name_break_scope(name, stmt):
if names and self._is_name_break_scope(stmt):
if self._does_scope_break_immediately(scope, name_list_scope):
break
else:
@@ -139,16 +139,16 @@ class NameFinder(object):
evaluation, so remove them already here!
"""
for n in names:
definition = n.parent.parent
definition = n.parent
if isinstance(definition, (pr.Function, pr.Class, pr.Module)):
yield er.wrap(self._evaluator, definition).name.names[-1]
yield er.wrap(self._evaluator, definition).name
else:
yield n
def _check_getattr(self, inst):
"""Checks for both __getattr__ and __getattribute__ methods"""
result = []
# str is important to lose the NamePart!
# str is important, because it shouldn't be `Name`!
name = compiled.create(self._evaluator, str(self.name_str))
with common.ignored(KeyError):
result = inst.execute_subscope_by_name('__getattr__', [name])
@@ -161,12 +161,12 @@ class NameFinder(object):
result = inst.execute_subscope_by_name('__getattribute__', [name])
return result
def _is_name_break_scope(self, name, stmt):
def _is_name_break_scope(self, stmt):
"""
Returns True except for nested imports and instance variables.
"""
if stmt.isinstance(pr.ExprStmt):
if isinstance(name, er.InstanceElement) and not name.is_class_var:
if isinstance(stmt, er.InstanceElement) and not stmt.is_class_var:
return False
elif isinstance(stmt, pr.Import) and stmt.is_nested():
return False
@@ -219,7 +219,7 @@ class NameFinder(object):
evaluator = self._evaluator
# Add isinstance and other if/assert knowledge.
if isinstance(self.name_str, pr.NamePart):
if isinstance(self.name_str, pr.Name):
flow_scope = self.name_str.parent.parent
# Ignore FunctionExecution parents for now.
until = flow_scope.get_parent_until(er.FunctionExecution)
@@ -281,7 +281,7 @@ class NameFinder(object):
if isinstance(p, pr.Flow) and p.command == 'except' and p.inputs:
as_names = p.inputs[0].as_names
try:
if as_names[0].names[-1] == name:
if as_names[0] == name:
# TODO check for types that are not classes and add it to
# the static analysis report.
types = list(chain.from_iterable(
@@ -395,7 +395,7 @@ def check_flow_information(evaluator, flow, search_name_part, pos):
return result
def _check_isinstance_type(evaluator, stmt, search_name_part):
def _check_isinstance_type(evaluator, stmt, search_name):
try:
expression_list = stmt.expression_list()
# this might be removed if we analyze and, etc
@@ -412,8 +412,12 @@ def _check_isinstance_type(evaluator, stmt, search_name_part):
assert len(classes) == 1
assert isinstance(obj[0], pr.Call)
# names fit?
assert unicode(obj[0].name) == unicode(search_name_part.parent)
prev = search_name.parent
while prev.previous is not None:
prev = prev.previous
# Do a simple get_code comparison. They should just have the same code,
# and everything will be all right.
assert obj[0].get_code() == prev.get_code()
assert isinstance(classes[0], pr.StatementElement) # can be type or tuple
except AssertionError:
return []
@@ -583,7 +587,7 @@ def find_assignments(lhs, results, seek_name):
"""
if isinstance(lhs, pr.Array):
return _assign_tuples(lhs, results, seek_name)
elif unicode(lhs.name.names[-1]) == seek_name:
elif unicode(lhs.name) == seek_name:
return results
else:
return []
+18 -17
View File
@@ -1,6 +1,7 @@
import copy
from itertools import chain
from jedi._compatibility import unicode
from jedi.parser import representation as pr
from jedi import debug
@@ -14,7 +15,7 @@ def deep_ast_copy(obj, new_elements_default=None):
return key_value[0] not in ('_expression_list', '_assignment_details')
new_elements = new_elements_default or {}
accept = (pr.Simple, pr.NamePart, pr.KeywordStatement)
accept = (pr.Simple, pr.Name, pr.KeywordStatement)
def recursion(obj):
# If it's already in the cache, just return it.
@@ -50,6 +51,8 @@ def deep_ast_copy(obj, new_elements_default=None):
# because there are several references that don't walk the whole
# tree in there.
items = sorted(items, key=sort_stmt)
else:
items = sorted(items, key=lambda x: x[0] == '_names_dict')
# Actually copy and set attributes.
new_obj = copy.copy(obj)
@@ -67,13 +70,17 @@ def deep_ast_copy(obj, new_elements_default=None):
pass
elif key in ['parent_function', 'use_as_parent', '_sub_module']:
continue
elif key == '_names_dict':
d = dict((k, sequence_recursion(v)) for k, v in value.items())
setattr(new_obj, key, d)
elif isinstance(value, (list, tuple)):
setattr(new_obj, key, list_or_tuple_rec(value))
setattr(new_obj, key, sequence_recursion(value))
elif isinstance(value, accept):
setattr(new_obj, key, recursion(value))
return new_obj
def list_or_tuple_rec(array_obj):
def sequence_recursion(array_obj):
if isinstance(array_obj, tuple):
copied_array = list(array_obj)
else:
@@ -82,7 +89,7 @@ def deep_ast_copy(obj, new_elements_default=None):
if isinstance(el, accept):
copied_array[i] = recursion(el)
elif isinstance(el, (tuple, list)):
copied_array[i] = list_or_tuple_rec(el)
copied_array[i] = sequence_recursion(el)
if isinstance(array_obj, tuple):
return tuple(copied_array)
@@ -196,9 +203,7 @@ def scan_statement_for_calls(stmt, search_name, assignment_details=False):
if isinstance(s_new, pr.Array):
result += scan_array(s_new, search_name)
else:
n = s_new.name
if isinstance(n, pr.Name) \
and search_name in [str(x) for x in n.names]:
if search_name == unicode(s_new.name):
result.append(c)
s_new = s_new.next
@@ -217,7 +222,7 @@ def get_module_name_parts(module):
def scope_name_parts(scope):
for s in scope.subscopes:
# Yield the name parts, not names.
yield s.name.names[0]
yield s.name
for need_yield_from in scope_name_parts(s):
yield need_yield_from
@@ -226,7 +231,7 @@ def get_module_name_parts(module):
for stmt_or_import in statements_or_imports:
if isinstance(stmt_or_import, pr.Import):
for name in stmt_or_import.get_all_import_names():
name_parts.update(name.names)
name_parts.add(name)
else:
# Running this ensures that all the expression lists are generated
# and the parents are all set. (Important for Lambdas) Howeer, this
@@ -238,7 +243,7 @@ def get_module_name_parts(module):
# all the name_parts.
for tok in stmt_or_import._token_list:
if isinstance(tok, pr.Name):
name_parts.update(tok.names)
name_parts.add(tok)
return name_parts
@@ -298,18 +303,14 @@ class FakeStatement(pr.ExprStmt):
class FakeImport(pr.Import):
def __init__(self, name, parent, level=0):
p = 0, 0
super(FakeImport, self).__init__(FakeSubModule, p, p, name,
super(FakeImport, self).__init__(FakeSubModule, p, p, [name],
relative_count=level)
self.parent = parent
class FakeName(pr.Name):
def __init__(self, name_or_names, parent=None, start_pos=(0, 0)):
if isinstance(name_or_names, list):
names = [(n, start_pos) for n in name_or_names]
else:
names = [(name_or_names, start_pos)]
super(FakeName, self).__init__(FakeSubModule, names, start_pos, start_pos, parent)
def __init__(self, name_str, parent=None, start_pos=(0, 0)):
super(FakeName, self).__init__(FakeSubModule, name_str, parent, start_pos)
def get_definition(self):
return self.parent
+70 -65
View File
@@ -67,13 +67,13 @@ class ImportWrapper(pr.Base):
# rest is import_path resolution
import_path = []
if import_stmt.from_ns:
import_path += import_stmt.from_ns.names
if import_stmt.namespace:
if import_stmt.from_names:
import_path += import_stmt.from_names
if import_stmt.namespace_names:
if self.import_stmt.is_nested() and not nested_resolve:
import_path.append(import_stmt.namespace.names[0])
import_path.append(import_stmt.namespace_names[0])
else:
import_path += import_stmt.namespace.names
import_path += import_stmt.namespace_names
for i in range(kill_count + int(is_like_search)):
if import_path:
@@ -110,6 +110,7 @@ class ImportWrapper(pr.Base):
m = _load_module(rel_path)
names += m.get_defined_names()
else:
# flask
if self.import_path == ('flask', 'ext'):
# List Flask extensions like ``flask_foo``
for mod in self._get_module_names():
@@ -122,6 +123,8 @@ class ImportWrapper(pr.Base):
flaskext = os.path.join(dir, 'flaskext')
if os.path.isdir(flaskext):
names += self._get_module_names([flaskext])
# namespace packages
if on_import_stmt and isinstance(scope, pr.Module) \
and scope.path.endswith('__init__.py'):
pkg_path = os.path.dirname(scope.path)
@@ -136,18 +139,18 @@ class ImportWrapper(pr.Base):
# ``sys.modules`` modification.
names.append(self._generate_name('path'))
continue
if not self.import_stmt.from_names or self.is_partial_import:
# from_names must be defined to access module
# values plus a partial import means that there
# is something after the import, which
# automatically implies that there must not be
# any non-module scope.
continue
from jedi.evaluate import finder
for s, scope_names in finder.get_names_of_scope(self._evaluator,
scope, include_builtin=False):
for n in scope_names:
if self.import_stmt.from_ns is None \
or self.is_partial_import:
# from_ns must be defined to access module
# values plus a partial import means that there
# is something after the import, which
# automatically implies that there must not be
# any non-module scope.
continue
names.append(n)
return names
@@ -179,55 +182,57 @@ class ImportWrapper(pr.Base):
# check recursion
return []
if self.import_path:
try:
module, rest = self._importer.follow_file_system()
except ModuleNotFound as e:
analysis.add(self._evaluator, 'import-error', e.name_part)
return []
try:
if self.import_path:
try:
module, rest = self._importer.follow_file_system()
except ModuleNotFound as e:
analysis.add(self._evaluator, 'import-error', e.name_part)
return []
if module is None:
return []
if module is None:
return []
if self.import_stmt.is_nested() and not self.nested_resolve:
scopes = [NestedImportModule(module, self.import_stmt)]
else:
scopes = [module]
star_imports = remove_star_imports(self._evaluator, module)
if star_imports:
scopes = [StarImportModule(scopes[0], star_imports)]
# goto only accepts Names or NameParts
if is_goto and not rest:
scopes = [s.name.names[-1] for s in scopes]
# follow the rest of the import (not FS -> classes, functions)
if len(rest) > 1 or rest and self.is_like_search:
scopes = []
if ('os', 'path') == self.import_path[:2] \
and not self._is_relative_import():
# This is a huge exception, we follow a nested import
# ``os.path``, because it's a very important one in Python
# that is being achieved by messing with ``sys.modules`` in
# ``os``.
scopes = self._evaluator.follow_path(iter(rest), [module], module)
elif rest:
if is_goto:
scopes = list(chain.from_iterable(
self._evaluator.find_types(s, rest[0], is_goto=True)
for s in scopes))
if self.import_stmt.is_nested() and not self.nested_resolve:
scopes = [NestedImportModule(module, self.import_stmt)]
else:
scopes = list(chain.from_iterable(
self._evaluator.follow_path(iter(rest), [s], s)
for s in scopes))
else:
scopes = [ImportWrapper.GlobalNamespace]
debug.dbg('after import: %s', scopes)
if not scopes:
analysis.add(self._evaluator, 'import-error',
self._importer.import_path[-1])
self._evaluator.recursion_detector.pop_stmt()
scopes = [module]
star_imports = remove_star_imports(self._evaluator, module)
if star_imports:
scopes = [StarImportModule(scopes[0], star_imports)]
# goto only accepts `Name`
if is_goto and not rest:
scopes = [s.name for s in scopes]
# follow the rest of the import (not FS -> classes, functions)
if len(rest) > 1 or rest and self.is_like_search:
scopes = []
if ('os', 'path') == self.import_path[:2] \
and not self._is_relative_import():
# This is a huge exception, we follow a nested import
# ``os.path``, because it's a very important one in Python
# that is being achieved by messing with ``sys.modules`` in
# ``os``.
scopes = self._evaluator.follow_path(iter(rest), [module], module)
elif rest:
if is_goto:
scopes = list(chain.from_iterable(
self._evaluator.find_types(s, rest[0], is_goto=True)
for s in scopes))
else:
scopes = list(chain.from_iterable(
self._evaluator.follow_path(iter(rest), [s], s)
for s in scopes))
else:
scopes = [ImportWrapper.GlobalNamespace]
debug.dbg('after import: %s', scopes)
if not scopes:
analysis.add(self._evaluator, 'import-error',
self._importer.import_path[-1])
finally:
self._evaluator.recursion_detector.pop_stmt()
return scopes
@@ -244,12 +249,12 @@ class NestedImportModule(pr.Module):
# This is not an existing Import statement. Therefore, set position to
# 0 (0 is not a valid line number).
zero = (0, 0)
names = [unicode(name_part) for name_part in i.namespace.names[1:]]
names = [unicode(name_part) for name_part in i.namespace_names[1:]]
name = helpers.FakeName(names, self._nested_import)
new = pr.Import(i._sub_module, zero, zero, name)
new.parent = self._module
debug.dbg('Generated a nested import: %s', new)
return helpers.FakeName(str(i.namespace.names[1]), new)
return helpers.FakeName(str(i.namespace_names[1]), new)
def _get_defined_names(self):
"""
@@ -330,7 +335,7 @@ class _Importer(object):
self.file_path = os.path.dirname(path) if path is not None else None
def str_import_path(self):
"""Returns the import path as pure strings instead of NameParts."""
"""Returns the import path as pure strings instead of `Name`."""
return tuple(str(name_part) for name_part in self.import_path)
def get_relative_path(self):
@@ -372,12 +377,12 @@ class _Importer(object):
pos = (part._line, part._column)
try:
self.import_path = (
pr.NamePart(FakeSubModule, 'flask_' + str(part), part.parent, pos),
pr.Name(FakeSubModule, 'flask_' + str(part), part.parent, pos),
) + orig_path[3:]
return self._real_follow_file_system()
except ModuleNotFound as e:
self.import_path = (
pr.NamePart(FakeSubModule, 'flaskext', part.parent, pos),
pr.Name(FakeSubModule, 'flaskext', part.parent, pos),
) + orig_path[2:]
return self._real_follow_file_system()
return self._real_follow_file_system()
@@ -606,5 +611,5 @@ def get_modules_containing_name(mods, name):
for p in sorted(paths):
# make testing easier, sort it - same results on every interpreter
c = check_python_file(p)
if c is not None and c not in mods:
if c is not None and c not in mods and not isinstance(c, compiled.CompiledObject):
yield c
+3 -31
View File
@@ -193,14 +193,14 @@ class Array(use_metaclass(CachedMetaClass, IterableWrapper)):
def scope_names_generator(self, position=None):
"""
This method generates all `ArrayMethod` for one pr.Array.
It returns e.g. for a list: append, pop, ...
"""
# `array.type` is a string with the type, e.g. 'list'.
scope = self._evaluator.find_types(compiled.builtin, self._array.type)[0]
scope = self._evaluator.execute(scope)[0] # builtins only have one class
from jedi.evaluate.representation import get_instance_el
for _, names in scope.scope_names_generator():
yield self, [ArrayMethod(n) for n in names]
yield self, [get_instance_el(self._evaluator, self, n) for n in names]
@common.safe_property
def parent(self):
@@ -225,34 +225,6 @@ class Array(use_metaclass(CachedMetaClass, IterableWrapper)):
return "<e%s of %s>" % (type(self).__name__, self._array)
class ArrayMethod(IterableWrapper):
"""
A name, e.g. `list.append`, it is used to access the original array
methods.
"""
def __init__(self, name):
super(ArrayMethod, self).__init__()
self.name = name
@property
@underscore_memoization
def names(self):
# TODO remove this method, we need the ArrayMethod input to be a NamePart.
return [pr.NamePart(self.name._sub_module, unicode(n), self, n.start_pos) for n in self.name.names]
def __getattr__(self, name):
# Set access privileges:
if name not in ['parent', 'start_pos', 'end_pos', 'get_code', 'get_definition']:
raise AttributeError('Strange access on %s: %s.' % (self, name))
return getattr(self.name, name)
def get_parent_until(self):
return compiled.builtin
def __repr__(self):
return "<%s of %s>" % (type(self).__name__, self.name)
class MergedArray(Array):
def __init__(self, evaluator, arrays):
super(MergedArray, self).__init__(evaluator, arrays[-1]._array)
@@ -342,7 +314,7 @@ def _check_array_additions(evaluator, compare_array, module, is_list):
result = []
for c in calls:
call_path = list(c.generate_call_path())
call_path_simple = [unicode(n) if isinstance(n, pr.NamePart) else n
call_path_simple = [unicode(n) if isinstance(n, pr.Name) else n
for n in call_path]
separate_index = call_path_simple.index(add_name)
if add_name == call_path_simple[-1] or separate_index == 0:
+4 -4
View File
@@ -1,9 +1,10 @@
import copy
from jedi._compatibility import unicode, zip_longest
from jedi import debug
from jedi import common
from jedi.parser import representation as pr
from jedi.evaluate import iterable
from jedi import common
from jedi.evaluate import helpers
from jedi.evaluate import analysis
@@ -294,7 +295,7 @@ def _iterate_star_args(evaluator, array, expression_list, func):
for field_stmt in array.iter_content():
yield helpers.FakeStatement([field_stmt])
elif isinstance(array, Instance) and array.name.get_code() == 'tuple':
pass
debug.warning('Ignored a tuple *args input %s' % array)
else:
if expression_list:
m = "TypeError: %s() argument after * must be a sequence, not %s" \
@@ -320,6 +321,7 @@ def _star_star_dict(evaluator, array, expression_list, func):
elif isinstance(call, pr.Call):
key = call.name
else:
debug.warning('Ignored complicated **kwargs stmt %s' % call)
continue # We ignore complicated statements here, for now.
# If the string is a duplicate, we don't care it's illegal Python
@@ -359,8 +361,6 @@ def _gen_param_name_copy(func, var_args, param, keys=(), values=(), array_type=N
new_param.set_expression_list([arr])
name = copy.copy(param.get_name())
name.names = [copy.copy(name.names[0])]
name.names[0].parent = name
name.parent = new_param
return name
+57 -20
View File
@@ -164,12 +164,14 @@ class Instance(use_metaclass(CachedMetaClass, Executed)):
# because to follow them and their self variables is too
# complicated.
sub = self._get_method_execution(sub)
for n in sub.get_defined_names():
# Only names with the selfname are being added.
# It is also important, that they have a len() of 2,
# because otherwise, they are just something else
if unicode(n.names[0]) == self_name and len(n.names) == 2:
add_self_dot_name(n)
for per_name_list in sub.get_names_dict().values():
for call in per_name_list:
if unicode(call.name) == self_name \
and isinstance(call.next, pr.Call) \
and call.next.next is None:
names.append(get_instance_el(self._evaluator, self, call.next.name))
#if unicode(n.names[0]) == self_name and len(n.names) == 2:
# add_self_dot_name(n)
for s in self.base.py__bases__(self._evaluator):
if not isinstance(s, compiled.CompiledObject):
@@ -243,7 +245,12 @@ def get_instance_el(evaluator, instance, var, is_class_var=False):
untouched.
"""
if isinstance(var, (Instance, compiled.CompiledObject, pr.Operator, Token,
pr.Module, FunctionExecution)):
pr.Module, FunctionExecution, pr.Name)):
if isinstance(var, pr.Name):
# TODO temp solution, remove later, Name should never get
# here?
par = get_instance_el(evaluator, instance, var.parent, is_class_var)
return pr.Name(var._sub_module, unicode(var), par, var.start_pos)
return var
var = wrap(evaluator, var)
@@ -275,6 +282,9 @@ class InstanceElement(use_metaclass(CachedMetaClass, pr.Base)):
return par
def get_parent_until(self, *args, **kwargs):
if isinstance(self.var, pr.Name):
# TODO Name should never even be InstanceElements
return pr.Simple.get_parent_until(self.parent, *args, **kwargs)
return pr.Simple.get_parent_until(self, *args, **kwargs)
def get_definition(self):
@@ -291,12 +301,6 @@ class InstanceElement(use_metaclass(CachedMetaClass, pr.Base)):
return [get_instance_el(self._evaluator, self.instance, command, self.is_class_var)
for command in self.var.expression_list()]
@property
@underscore_memoization
def names(self):
return [pr.NamePart(helpers.FakeSubModule, unicode(n), self, n.start_pos)
for n in self.var.names]
@property
@underscore_memoization
def name(self):
@@ -390,6 +394,9 @@ class Class(use_metaclass(CachedMetaClass, Wrapper)):
def py__call__(self, evaluator, params):
return [Instance(evaluator, self, params)]
def py__getattribute__(self, name):
return self._evaluator.find_types(self, name)
def scope_names_generator(self, position=None, add_class_vars=True):
def in_iterable(name, iterable):
""" checks if the name is in the variable 'iterable'. """
@@ -522,6 +529,21 @@ class Function(use_metaclass(CachedMetaClass, Wrapper)):
return "<e%s of %s%s>" % (type(self).__name__, self.base_func, dec)
class LazyDict(object):
def __init__(self, old_dct, copy_func):
self._copy_func = copy_func
self._old_dct = old_dct
def __getitem__(self, key):
return self._copy_func(self._old_dct[key])
@underscore_memoization
def values(self):
# TODO REMOVE this. Not necessary with correct name lookups.
for calls in self._old_dct.values():
yield self._copy_func(calls)
class FunctionExecution(Executed):
"""
This class is used to evaluate functions and their returns.
@@ -570,6 +592,10 @@ class FunctionExecution(Executed):
break
return types
@underscore_memoization
def get_names_dict(self):
return LazyDict(self.base.get_names_dict(), self._copy_list)
@memoize_default(default=())
def _get_params(self):
"""
@@ -591,15 +617,13 @@ class FunctionExecution(Executed):
names = pr.filter_after_position(pr.Scope.get_defined_names(self), position)
yield self, self._get_params() + names
def _copy_list(self, list_name):
def _copy_list(self, lst):
"""
Copies a list attribute of a parser Function. Copying is very
expensive, because it is something like `copy.deepcopy`. However, these
copied objects can be used for the executions, as if they were in the
execution.
"""
# Copy all these lists into this local function.
lst = getattr(self.base, list_name)
objects = []
for element in lst:
self._scope_copy(element.parent)
@@ -622,22 +646,22 @@ class FunctionExecution(Executed):
@common.safe_property
@memoize_default([])
def returns(self):
return self._copy_list('returns')
return self._copy_list(self.base.returns)
@common.safe_property
@memoize_default([])
def asserts(self):
return self._copy_list('asserts')
return self._copy_list(self.base.asserts)
@common.safe_property
@memoize_default([])
def statements(self):
return self._copy_list('statements')
return self._copy_list(self.base.statements)
@common.safe_property
@memoize_default([])
def subscopes(self):
return self._copy_list('subscopes')
return self._copy_list(self.base.subscopes)
def get_statement_for_position(self, pos):
return pr.Scope.get_statement_for_position(self, pos)
@@ -667,6 +691,11 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
# All the additional module attributes are strings.
return [helpers.LazyName(n, parent_callback) for n in names]
@property
@memoize_default()
def name(self):
return pr.Name(self, unicode(self.base.name), self, (1, 0))
@memoize_default()
def _sub_modules(self):
"""
@@ -683,6 +712,14 @@ class ModuleWrapper(use_metaclass(CachedMetaClass, pr.Module, Wrapper)):
imp = helpers.FakeImport(name, self, level=1)
name.parent = imp
names.append(name)
# TODO add something like this in the future, its cleaner than the
# import hacks.
# ``os.path`` is a hardcoded exception, because it's a
# ``sys.modules`` modification.
#if str(self.name) == 'os':
# names.append(helpers.FakeName('path', parent=self))
return names
def __getattr__(self, name):
+8 -10
View File
@@ -60,7 +60,7 @@ def _paths_from_assignment(evaluator, statement):
for exp_list, operator in statement.assignment_details:
if len(exp_list) != 1 or not isinstance(exp_list[0], pr.Call):
continue
if unicode(exp_list[0].name) != 'sys.path':
if exp_list[0].names() != ['sys', 'path']:
continue
# TODO at this point we ignore all ways what could be assigned to
# sys.path or an execution of it. Here we could do way more
@@ -88,17 +88,15 @@ def _paths_from_insert(module_path, exe):
def _paths_from_call_expression(module_path, call):
""" extract the path from either "sys.path.append" or "sys.path.insert" """
if not call.next_is_execution():
return
names = call.names()
if names[:3] != ['sys', 'path', 'append'] and names[:3] != ['sys', 'path', 'insert']:
return []
if not call.next.next.next_is_execution():
return []
n = call.name
if not isinstance(n, pr.Name) or len(n.names) != 3:
return
names = [unicode(x) for x in n.names]
if names[:2] != ['sys', 'path']:
return
cmd = names[2]
exe = call.next
exe = call.next.next.next
path = None
if cmd == 'insert' and len(exe) == 2:
path = _paths_from_insert(module_path, exe)
elif cmd == 'append' and len(exe) == 1: