mirror of
https://github.com/astral-sh/ruff.git
synced 2025-09-26 11:59:35 +00:00
[syntax-errors] Parenthesized context managers before Python 3.9 (#16523)
Summary -- I thought this was very complicated based on the comment here: https://github.com/astral-sh/ruff/pull/16106#issuecomment-2653505671 and on some of the discussion in the CPython issue here: https://github.com/python/cpython/issues/56991. However, after a little bit of experimentation, I think it boils down to this example: ```python with (x as y): ... ``` The issue is parentheses around a `with` item with an `optional_var`, as we (and [Python](https://docs.python.org/3/library/ast.html#ast.withitem)) call the trailing variable name (`y` in this case). It's not actually about line breaks after all, except that line breaks are allowed in parenthesized expressions, which explains the validity of cases like ```pycon >>> with ( ... x, ... y ... ) as foo: ... pass ... ``` even on Python 3.8. I followed [pyright]'s example again here on the diagnostic range (just the opening paren) and the wording of the error. Test Plan -- Inline tests [pyright]: https://pyright-play.net/?pythonVersion=3.7&strict=true&code=FAdwlgLgFgBAFAewA4FMB2cBEAzBCB0EAHhJgJQwCGAzjLgmQFwz6tA
This commit is contained in:
parent
8d3643f409
commit
75a562d313
10 changed files with 800 additions and 0 deletions
|
@ -0,0 +1,4 @@
|
||||||
|
# parse_options: {"target-version": "3.8"}
|
||||||
|
with (foo as x, bar as y): ...
|
||||||
|
with (foo, bar as y): ...
|
||||||
|
with (foo as x, bar): ...
|
|
@ -0,0 +1,13 @@
|
||||||
|
# parse_options: {"target-version": "3.8"}
|
||||||
|
# these cases are _syntactically_ valid before Python 3.9 because the `with` item
|
||||||
|
# is parsed as a tuple, but this will always cause a runtime error, so we flag it
|
||||||
|
# anyway
|
||||||
|
with (foo, bar): ...
|
||||||
|
with (
|
||||||
|
open('foo.txt')) as foo: ...
|
||||||
|
with (
|
||||||
|
foo,
|
||||||
|
bar,
|
||||||
|
baz,
|
||||||
|
): ...
|
||||||
|
with (foo,): ...
|
|
@ -0,0 +1,4 @@
|
||||||
|
# parse_options: {"target-version": "3.9"}
|
||||||
|
with (foo as x, bar as y): ...
|
||||||
|
with (foo, bar as y): ...
|
||||||
|
with (foo as x, bar): ...
|
|
@ -0,0 +1,6 @@
|
||||||
|
# parse_options: {"target-version": "3.8"}
|
||||||
|
with (
|
||||||
|
foo,
|
||||||
|
bar,
|
||||||
|
baz,
|
||||||
|
) as tup: ...
|
|
@ -661,6 +661,46 @@ pub enum UnsupportedSyntaxErrorKind {
|
||||||
TypeAliasStatement,
|
TypeAliasStatement,
|
||||||
TypeParamDefault,
|
TypeParamDefault,
|
||||||
|
|
||||||
|
/// Represents the use of a parenthesized `with` item before Python 3.9.
|
||||||
|
///
|
||||||
|
/// ## Examples
|
||||||
|
///
|
||||||
|
/// As described in [BPO 12782], `with` uses like this were not allowed on Python 3.8:
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// with (open("a_really_long_foo") as foo,
|
||||||
|
/// open("a_really_long_bar") as bar):
|
||||||
|
/// pass
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// because parentheses were not allowed within the `with` statement itself (see [this comment]
|
||||||
|
/// in particular). However, parenthesized expressions were still allowed, including the cases
|
||||||
|
/// below, so the issue can be pretty subtle and relates specifically to parenthesized items
|
||||||
|
/// with `as` bindings.
|
||||||
|
///
|
||||||
|
/// ```python
|
||||||
|
/// with (foo, bar): ... # okay
|
||||||
|
/// with (
|
||||||
|
/// open('foo.txt')) as foo: ... # also okay
|
||||||
|
/// with (
|
||||||
|
/// foo,
|
||||||
|
/// bar,
|
||||||
|
/// baz,
|
||||||
|
/// ): ... # also okay, just a tuple
|
||||||
|
/// with (
|
||||||
|
/// foo,
|
||||||
|
/// bar,
|
||||||
|
/// baz,
|
||||||
|
/// ) as tup: ... # also okay, binding the tuple
|
||||||
|
/// ```
|
||||||
|
///
|
||||||
|
/// This restriction was lifted in 3.9 but formally included in the [release notes] for 3.10.
|
||||||
|
///
|
||||||
|
/// [BPO 12782]: https://github.com/python/cpython/issues/56991
|
||||||
|
/// [this comment]: https://github.com/python/cpython/issues/56991#issuecomment-1093555141
|
||||||
|
/// [release notes]: https://docs.python.org/3/whatsnew/3.10.html#summary-release-highlights
|
||||||
|
ParenthesizedContextManager,
|
||||||
|
|
||||||
/// Represents the use of a [PEP 646] star expression in an index.
|
/// Represents the use of a [PEP 646] star expression in an index.
|
||||||
///
|
///
|
||||||
/// ## Examples
|
/// ## Examples
|
||||||
|
@ -798,6 +838,9 @@ impl Display for UnsupportedSyntaxError {
|
||||||
UnsupportedSyntaxErrorKind::TypeParamDefault => {
|
UnsupportedSyntaxErrorKind::TypeParamDefault => {
|
||||||
"Cannot set default type for a type parameter"
|
"Cannot set default type for a type parameter"
|
||||||
}
|
}
|
||||||
|
UnsupportedSyntaxErrorKind::ParenthesizedContextManager => {
|
||||||
|
"Cannot use parentheses within a `with` statement"
|
||||||
|
}
|
||||||
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
|
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
|
||||||
"Cannot use star expression in index"
|
"Cannot use star expression in index"
|
||||||
}
|
}
|
||||||
|
@ -861,6 +904,9 @@ impl UnsupportedSyntaxErrorKind {
|
||||||
UnsupportedSyntaxErrorKind::TypeParameterList => Change::Added(PythonVersion::PY312),
|
UnsupportedSyntaxErrorKind::TypeParameterList => Change::Added(PythonVersion::PY312),
|
||||||
UnsupportedSyntaxErrorKind::TypeAliasStatement => Change::Added(PythonVersion::PY312),
|
UnsupportedSyntaxErrorKind::TypeAliasStatement => Change::Added(PythonVersion::PY312),
|
||||||
UnsupportedSyntaxErrorKind::TypeParamDefault => Change::Added(PythonVersion::PY313),
|
UnsupportedSyntaxErrorKind::TypeParamDefault => Change::Added(PythonVersion::PY313),
|
||||||
|
UnsupportedSyntaxErrorKind::ParenthesizedContextManager => {
|
||||||
|
Change::Added(PythonVersion::PY39)
|
||||||
|
}
|
||||||
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
|
UnsupportedSyntaxErrorKind::StarExpressionInIndex => {
|
||||||
Change::Added(PythonVersion::PY311)
|
Change::Added(PythonVersion::PY311)
|
||||||
}
|
}
|
||||||
|
|
|
@ -2066,8 +2066,49 @@ impl<'src> Parser<'src> {
|
||||||
return vec![];
|
return vec![];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let open_paren_range = self.current_token_range();
|
||||||
|
|
||||||
if self.at(TokenKind::Lpar) {
|
if self.at(TokenKind::Lpar) {
|
||||||
if let Some(items) = self.try_parse_parenthesized_with_items() {
|
if let Some(items) = self.try_parse_parenthesized_with_items() {
|
||||||
|
// test_ok tuple_context_manager_py38
|
||||||
|
// # parse_options: {"target-version": "3.8"}
|
||||||
|
// with (
|
||||||
|
// foo,
|
||||||
|
// bar,
|
||||||
|
// baz,
|
||||||
|
// ) as tup: ...
|
||||||
|
|
||||||
|
// test_err tuple_context_manager_py38
|
||||||
|
// # parse_options: {"target-version": "3.8"}
|
||||||
|
// # these cases are _syntactically_ valid before Python 3.9 because the `with` item
|
||||||
|
// # is parsed as a tuple, but this will always cause a runtime error, so we flag it
|
||||||
|
// # anyway
|
||||||
|
// with (foo, bar): ...
|
||||||
|
// with (
|
||||||
|
// open('foo.txt')) as foo: ...
|
||||||
|
// with (
|
||||||
|
// foo,
|
||||||
|
// bar,
|
||||||
|
// baz,
|
||||||
|
// ): ...
|
||||||
|
// with (foo,): ...
|
||||||
|
|
||||||
|
// test_ok parenthesized_context_manager_py39
|
||||||
|
// # parse_options: {"target-version": "3.9"}
|
||||||
|
// with (foo as x, bar as y): ...
|
||||||
|
// with (foo, bar as y): ...
|
||||||
|
// with (foo as x, bar): ...
|
||||||
|
|
||||||
|
// test_err parenthesized_context_manager_py38
|
||||||
|
// # parse_options: {"target-version": "3.8"}
|
||||||
|
// with (foo as x, bar as y): ...
|
||||||
|
// with (foo, bar as y): ...
|
||||||
|
// with (foo as x, bar): ...
|
||||||
|
self.add_unsupported_syntax_error(
|
||||||
|
UnsupportedSyntaxErrorKind::ParenthesizedContextManager,
|
||||||
|
open_paren_range,
|
||||||
|
);
|
||||||
|
|
||||||
self.expect(TokenKind::Rpar);
|
self.expect(TokenKind::Rpar);
|
||||||
items
|
items
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/err/parenthesized_context_manager_py38.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..126,
|
||||||
|
body: [
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 43..73,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 49..57,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 49..52,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 56..57,
|
||||||
|
id: Name("x"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 59..67,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 59..62,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 66..67,
|
||||||
|
id: Name("y"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 70..73,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 70..73,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 74..99,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 80..83,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 80..83,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 85..93,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 85..88,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 92..93,
|
||||||
|
id: Name("y"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 96..99,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 96..99,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 100..125,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 106..114,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 106..109,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 113..114,
|
||||||
|
id: Name("x"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 116..119,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 116..119,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 122..125,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 122..125,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
## Unsupported Syntax Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # parse_options: {"target-version": "3.8"}
|
||||||
|
2 | with (foo as x, bar as y): ...
|
||||||
|
| ^ Syntax Error: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
|
||||||
|
3 | with (foo, bar as y): ...
|
||||||
|
4 | with (foo as x, bar): ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
1 | # parse_options: {"target-version": "3.8"}
|
||||||
|
2 | with (foo as x, bar as y): ...
|
||||||
|
3 | with (foo, bar as y): ...
|
||||||
|
| ^ Syntax Error: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
|
||||||
|
4 | with (foo as x, bar): ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
2 | with (foo as x, bar as y): ...
|
||||||
|
3 | with (foo, bar as y): ...
|
||||||
|
4 | with (foo as x, bar): ...
|
||||||
|
| ^ Syntax Error: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
|
||||||
|
|
|
|
@ -0,0 +1,237 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/err/tuple_context_manager_py38.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..327,
|
||||||
|
body: [
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 216..236,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 222..225,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 222..225,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 227..230,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 227..230,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 233..236,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 233..236,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 237..274,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 242..269,
|
||||||
|
context_expr: Call(
|
||||||
|
ExprCall {
|
||||||
|
range: 246..261,
|
||||||
|
func: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 246..250,
|
||||||
|
id: Name("open"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
arguments: Arguments {
|
||||||
|
range: 250..261,
|
||||||
|
args: [
|
||||||
|
StringLiteral(
|
||||||
|
ExprStringLiteral {
|
||||||
|
range: 251..260,
|
||||||
|
value: StringLiteralValue {
|
||||||
|
inner: Single(
|
||||||
|
StringLiteral {
|
||||||
|
range: 251..260,
|
||||||
|
value: "foo.txt",
|
||||||
|
flags: StringLiteralFlags {
|
||||||
|
quote_style: Single,
|
||||||
|
prefix: Empty,
|
||||||
|
triple_quoted: false,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
keywords: [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 266..269,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 271..274,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 271..274,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 275..309,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 284..287,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 284..287,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 291..294,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 291..294,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 298..301,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 298..301,
|
||||||
|
id: Name("baz"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 306..309,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 306..309,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 310..326,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 316..319,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 316..319,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 323..326,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 323..326,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
||||||
|
## Unsupported Syntax Errors
|
||||||
|
|
||||||
|
|
|
||||||
|
3 | # is parsed as a tuple, but this will always cause a runtime error, so we flag it
|
||||||
|
4 | # anyway
|
||||||
|
5 | with (foo, bar): ...
|
||||||
|
| ^ Syntax Error: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
|
||||||
|
6 | with (
|
||||||
|
7 | open('foo.txt')) as foo: ...
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
6 | with (
|
||||||
|
7 | open('foo.txt')) as foo: ...
|
||||||
|
8 | with (
|
||||||
|
| ^ Syntax Error: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
|
||||||
|
9 | foo,
|
||||||
|
10 | bar,
|
||||||
|
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
11 | baz,
|
||||||
|
12 | ): ...
|
||||||
|
13 | with (foo,): ...
|
||||||
|
| ^ Syntax Error: Cannot use parentheses within a `with` statement on Python 3.8 (syntax was added in Python 3.9)
|
||||||
|
|
|
|
@ -0,0 +1,173 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/ok/parenthesized_context_manager_py39.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..126,
|
||||||
|
body: [
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 43..73,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 49..57,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 49..52,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 56..57,
|
||||||
|
id: Name("x"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 59..67,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 59..62,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 66..67,
|
||||||
|
id: Name("y"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 70..73,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 70..73,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 74..99,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 80..83,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 80..83,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 85..93,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 85..88,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 92..93,
|
||||||
|
id: Name("y"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 96..99,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 96..99,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 100..125,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 106..114,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 106..109,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 113..114,
|
||||||
|
id: Name("x"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
WithItem {
|
||||||
|
range: 116..119,
|
||||||
|
context_expr: Name(
|
||||||
|
ExprName {
|
||||||
|
range: 116..119,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: None,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 122..125,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 122..125,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
|
@ -0,0 +1,77 @@
|
||||||
|
---
|
||||||
|
source: crates/ruff_python_parser/tests/fixtures.rs
|
||||||
|
input_file: crates/ruff_python_parser/resources/inline/ok/tuple_context_manager_py38.py
|
||||||
|
---
|
||||||
|
## AST
|
||||||
|
|
||||||
|
```
|
||||||
|
Module(
|
||||||
|
ModModule {
|
||||||
|
range: 0..85,
|
||||||
|
body: [
|
||||||
|
With(
|
||||||
|
StmtWith {
|
||||||
|
range: 43..84,
|
||||||
|
is_async: false,
|
||||||
|
items: [
|
||||||
|
WithItem {
|
||||||
|
range: 48..79,
|
||||||
|
context_expr: Tuple(
|
||||||
|
ExprTuple {
|
||||||
|
range: 48..72,
|
||||||
|
elts: [
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 52..55,
|
||||||
|
id: Name("foo"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 59..62,
|
||||||
|
id: Name("bar"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 66..69,
|
||||||
|
id: Name("baz"),
|
||||||
|
ctx: Load,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
ctx: Load,
|
||||||
|
parenthesized: true,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
optional_vars: Some(
|
||||||
|
Name(
|
||||||
|
ExprName {
|
||||||
|
range: 76..79,
|
||||||
|
id: Name("tup"),
|
||||||
|
ctx: Store,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
body: [
|
||||||
|
Expr(
|
||||||
|
StmtExpr {
|
||||||
|
range: 81..84,
|
||||||
|
value: EllipsisLiteral(
|
||||||
|
ExprEllipsisLiteral {
|
||||||
|
range: 81..84,
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
),
|
||||||
|
],
|
||||||
|
},
|
||||||
|
)
|
||||||
|
```
|
Loading…
Add table
Add a link
Reference in a new issue