diff --git a/crates/ruff_linter/resources/test/fixtures/pylint/await_outside_async.ipynb b/crates/ruff_linter/resources/test/fixtures/pylint/await_outside_async.ipynb new file mode 100644 index 0000000000..a94aa9fa2c --- /dev/null +++ b/crates/ruff_linter/resources/test/fixtures/pylint/await_outside_async.ipynb @@ -0,0 +1,42 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [ + "import asyncio\n", + "\n", + "await asyncio.sleep(1) # This is okay\n", + "\n", + "if True:\n", + " await asyncio.sleep(1) # This is okay\n", + "\n", + "def foo():\n", + " await asyncio.sleep(1) # # [await-outside-async]" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/crates/ruff_linter/src/rules/pylint/mod.rs b/crates/ruff_linter/src/rules/pylint/mod.rs index e4fec38d45..09392c125d 100644 --- a/crates/ruff_linter/src/rules/pylint/mod.rs +++ b/crates/ruff_linter/src/rules/pylint/mod.rs @@ -27,6 +27,7 @@ mod tests { )] #[test_case(Rule::AssertOnStringLiteral, Path::new("assert_on_string_literal.py"))] #[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.py"))] + #[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.ipynb"))] #[test_case(Rule::BadOpenMode, Path::new("bad_open_mode.py"))] #[test_case( Rule::BadStringFormatCharacter, diff --git a/crates/ruff_linter/src/rules/pylint/rules/await_outside_async.rs b/crates/ruff_linter/src/rules/pylint/rules/await_outside_async.rs index db248c1911..ecf82e7fff 100644 --- a/crates/ruff_linter/src/rules/pylint/rules/await_outside_async.rs +++ b/crates/ruff_linter/src/rules/pylint/rules/await_outside_async.rs @@ -11,6 +11,9 @@ use crate::checkers::ast::Checker; /// ## Why is this bad? /// Using `await` outside an `async` function is a syntax error. /// +/// As an exception, `await` is allowed at the top level of a Jupyter notebook +/// (see: [autoawait]). +/// /// ## Example /// ```python /// import asyncio @@ -32,6 +35,8 @@ use crate::checkers::ast::Checker; /// ## References /// - [Python documentation: Await expression](https://docs.python.org/3/reference/expressions.html#await) /// - [PEP 492: Await Expression](https://peps.python.org/pep-0492/#await-expression) +/// +/// [autoawait]: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html #[violation] pub struct AwaitOutsideAsync; @@ -49,6 +54,12 @@ pub(crate) fn await_outside_async(checker: &mut Checker, node: T) { return; } + // `await` is allowed at the top level of a Jupyter notebook. + // See: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html. + if checker.semantic().current_scope().kind.is_module() && checker.source_type.is_ipynb() { + return; + } + // Generators are evaluated lazily, so you can use `await` in them. For example: // ```python // # This is valid diff --git a/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1142_await_outside_async.ipynb.snap b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1142_await_outside_async.ipynb.snap new file mode 100644 index 0000000000..7536eac4d5 --- /dev/null +++ b/crates/ruff_linter/src/rules/pylint/snapshots/ruff_linter__rules__pylint__tests__PLE1142_await_outside_async.ipynb.snap @@ -0,0 +1,11 @@ +--- +source: crates/ruff_linter/src/rules/pylint/mod.rs +assertion_line: 236 +snapshot_kind: text +--- +await_outside_async.ipynb:9:5: PLE1142 `await` should be used within an async function + | +8 | def foo(): +9 | await asyncio.sleep(1) # # [await-outside-async] + | ^^^^^^^^^^^^^^^^^^^^^^ PLE1142 + |