diff --git a/jedi/evaluate/compiled/fake.py b/jedi/evaluate/compiled/fake.py index 597a77fa..20e911a1 100644 --- a/jedi/evaluate/compiled/fake.py +++ b/jedi/evaluate/compiled/fake.py @@ -7,6 +7,7 @@ mixing in Python code, the autocompletion should work much better for builtins. import os import inspect import types +from itertools import chain from jedi._compatibility import is_py3, builtins, unicode, is_py34 from jedi.parser.python import parse @@ -74,7 +75,7 @@ def _load_faked_module(module): def _search_scope(scope, obj_name): - for s in scope.subscopes: + for s in chain(scope.iter_classdefs(), scope.iter_funcdefs()): if s.name.value == obj_name: return s diff --git a/jedi/evaluate/docstrings.py b/jedi/evaluate/docstrings.py index 1b7db0da..f842b80f 100644 --- a/jedi/evaluate/docstrings.py +++ b/jedi/evaluate/docstrings.py @@ -134,7 +134,7 @@ def _evaluate_for_statement_string(module_context, string): # don't need to conform with the current grammar. module = parse(code.format(indent_block(string))) try: - funcdef = module.subscopes[0] + funcdef = next(module.iter_funcdefs()) # First pick suite, then simple_stmt and then the node, # which is also not the last item, because there's a newline. stmt = funcdef.children[-1].children[-1].children[-2] diff --git a/jedi/evaluate/filters.py b/jedi/evaluate/filters.py index 0784c675..053c2412 100644 --- a/jedi/evaluate/filters.py +++ b/jedi/evaluate/filters.py @@ -275,7 +275,7 @@ def get_global_filters(evaluator, context, until_position, origin_scope): ... y = None ... ''')) >>> module_node = script._get_module_node() - >>> scope = module_node.subscopes[0] + >>> scope = next(module_node.iter_funcdefs()) >>> scope >>> context = script._get_module().create_context(scope) diff --git a/jedi/evaluate/stdlib.py b/jedi/evaluate/stdlib.py index e71f9d18..1dbfae73 100644 --- a/jedi/evaluate/stdlib.py +++ b/jedi/evaluate/stdlib.py @@ -270,7 +270,7 @@ def collections_namedtuple(evaluator, obj, arguments): ) # Parse source - generated_class = parse(source, grammar=evaluator.grammar).subscopes[0] + generated_class = next(parse(source, grammar=evaluator.grammar).iter_classdefs()) return set([er.ClassContext(evaluator, generated_class, evaluator.BUILTINS)]) diff --git a/jedi/parser/python/tree.py b/jedi/parser/python/tree.py index 5ca944a2..a31a0330 100644 --- a/jedi/parser/python/tree.py +++ b/jedi/parser/python/tree.py @@ -23,8 +23,6 @@ Any subclasses of :class:`Scope`, including :class:`Module` has an attribute >>> module.imports [] - -See also :attr:`Scope.subscopes` and :attr:`Scope.statements`. """ from itertools import chain @@ -234,12 +232,7 @@ class Scope(PythonBaseNode, DocstringMixin): """ Super class for the parser tree, which represents the state of a python text file. - A Scope manages and owns its subscopes, which are classes and functions, as - well as variables and imports. It is used to access the structure of python - files. - - :param start_pos: The position (line and column) of the scope. - :type start_pos: tuple(int, int) + A Scope is either a function, class or lambda. """ __slots__ = () @@ -250,26 +243,27 @@ class Scope(PythonBaseNode, DocstringMixin): def returns(self): # Needed here for fast_parser, because the fast_parser splits and # returns will be in "normal" modules. - return self._search_in_scope(ReturnStmt) + return list(self._search_in_scope(ReturnStmt)) - @property - def subscopes(self): - return self._search_in_scope(Scope) + def iter_funcdefs(self): + return self._search_in_scope(Function) + + def iter_classdefs(self): + return self._search_in_scope(Class) @property def imports(self): - return self._search_in_scope(Import) + return list(self._search_in_scope(Import)) def _search_in_scope(self, typ): def scan(children): - elements = [] for element in children: if isinstance(element, typ): - elements.append(element) + yield element if element.type in ('suite', 'simple_stmt', 'decorated') \ or isinstance(element, Flow): - elements += scan(element.children) - return elements + for e in scan(element.children): + yield e return scan(self.children) @@ -473,7 +467,7 @@ class Function(ClassOrFunc): @property def yields(self): # TODO This is incorrect, yields are also possible in a statement. - return self._search_in_scope(YieldExpr) + return list(self._search_in_scope(YieldExpr)) def is_generator(self): return bool(self.yields) diff --git a/jedi/parser_utils.py b/jedi/parser_utils.py index dd7836cb..47a9a17c 100644 --- a/jedi/parser_utils.py +++ b/jedi/parser_utils.py @@ -164,10 +164,10 @@ def get_doc_with_call_signature(scope_node): """ call_signature = None if scope_node.type == 'classdef': - for sub in scope_node.subscopes: - if sub.name.value == '__init__': + for funcdef in scope_node.iter_funcdefs(): + if funcdef.name.value == '__init__': call_signature = \ - get_call_signature(sub, call_string=scope_node.name.value) + get_call_signature(funcdef, call_string=scope_node.name.value) elif scope_node.type in ('funcdef', 'lambdef'): call_signature = get_call_signature(scope_node) diff --git a/test/test_parser/test_param_splitting.py b/test/test_parser/test_param_splitting.py index 998398f3..ffff527c 100644 --- a/test/test_parser/test_param_splitting.py +++ b/test/test_parser/test_param_splitting.py @@ -15,7 +15,7 @@ def assert_params(param_string, **wanted_dct): ''') % param_string module = parse(source) - funcdef = module.subscopes[0] + funcdef = next(module.iter_funcdefs()) dct = dict((p.name.value, p.default and p.default.get_code()) for p in funcdef.params) assert dct == wanted_dct diff --git a/test/test_parser/test_parser.py b/test/test_parser/test_parser.py index 012d8378..a1eb1499 100644 --- a/test/test_parser/test_parser.py +++ b/test/test_parser/test_parser.py @@ -63,7 +63,7 @@ class TestCallAndName(): class TestSubscopes(): def get_sub(self, source): - return parse(source).subscopes[0] + return parse(source).children[0] def test_subscope_names(self): name = self.get_sub('class Foo: pass').name @@ -100,7 +100,7 @@ def test_end_pos(): y = None ''') parser = parse(s) - scope = parser.subscopes[0] + scope = next(parser.iter_funcdefs()) assert scope.start_pos == (3, 0) assert scope.end_pos == (5, 0) @@ -134,7 +134,7 @@ def test_hex_values_in_docstring(): return 1 ''' - doc = clean_scope_docstring(parse(source).subscopes[0]) + doc = clean_scope_docstring(next(parse(source).iter_funcdefs())) if is_py3: assert doc == '\xff' else: @@ -184,12 +184,12 @@ def test_param_splitting(): grammar = load_grammar('%s.%s' % sys.version_info[:2]) m = parse(src, grammar=grammar) if is_py3: - assert not m.subscopes + assert not list(m.iter_funcdefs()) else: # We don't want b and c to be a part of the param enumeration. Just # ignore them, because it's not what we want to support in the # future. - assert [param.name.value for param in m.subscopes[0].params] == result + assert [param.name.value for param in next(m.iter_funcdefs()).params] == result check('def x(a, (b, c)):\n pass', ['a']) check('def x((b, c)):\n pass', []) diff --git a/test/test_parser/test_parser_tree.py b/test/test_parser/test_parser_tree.py index 6ea5fdcd..7e4216fc 100644 --- a/test/test_parser/test_parser_tree.py +++ b/test/test_parser/test_parser_tree.py @@ -29,7 +29,10 @@ class TestsFunctionAndLambdaParsing(object): def node(self, request): parsed = parse(dedent(request.param[0])) request.keywords['expected'] = request.param[1] - return parsed.subscopes[0] + child = parsed.children[0] + if child.type == 'simple_stmt': + child = child.children[0] + return child @pytest.fixture() def expected(self, request, node): diff --git a/test/test_parser/test_tokenize.py b/test/test_parser/test_tokenize.py index 4379c2b1..4cddf9c1 100644 --- a/test/test_parser/test_tokenize.py +++ b/test/test_parser/test_tokenize.py @@ -23,7 +23,7 @@ class TokenTest(unittest.TestCase): def testit(): a = "huhu" ''')) - simple_stmt = parsed.subscopes[0].get_suite().children[-1] + simple_stmt = next(parsed.iter_funcdefs()).get_suite().children[-1] string = simple_stmt.children[0].get_rhs() assert string.end_pos == (3, 14) @@ -33,7 +33,7 @@ class TokenTest(unittest.TestCase): a = """huhu asdfasdf""" + "h" ''')) - expr_stmt = parsed.subscopes[0].get_suite().children[1].children[0] + expr_stmt = next(parsed.iter_funcdefs()).get_suite().children[1].children[0] string_leaf = expr_stmt.get_rhs().children[0] assert string_leaf.end_pos == (4, 11)