From 0f01212d4bb6408d37bd33ff5cd665dbc00c9e0a Mon Sep 17 00:00:00 2001 From: Apoorv Darshan Date: Thu, 9 Jul 2026 19:32:09 +0530 Subject: [PATCH] Treat named expression call arguments as positional parso raised a false 'SyntaxError: positional argument follows keyword argument' for valid calls such as f(a := 1, b). A named expression argument (name := value) was misclassified as a keyword argument because _ArglistRule treated every non-*/** argument as a keyword, so it set kw_only and flagged any following positional argument. Detect the ':=' operator and treat such arguments as positional. They no longer set kw_only, and they now correctly raise when they themselves follow a keyword argument or keyword-argument unpacking, matching CPython. Fixes #212. --- AUTHORS.txt | 1 + CHANGELOG.rst | 3 +++ parso/python/errors.py | 12 ++++++++++++ test/failing_examples.py | 2 ++ test/test_python_errors.py | 6 ++++++ 5 files changed, 24 insertions(+) diff --git a/AUTHORS.txt b/AUTHORS.txt index 9737530..9853dff 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -53,6 +53,7 @@ Simon Ruggier (@sruggier) Tim Gates (@timgates42) Batuhan Taskaya (@isidentical) Jocelyn Boullier (@Kazy) +Apoorv Darshan (@apoorvdarshan) Note: (@user) means a github user name. diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a4811fb..811e095 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,9 @@ Changelog Unreleased ++++++++++ +- Fix a false ``SyntaxError`` for named expressions used as positional + arguments in a call, e.g. ``f(a := 1, b)`` + 0.8.7 (2026-05-02) ++++++++++++++++++ diff --git a/parso/python/errors.py b/parso/python/errors.py index 09c5047..79945b2 100644 --- a/parso/python/errors.py +++ b/parso/python/errors.py @@ -933,6 +933,18 @@ def is_issue(self, node): self.add_issue(argument, message=message) else: kw_unpacking_only = True + elif argument.children[1] == ':=': + # f(a := 1) is a named expression, i.e. a positional + # argument, not a keyword argument. + if kw_unpacking_only: + # f(**x, a := 1) + message = "positional argument follows " \ + "keyword argument unpacking" + self.add_issue(argument, message=message) + elif kw_only: + # f(x=2, a := 1) + message = "positional argument follows keyword argument" + self.add_issue(argument, message=message) else: # Is a keyword argument. kw_only = True if first.type == 'name': diff --git a/test/failing_examples.py b/test/failing_examples.py index a555fde..1e1e745 100644 --- a/test/failing_examples.py +++ b/test/failing_examples.py @@ -49,6 +49,8 @@ def build_nested(code, depth, base='def f():\n'): 'f(x=1, x=2)', 'f(**x, y)', 'f(x=2, y)', + 'f(x=2, a := 1)', + 'f(**x, a := 1)', 'f(**x, *y)', 'f(**x, y=3, z)', # augassign diff --git a/test/test_python_errors.py b/test/test_python_errors.py index 0c89016..11ec97a 100644 --- a/test/test_python_errors.py +++ b/test/test_python_errors.py @@ -362,6 +362,12 @@ def test_valid_fstrings(code): 'a[(b:=0)]', 'a[(b:=0, c:=0)]', 'a[(b:=0):1:2]', + 'f(a := 1, b)', + 'f(a := 1, b=2)', + 'f(a := 1, *b)', + 'f(a := 1, **b)', + 'f(b, a := 1)', + 'f(*b, a := 1)', ] ) def test_valid_namedexpr(code):