Skip to content

Commit

Permalink
Allow no whitespace between lambda keyword and params in certain cases
Browse files Browse the repository at this point in the history
Python accepts code where `lambda` follows a `*`, so this PR relaxes validation rules for Lambdas.

Raised in #930.
  • Loading branch information
zsol committed May 27, 2023
1 parent b230302 commit 744d6fd
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 24 deletions.
20 changes: 20 additions & 0 deletions libcst/_nodes/expression.py
Original file line number Diff line number Diff line change
Expand Up @@ -1983,6 +1983,25 @@ def _visit_and_replace_children(self, visitor: CSTVisitorT) -> "Parameters":
star_kwarg=visit_optional(self, "star_kwarg", self.star_kwarg, visitor),
)

def _safe_to_join_with_lambda(self) -> bool:
"""
Determine if Parameters need a space after the `lambda` keyword. Returns True
iff it's safe to omit the space between `lambda` and these Parameters.
See also `BaseExpression._safe_to_use_with_word_operator`.
For example: `lambda*_: pass`
"""
if len(self.posonly_params) != 0:
return False

# posonly_ind can't appear if above condition is false

if len(self.params) > 0 and self.params[0].star not in {"*", "**"}:
return False

return True

def _codegen_impl(self, state: CodegenState) -> None: # noqa: C901
# Compute the star existence first so we can ask about whether
# each element is the last in the list or not.
Expand Down Expand Up @@ -2115,6 +2134,7 @@ def _validate(self) -> None:
if (
isinstance(whitespace_after_lambda, BaseParenthesizableWhitespace)
and whitespace_after_lambda.empty
and not self.params._safe_to_join_with_lambda()
):
raise CSTValidationError(
"Must have at least one space after lambda when specifying params"
Expand Down
56 changes: 32 additions & 24 deletions libcst/_nodes/tests/test_lambda.py
Original file line number Diff line number Diff line change
Expand Up @@ -303,30 +303,6 @@ def test_valid(
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(star_arg=cst.Param(cst.Name("arg"))),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(kwonly_params=(cst.Param(cst.Name("arg")),)),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(star_kwarg=cst.Param(cst.Name("arg"))),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"at least one space after lambda",
),
(
lambda: cst.Lambda(
cst.Parameters(
Expand Down Expand Up @@ -944,6 +920,38 @@ class LambdaParserTest(CSTNodeTest):
),
"( lambda : 5 )",
),
# No space between lambda and params
(
cst.Lambda(
cst.Parameters(star_arg=cst.Param(cst.Name("args"), star="*")),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"lambda*args: 5",
),
(
cst.Lambda(
cst.Parameters(star_kwarg=cst.Param(cst.Name("kwargs"), star="**")),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"lambda**kwargs: 5",
),
(
cst.Lambda(
cst.Parameters(
star_arg=cst.ParamStar(
comma=cst.Comma(
cst.SimpleWhitespace(""), cst.SimpleWhitespace("")
)
),
kwonly_params=[cst.Param(cst.Name("args"), star="")],
),
cst.Integer("5"),
whitespace_after_lambda=cst.SimpleWhitespace(""),
),
"lambda*,args: 5",
),
)
)
def test_valid(
Expand Down

0 comments on commit 744d6fd

Please sign in to comment.