diff --git a/CHANGELOG.rst b/CHANGELOG.rst index de8e0bf2..f288f8f4 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -16,6 +16,7 @@ Unreleased - Functions with ``@property`` now return ``property`` instead of ``function`` in ``Name().type`` - Started using annotations +- Better support for the walrus operator - Project attributes are now read accessible This is likely going to be the last minor release before 1.0. diff --git a/jedi/inference/__init__.py b/jedi/inference/__init__.py index 8cf6a1c2..7409aa84 100644 --- a/jedi/inference/__init__.py +++ b/jedi/inference/__init__.py @@ -170,6 +170,8 @@ class InferenceState: return tree_name_to_values(self, context, name) elif type_ == 'param': return context.py__getattribute__(name.value, position=name.end_pos) + elif type_ == 'namedexpr_test': + return context.infer_node(def_) else: result = follow_error_node_imports_if_possible(context, name) if result is not None: diff --git a/jedi/inference/syntax_tree.py b/jedi/inference/syntax_tree.py index fb7743cb..0b02d00f 100644 --- a/jedi/inference/syntax_tree.py +++ b/jedi/inference/syntax_tree.py @@ -753,6 +753,8 @@ def tree_name_to_values(inference_state, context, tree_name): types = NO_VALUES elif typ == 'del_stmt': types = NO_VALUES + elif typ == 'namedexpr_test': + types = infer_node(context, node) else: raise ValueError("Should not happen. type: %s" % typ) return types diff --git a/jedi/parser_utils.py b/jedi/parser_utils.py index 81c8391e..9c1290be 100644 --- a/jedi/parser_utils.py +++ b/jedi/parser_utils.py @@ -90,7 +90,7 @@ def get_flow_branch_keyword(flow_node, node): first_leaf = child.get_first_leaf() if first_leaf in _FLOW_KEYWORDS: keyword = first_leaf - return 0 + return None def clean_scope_docstring(scope_node): @@ -239,7 +239,7 @@ def get_parent_scope(node, include_flows=False): return None # It's a module already. while True: - if is_scope(scope) or include_flows and isinstance(scope, tree.Flow): + if is_scope(scope): if scope.type in ('classdef', 'funcdef', 'lambdef'): index = scope.children.index(':') if scope.children[index].start_pos >= node.start_pos: @@ -251,6 +251,14 @@ def get_parent_scope(node, include_flows=False): scope = scope.parent continue return scope + elif include_flows and isinstance(scope, tree.Flow): + # The cursor might be on `if foo`, so the parent scope will not be + # the if, but the parent of the if. + if not (scope.type == 'if_stmt' + and any(n.start_pos <= node.start_pos < n.end_pos + for n in scope.get_test_nodes())): + return scope + scope = scope.parent diff --git a/test/completion/flow_analysis.py b/test/completion/flow_analysis.py index ed1f0533..4082fb0e 100644 --- a/test/completion/flow_analysis.py +++ b/test/completion/flow_analysis.py @@ -5,7 +5,7 @@ x = 3 if NOT_DEFINED: x = '' -#? 6 int() +#? 6 int() str() elif x: pass else: diff --git a/test/completion/named_expression.py b/test/completion/named_expression.py index 37c835a5..11293b68 100644 --- a/test/completion/named_expression.py +++ b/test/completion/named_expression.py @@ -1,3 +1,6 @@ +# For assignment expressions / named expressions / walrus operators / whatever +# they are called. + # python >= 3.8 b = (a:=1, a) @@ -11,3 +14,39 @@ b = ('':=1,) #? int() b[0] + +def test_assignments(): + match = '' + #? str() + match + #? 8 int() + if match := 1: + #? int() + match + #? int() + match + +def test_assignments2(): + class Foo: + match = '' + #? str() + Foo.match + #? 13 int() + if Foo.match := 1: + #? str() + Foo.match + #? str() + Foo.match + + #? + y + #? 16 str() + if y := Foo.match: + #? str() + y + #? str() + y + + #? 8 str() + if z := Foo.match: + pass