Skip to content

Commit 71da1d6

Browse files
authored
Fix await-outside-async to allow await at the top-level scope of a notebook (#14225)
## Summary <!-- What's the purpose of the change? What does it do, and why? --> Fix `await-outside-async` to allow `await` at the top-level scope of a notebook. ```python # foo.ipynb await asyncio.sleep(1) # should be allowed ``` ## Test Plan <!-- How was it tested? --> A unit test
1 parent e598240 commit 71da1d6

4 files changed

Lines changed: 65 additions & 0 deletions

File tree

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": null,
6+
"metadata": {},
7+
"outputs": [],
8+
"source": [
9+
"import asyncio\n",
10+
"\n",
11+
"await asyncio.sleep(1) # This is okay\n",
12+
"\n",
13+
"if True:\n",
14+
" await asyncio.sleep(1) # This is okay\n",
15+
"\n",
16+
"def foo():\n",
17+
" await asyncio.sleep(1) # # [await-outside-async]"
18+
]
19+
}
20+
],
21+
"metadata": {
22+
"kernelspec": {
23+
"display_name": "base",
24+
"language": "python",
25+
"name": "python3"
26+
},
27+
"language_info": {
28+
"codemirror_mode": {
29+
"name": "ipython",
30+
"version": 3
31+
},
32+
"file_extension": ".py",
33+
"mimetype": "text/x-python",
34+
"name": "python",
35+
"nbconvert_exporter": "python",
36+
"pygments_lexer": "ipython3",
37+
"version": "3.8.5"
38+
}
39+
},
40+
"nbformat": 4,
41+
"nbformat_minor": 2
42+
}

crates/ruff_linter/src/rules/pylint/mod.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ mod tests {
2727
)]
2828
#[test_case(Rule::AssertOnStringLiteral, Path::new("assert_on_string_literal.py"))]
2929
#[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.py"))]
30+
#[test_case(Rule::AwaitOutsideAsync, Path::new("await_outside_async.ipynb"))]
3031
#[test_case(Rule::BadOpenMode, Path::new("bad_open_mode.py"))]
3132
#[test_case(
3233
Rule::BadStringFormatCharacter,

crates/ruff_linter/src/rules/pylint/rules/await_outside_async.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ use crate::checkers::ast::Checker;
1111
/// ## Why is this bad?
1212
/// Using `await` outside an `async` function is a syntax error.
1313
///
14+
/// As an exception, `await` is allowed at the top level of a Jupyter notebook
15+
/// (see: [autoawait]).
16+
///
1417
/// ## Example
1518
/// ```python
1619
/// import asyncio
@@ -32,6 +35,8 @@ use crate::checkers::ast::Checker;
3235
/// ## References
3336
/// - [Python documentation: Await expression](https://docs.python.org/3/reference/expressions.html#await)
3437
/// - [PEP 492: Await Expression](https://peps.python.org/pep-0492/#await-expression)
38+
///
39+
/// [autoawait]: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html
3540
#[violation]
3641
pub struct AwaitOutsideAsync;
3742

@@ -49,6 +54,12 @@ pub(crate) fn await_outside_async<T: Ranged>(checker: &mut Checker, node: T) {
4954
return;
5055
}
5156

57+
// `await` is allowed at the top level of a Jupyter notebook.
58+
// See: https://ipython.readthedocs.io/en/stable/interactive/autoawait.html.
59+
if checker.semantic().current_scope().kind.is_module() && checker.source_type.is_ipynb() {
60+
return;
61+
}
62+
5263
// Generators are evaluated lazily, so you can use `await` in them. For example:
5364
// ```python
5465
// # This is valid
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
---
2+
source: crates/ruff_linter/src/rules/pylint/mod.rs
3+
assertion_line: 236
4+
snapshot_kind: text
5+
---
6+
await_outside_async.ipynb:9:5: PLE1142 `await` should be used within an async function
7+
|
8+
8 | def foo():
9+
9 | await asyncio.sleep(1) # # [await-outside-async]
10+
| ^^^^^^^^^^^^^^^^^^^^^^ PLE1142
11+
|

0 commit comments

Comments
 (0)