Skip to content
Closed

Fix 868 #1282

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
30 changes: 23 additions & 7 deletions pycodestyle.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
700 statements
900 syntax error
"""
import ast
Copy link
Member

@asottile asottile May 23, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unfortunately one of the design principles of pycodestyle is that it does not use the ast to do its linting (this allows it to be ~somewhat cross-version compatible via the tokenizer)

so while this would work -- it can't be merged :(

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I suggest documenting that in CONTRIBUTING.rst?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you consider a PR that attempts ast and if there's any issue, falls back to the current method?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no I would not, that's why I closed this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and it's already in the docs:

Using the ast module defeats that purpose

import bisect
import configparser
import inspect
Expand Down Expand Up @@ -1193,6 +1194,25 @@ def is_string_literal(line):
checker_state['seen_non_imports'] = True


def _use_ast_to_check_for_lambda_assignment(logical_line):
"""Check if the line contains a lambda assignment."""
if not logical_line:
return False
try:
tree = ast.parse(logical_line)
except (SyntaxError, ValueError):
return False
for node in ast.walk(tree):
if isinstance(node, ast.Assign):
for target in node.targets:
if (
isinstance(target, ast.Name) and # assigning to a variable
isinstance(node.value, ast.Lambda) # lambda expression
):
return True
return False


@register_check
def compound_statements(logical_line):
r"""Compound statements (on the same line) are generally
Expand Down Expand Up @@ -1228,6 +1248,9 @@ def compound_statements(logical_line):
found = line.find(':')
prev_found = 0
counts = {char: 0 for char in '{}[]()'}
if _use_ast_to_check_for_lambda_assignment(line):
yield 0, "E731 do not assign a lambda expression, use a def"
return
while -1 < found < last_char:
update_counts(line[prev_found:found], counts)
if (
Expand All @@ -1236,13 +1259,6 @@ def compound_statements(logical_line):
counts['('] <= counts[')'] and # (annotation)
line[found + 1] != '=' # assignment expression
):
lambda_kw = LAMBDA_REGEX.search(line, 0, found)
if lambda_kw:
before = line[:lambda_kw.start()].rstrip()
if before[-1:] == '=' and before[:-1].strip().isidentifier():
yield 0, ("E731 do not assign a lambda expression, use a "
"def")
break
if STARTSWITH_DEF_REGEX.match(line):
yield 0, "E704 multiple statements on one line (def)"
elif STARTSWITH_INDENT_STATEMENT_REGEX.match(line):
Expand Down
11 changes: 11 additions & 0 deletions testing/data/E73.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
#: E731:1:1
f = lambda x: 2 * x
#: E731:1:1
f = ( # some long comment that forces this to be on a new line
lambda x: 731 * x
)
#: E731:1:1 E226:1:16
f = lambda x: 2*x
#: E731:2:5
Expand All @@ -9,6 +13,13 @@
f = object()
f.method = lambda: 'Method'

f = (lambda o: o.lower()) if isinstance('a', str) else (lambda o: o)

f = (
lambda o: o.lower(),
lambda o: o.upper(),
)

f = {}
f['a'] = lambda x: x ** 2

Expand Down