Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AUTHORS.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ David Halter (@davidhalter) <davidhalter88@gmail.com>
Code Contributors
=================
Alisdair Robertson (@robodair)
AryanRG (@AryanRG)
Bryan Forbes (@bryanforbes) <bryan@reigndropsfall.net>
Likio3000 (@Likio3000) — documentation

Expand Down
4 changes: 4 additions & 0 deletions parso/python/tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,10 @@ def get_definition(self, import_name_always=False, include_setitem=False):
return node.parent # The try_stmt.
return None

# Unparenthesized assignment expressions in calls use argument nodes.
if type_ == 'argument' and node.children[1] == ':=':
return node if node.children[0] is self else None

while node is not None:
if node.type == 'suite':
return None
Expand Down
42 changes: 42 additions & 0 deletions test/test_parser_tree.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# -*- coding: utf-8 # This file contains Unicode characters.

import sys
from textwrap import dedent

import pytest
Expand Down Expand Up @@ -216,6 +217,47 @@ def test_is_definition(code, name_index, is_definition, include_setitem):
assert name.is_definition(include_setitem=include_setitem) == is_definition


@pytest.mark.parametrize('code', [
'f(target := source)\n',
'f(target := target)\n',
'f(0, target := source)\n',
'result = f(target := source)\n',
'def outer():\n return f(target := source)\n',
'f(target := source.attr)\n',
'f(target := g(source))\n',
'obj.f(target := source)\n',
])
def test_walrus_argument_definition(code, version_ge_py38):
if sys.version_info >= (3, 8):
compile(code, '<walrus argument>', 'exec')
module = parse(code, version=version_ge_py38, error_recovery=False)
names = module.get_used_names()
target = names['target'][0]

assert target.get_definition() is target.parent
assert target.is_definition()
assert target.is_definition(include_setitem=True)
assert target.get_definition(import_name_always=True) is target.parent
for name in names.get('source', []) + names['target'][1:]:
assert name.get_definition() is None
assert not name.is_definition()
assert module.get_code() == code


@pytest.mark.parametrize('code, expected', [
('f(target=source)\n', [False]),
('f(target)\n', [False]),
('f(*target)\n', [False]),
('f(**target)\n', [False]),
('f(target for target in source)\n', [False, True]),
('(target := source)\n', [True]),
('f((target := source))\n', [True]),
])
def test_argument_definition_controls(code, expected, version_ge_py38):
module = parse(code, version=version_ge_py38, error_recovery=False)
assert [name.is_definition() for name in module.get_used_names()['target']] == expected


def test_iter_funcdefs():
code = dedent('''
def normal(): ...
Expand Down
Loading