Skip to content

Conversation

@ntBre
Copy link
Contributor

@ntBre ntBre commented Apr 18, 2025

Summary

While adding semantic error support to red-knot, I noticed duplicate diagnostics for code like this:

# error: [invalid-syntax] "cannot use an asynchronous comprehension outside of an asynchronous function on Python 3.9 (syntax was added in 3.11)"
# error: [invalid-syntax] "`asynchronous comprehension` outside of an asynchronous function"
 [reveal_type(x) async for x in AsyncIterable()]

Beyond the duplication, the first error message doesn't make much sense because this syntax is not allowed on Python 3.11 either.

To fix this, this PR renames the async-comprehension-outside-async-function semantic syntax error to async-comprehension-in-sync-comprehension and fixes the rule to avoid applying outside of sync comprehensions at all.

Test Plan

New linter test demonstrating the false positive. The mdtests I'm updating in my red-knot PR will also reflect this change eventually.

ntBre added 3 commits April 18, 2025 09:54
This case _is_ an error (async comprehension outside async function), but it's
not an async comprehension in a sync comprehension, which is all this rule is
supposed to catch. The error message stating that this is allowed after Python
3.11 is particularly nonsensical
@ntBre ntBre added the bug Something isn't working label Apr 18, 2025
@github-actions
Copy link
Contributor

github-actions bot commented Apr 18, 2025

ruff-ecosystem results

Linter (stable)

✅ ecosystem check detected no linter changes.

Linter (preview)

✅ ecosystem check detected no linter changes.

Formatter (stable)

✅ ecosystem check detected no format changes.

Formatter (preview)

✅ ecosystem check detected no format changes.

@ntBre ntBre marked this pull request as ready for review April 18, 2025 14:23
@dhruvmanila
Copy link
Member

dhruvmanila commented Apr 21, 2025

I think the error message is a bit misleading. It suggests that an asynchronous comprehension can be used in a synchronous comprehension from Python 3.11 but that's not actually true.

[[x async for x in foo(n)] for n in range(3)]

The following raises error for both <3.11 and 3.11+

The user actually need to include it in an async function for it to be considered valid:

async def _():
	[[x async for x in foo(n)] for n in range(3)]

I'm running this via python -m compileall.

I might be misunderstanding what's being changed here so feel free to correct me.

@ntBre
Copy link
Contributor Author

ntBre commented Apr 21, 2025

I see what you mean, but that case will lead to a separate AwaitOutsideAsyncFunction error, as shown in the PR summary. Do you think the error message for this variant also needs to be extended to mention the function, or will the second error clarify things enough?

If it needs to be extended, do you have any suggestions? I was afraid the message was quite long already.

@dhruvmanila
Copy link
Member

# error: [invalid-syntax] "`asynchronous comprehension` outside of an asynchronous function"

Is this the error message that's removed in this PR?

Copy link
Member

@dhruvmanila dhruvmanila left a comment

Choose a reason for hiding this comment

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

Testing this out locally for a few snippets, it seems good but I think we can still improve this. I'm not sure how but multi-span diagnostics is one way to go to provide additional context.

I don't have a good suggestion for improving the error message, maybe something like:

SyntaxError: cannot use an asynchronous comprehension inside of a synchronous scope created by the outer comprehension on Python 3.9 (syntax was added in 3.11)

Maybe @AlexWaygood has a better recommendation?

@ntBre
Copy link
Contributor Author

ntBre commented Apr 22, 2025

No, the top one (cannot use an asynchronous comprehension outside of an asynchronous function on Python 3.9 (syntax was added in 3.11)) is removed in this PR. The second message (asynchronous comprehension outside of an asynchronous function) is a separate SemanticSyntaxError entirely and will still be emitted in this example.

I don't think I explained this very well in the PR summary, looking back at it now. I'm doing two unrelated things in this PR:

  • removing the false positive for async comprehensions not in sync comprehensions (a bug in the implementation)
  • updating the naming of the variant and error message to better reflect the error

I only added a new test for the first of these, which probably made it even less clear what was changing in the code. Sorry!

@ntBre
Copy link
Contributor Author

ntBre commented Apr 24, 2025

I'm resolving the merge conflicts now. Just as one additional data point, the CPython error message for this is also quite poor:

>>> async def f(): [[x async for x in []] for x in []]
...
  File "<stdin>", line 1
SyntaxError: asynchronous comprehension outside of an asynchronous function

That's not an excuse for ours to be bad too, but I do think

SyntaxError: cannot use an asynchronous comprehension inside of a synchronous comprehension

is already a pretty good improvement over the current message in ruff and over Python's message.

I personally prefer this to the suggestion above too, but I don't feel strongly about it. If nobody else feels strongly, I'll just merge this for now, and we can tweak the message later.

@github-actions
Copy link
Contributor

mypy_primer results

No ecosystem changes detected ✅

@ntBre ntBre merged commit 92ecfc9 into main Apr 24, 2025
34 checks passed
@ntBre ntBre deleted the brent/fix-async-comprehension-in-sync-comprehension branch April 24, 2025 19:45
dcreager added a commit that referenced this pull request Apr 25, 2025
* main: (24 commits)
  [`airflow`] Extend `AIR301` rule (#17598)
  [`airflow`] update existing `AIR302` rules with better suggestions (#17542)
  red_knot_project: sort diagnostics from checking files
  [red-knot] fix detecting a metaclass on a not-explicitly-specialized generic base (#17621)
  [red-knot] fix inheritance-cycle detection for generic classes (#17620)
  [`pylint`] Detect `global` declarations in module scope (`PLE0118`) (#17411)
  Add Semantic Error Test for LateFutureImport (#17612)
  [red-knot] change TypeVarInstance to be interned, not tracked (#17616)
  [red-knot] Special case `@final`, `@override` (#17608)
  [red-knot] add TODO comment in specialization code (#17615)
  [`semantic-syntax-errors`] test for `LoadBeforeGlobalDeclaration` - ruff linter (#17592)
  [syntax-errors] `nonlocal` declaration at module level (#17559)
  [`airflow`] Apply auto fix to cases where name has been changed in Airflow 3 (`AIR311`) (#17571)
  [syntax-errors] Make `async-comprehension-in-sync-comprehension` more specific (#17460)
  Bump 0.11.7 (#17613)
  [red-knot] Use iterative approach to collect overloads (#17607)
  red_knot_python_semantic: avoid Rust's screaming snake case convention in mdtest
  red_knot_python_semantic: improve diagnostics for unsupported boolean conversions
  red_knot_python_semantic: add "return type span" helper method
  red_knot_python_semantic: move parameter span helper method
  ...
dcreager added a commit that referenced this pull request Apr 26, 2025
* main: (27 commits)
  [red-knot] Add new property tests for subtyping with "bottom" callable (#17635)
  [red-knot] Create generic context for generic classes lazily (#17617)
  ruff_db: add tests for annotations with no ranges
  [`airflow`] Extend `AIR301` rule (#17598)
  [`airflow`] update existing `AIR302` rules with better suggestions (#17542)
  red_knot_project: sort diagnostics from checking files
  [red-knot] fix detecting a metaclass on a not-explicitly-specialized generic base (#17621)
  [red-knot] fix inheritance-cycle detection for generic classes (#17620)
  [`pylint`] Detect `global` declarations in module scope (`PLE0118`) (#17411)
  Add Semantic Error Test for LateFutureImport (#17612)
  [red-knot] change TypeVarInstance to be interned, not tracked (#17616)
  [red-knot] Special case `@final`, `@override` (#17608)
  [red-knot] add TODO comment in specialization code (#17615)
  [`semantic-syntax-errors`] test for `LoadBeforeGlobalDeclaration` - ruff linter (#17592)
  [syntax-errors] `nonlocal` declaration at module level (#17559)
  [`airflow`] Apply auto fix to cases where name has been changed in Airflow 3 (`AIR311`) (#17571)
  [syntax-errors] Make `async-comprehension-in-sync-comprehension` more specific (#17460)
  Bump 0.11.7 (#17613)
  [red-knot] Use iterative approach to collect overloads (#17607)
  red_knot_python_semantic: avoid Rust's screaming snake case convention in mdtest
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants