Format PatternMatchClass (#6860)

This commit is contained in:
Victor Hugo Gomes 2023-08-25 16:03:37 -03:00 committed by GitHub
parent 91880b8273
commit 91a780c771
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
10 changed files with 458 additions and 536 deletions

View file

@ -264,6 +264,7 @@ match foo:
y = 1
match foo:
case [1, 2, *rest]:
pass
@ -399,3 +400,61 @@ match foo:
b,
}:
pass
match pattern_match_class:
case Point2D(
# own line
):
...
case (
Point2D
# own line
()
):
...
case Point2D( # end of line line
):
...
case Point2D( # end of line
0, 0
):
...
case Point2D(0, 0):
...
case Point2D(
( # end of line
# own line
0
), 0):
...
case Point3D(x=0, y=0, z=000000000000000000000000000000000000000000000000000000000000000000000000000000000):
...
case Bar(0, a=None, b="hello"):
...
case FooBar(# leading
# leading
# leading
# leading
0 # trailing
# trailing
# trailing
# trailing
):
...
case A(
b # b
= # c
2 # d
# e
):
pass

View file

@ -180,15 +180,7 @@ fn handle_enclosed_comment<'a>(
AnyNodeRef::Comprehension(comprehension) => {
handle_comprehension_comment(comment, comprehension, locator)
}
AnyNodeRef::PatternMatchSequence(pattern_match_sequence) => {
if SequenceType::from_pattern(pattern_match_sequence, locator.contents())
.is_parenthesized()
{
handle_bracketed_end_of_line_comment(comment, locator)
} else {
CommentPlacement::Default(comment)
}
}
AnyNodeRef::ExprAttribute(attribute) => {
handle_attribute_comment(comment, attribute, locator)
}
@ -219,6 +211,18 @@ fn handle_enclosed_comment<'a>(
handle_module_level_own_line_comment_before_class_or_function_comment(comment, locator)
}
AnyNodeRef::WithItem(_) => handle_with_item_comment(comment, locator),
AnyNodeRef::PatternMatchSequence(pattern_match_sequence) => {
if SequenceType::from_pattern(pattern_match_sequence, locator.contents())
.is_parenthesized()
{
handle_bracketed_end_of_line_comment(comment, locator)
} else {
CommentPlacement::Default(comment)
}
}
AnyNodeRef::PatternMatchClass(class) => {
handle_pattern_match_class_comment(comment, class, locator)
}
AnyNodeRef::PatternMatchAs(_) => handle_pattern_match_as_comment(comment, locator),
AnyNodeRef::PatternMatchStar(_) => handle_pattern_match_star_comment(comment),
AnyNodeRef::PatternMatchMapping(pattern) => {
@ -1229,6 +1233,77 @@ fn handle_with_item_comment<'a>(
}
}
/// Handles trailing comments after the `as` keyword of a pattern match item:
///
/// ```python
/// case (
/// Pattern
/// # dangling
/// ( # dangling
/// # dangling
/// )
/// ): ...
/// ```
fn handle_pattern_match_class_comment<'a>(
comment: DecoratedComment<'a>,
class: &'a ast::PatternMatchClass,
locator: &Locator,
) -> CommentPlacement<'a> {
// Find the open parentheses on the arguments.
let Some(left_paren) = SimpleTokenizer::starts_at(class.cls.end(), locator.contents())
.skip_trivia()
.find(|token| token.kind == SimpleTokenKind::LParen)
else {
return CommentPlacement::Default(comment);
};
// If the comment appears before the open parenthesis, it's dangling:
// ```python
// case (
// Pattern
// # dangling
// (...)
// ): ...
// ```
if comment.end() < left_paren.start() {
return CommentPlacement::dangling(comment.enclosing_node(), comment);
}
let Some(first_item) = class
.patterns
.first()
.map(Ranged::start)
.or_else(|| class.kwd_attrs.first().map(Ranged::start))
else {
// If there are no items, then the comment must be dangling:
// ```python
// case (
// Pattern(
// # dangling
// )
// ): ...
// ```
return CommentPlacement::dangling(comment.enclosing_node(), comment);
};
// If the comment appears before the first item or its parentheses, then it's dangling:
// ```python
// case (
// Pattern( # dangling
// 0,
// 0,
// )
// ): ...
// ```
if comment.line_position().is_end_of_line() {
if comment.end() < first_item {
return CommentPlacement::dangling(comment.enclosing_node(), comment);
}
}
CommentPlacement::Default(comment)
}
/// Handles trailing comments after the `as` keyword of a pattern match item:
///
/// ```python

View file

@ -1,23 +1,97 @@
use ruff_formatter::{write, Buffer, FormatResult};
use crate::comments::{dangling_comments, SourceComment};
use ruff_formatter::write;
use ruff_python_ast::node::AnyNodeRef;
use ruff_python_ast::PatternMatchClass;
use ruff_python_ast::{Pattern, PatternMatchClass, Ranged};
use ruff_python_trivia::{SimpleTokenKind, SimpleTokenizer};
use ruff_text_size::{TextRange, TextSize};
use crate::expression::parentheses::{NeedsParentheses, OptionalParentheses};
use crate::expression::parentheses::{
empty_parenthesized, parenthesized, NeedsParentheses, OptionalParentheses, Parentheses,
};
use crate::prelude::*;
use crate::{not_yet_implemented_custom_text, FormatNodeRule, PyFormatter};
#[derive(Default)]
pub struct FormatPatternMatchClass;
impl FormatNodeRule<PatternMatchClass> for FormatPatternMatchClass {
fn fmt_fields(&self, item: &PatternMatchClass, f: &mut PyFormatter) -> FormatResult<()> {
write!(
f,
[not_yet_implemented_custom_text(
"NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0)",
item
)]
)
let PatternMatchClass {
range,
cls,
patterns,
kwd_attrs,
kwd_patterns,
} = item;
let comments = f.context().comments().clone();
let dangling = comments.dangling(item);
// Identify the dangling comments before and after the open parenthesis.
let (before_parenthesis, after_parenthesis) = if let Some(left_paren) =
SimpleTokenizer::starts_at(cls.end(), f.context().source())
.find(|token| token.kind() == SimpleTokenKind::LParen)
{
dangling
.split_at(dangling.partition_point(|comment| comment.start() < left_paren.start()))
} else {
(dangling, [].as_slice())
};
write!(f, [cls.format(), dangling_comments(before_parenthesis)])?;
match (patterns.as_slice(), kwd_attrs.as_slice()) {
([], []) => {
// No patterns; render parentheses with any dangling comments.
write!(f, [empty_parenthesized("(", after_parenthesis, ")")])
}
([pattern], []) => {
// A single pattern. We need to take care not to re-parenthesize it, since our standard
// parenthesis detection will false-positive here.
let parentheses = if is_single_argument_parenthesized(
pattern,
item.end(),
f.context().source(),
) {
Parentheses::Always
} else {
Parentheses::Never
};
write!(
f,
[
parenthesized("(", &pattern.format().with_options(parentheses), ")")
.with_dangling_comments(after_parenthesis)
]
)
}
_ => {
// Multiple patterns: standard logic.
let items = format_with(|f| {
let mut join = f.join_comma_separated(range.end());
join.nodes(patterns.iter());
for (key, value) in kwd_attrs.iter().zip(kwd_patterns.iter()) {
join.entry(
key,
&format_with(|f| write!(f, [key.format(), text("="), value.format()])),
);
}
join.finish()
});
write!(
f,
[parenthesized("(", &group(&items), ")")
.with_dangling_comments(after_parenthesis)]
)
}
}
}
fn fmt_dangling_comments(
&self,
_dangling_comments: &[SourceComment],
_f: &mut PyFormatter,
) -> FormatResult<()> {
Ok(())
}
}
@ -25,8 +99,56 @@ impl NeedsParentheses for PatternMatchClass {
fn needs_parentheses(
&self,
_parent: AnyNodeRef,
_context: &PyFormatContext,
context: &PyFormatContext,
) -> OptionalParentheses {
// If there are any comments outside of the class parentheses, break:
// ```python
// case (
// Pattern
// # dangling
// (...)
// ): ...
// ```
let dangling = context.comments().dangling(self);
if !dangling.is_empty() {
if let Some(left_paren) = SimpleTokenizer::starts_at(self.cls.end(), context.source())
.find(|token| token.kind() == SimpleTokenKind::LParen)
{
if dangling
.iter()
.any(|comment| comment.start() < left_paren.start())
{
return OptionalParentheses::Multiline;
};
}
}
OptionalParentheses::Never
}
}
/// Returns `true` if the pattern (which is the only argument to a [`PatternMatchClass`]) is
/// parenthesized. Used to avoid falsely assuming that `x` is parenthesized in cases like:
/// ```python
/// case Point2D(x): ...
/// ```
fn is_single_argument_parenthesized(pattern: &Pattern, call_end: TextSize, source: &str) -> bool {
let mut has_seen_r_paren = false;
for token in SimpleTokenizer::new(source, TextRange::new(pattern.end(), call_end)).skip_trivia()
{
match token.kind() {
SimpleTokenKind::RParen => {
if has_seen_r_paren {
return true;
}
has_seen_r_paren = true;
}
// Skip over any trailing comma
SimpleTokenKind::Comma => continue,
_ => {
// Passed the arguments
break;
}
}
}
false
}

View file

@ -156,15 +156,6 @@ match x:
```diff
--- Black
+++ Ruff
@@ -6,7 +6,7 @@
y = 0
# case black_test_patma_142
match x:
- case bytes(z):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
y = 0
# case black_test_patma_073
match x:
@@ -16,11 +16,11 @@
y = 1
# case black_test_patma_006
@ -226,7 +217,7 @@ match x:
y = 0
# case black_test_patma_142
match x:
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case bytes(z):
y = 0
# case black_test_patma_073
match x:

View file

@ -131,36 +131,6 @@ match bar1:
```diff
--- Black
+++ Ruff
@@ -5,9 +5,9 @@
print(b)
case [a as b, c, d, e as f]:
print(f)
- case Point(a as b):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print(b)
- case Point(int() as x, int() as y):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print(x, y)
@@ -15,7 +15,7 @@
case: int = re.match(something)
match re.match(case):
- case type("match", match):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
pass
case match:
pass
@@ -23,7 +23,7 @@
def func(match: case, case: match) -> case:
match Something():
- case func(match, case):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
...
case another:
...
@@ -32,14 +32,23 @@
match maybe, multiple:
case perhaps, 5:
@ -188,21 +158,7 @@ match bar1:
pass
case _:
pass
@@ -59,12 +68,7 @@
),
case,
):
- case case(
- match=case,
- case=re.match(
- loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
- ),
- ):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
pass
case [a as match]:
@@ -87,10 +91,10 @@
@@ -87,7 +96,7 @@
match something:
case {
"key": key as key_1,
@ -210,12 +166,8 @@ match bar1:
+ "password": NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as password,
}:
pass
- case {"maybe": something(complicated as this) as that}:
+ case {"maybe": NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0) as that}:
pass
@@ -101,19 +105,17 @@
case {"maybe": something(complicated as this) as that}:
@@ -101,7 +110,7 @@
case 2 as b, 3 as c:
pass
@ -224,20 +176,6 @@ match bar1:
pass
match bar1:
- case Foo(aa=Callable() as aa, bb=int()):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print(bar1.aa, bar1.bb)
case _:
print("no match", "\n")
match bar1:
- case Foo(
- normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u
- ):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
pass
```
## Ruff Output
@ -250,9 +188,9 @@ match something:
print(b)
case [a as b, c, d, e as f]:
print(f)
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(a as b):
print(b)
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(int() as x, int() as y):
print(x, y)
@ -260,7 +198,7 @@ match = 1
case: int = re.match(something)
match re.match(case):
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case type("match", match):
pass
case match:
pass
@ -268,7 +206,7 @@ match re.match(case):
def func(match: case, case: match) -> case:
match Something():
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case func(match, case):
...
case another:
...
@ -313,7 +251,12 @@ match match(
),
case,
):
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case case(
match=case,
case=re.match(
loooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooong
),
):
pass
case [a as match]:
@ -339,7 +282,7 @@ match something:
"password": NOT_YET_IMPLEMENTED_PatternMatchOf | (y) as password,
}:
pass
case {"maybe": NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0) as that}:
case {"maybe": something(complicated as this) as that}:
pass
@ -355,14 +298,16 @@ match something:
match bar1:
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Foo(aa=Callable() as aa, bb=int()):
print(bar1.aa, bar1.bb)
case _:
print("no match", "\n")
match bar1:
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Foo(
normal=x, perhaps=[list, {"x": d, "y": 1.0}] as y, otherwise=something, q=t as u
):
pass
```

View file

@ -1,357 +0,0 @@
---
source: crates/ruff_python_formatter/tests/fixtures.rs
input_file: crates/ruff_python_formatter/resources/test/fixtures/black/py_310/pattern_matching_generic.py
---
## Input
```py
re.match()
match = a
with match() as match:
match = f"{match}"
re.match()
match = a
with match() as match:
match = f"{match}"
def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
if not target_versions:
# No target_version specified, so try all grammars.
return [
# Python 3.7+
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords,
# Python 3.0-3.6
pygram.python_grammar_no_print_statement_no_exec_statement,
# Python 2.7 with future print_function import
pygram.python_grammar_no_print_statement,
# Python 2.7
pygram.python_grammar,
]
match match:
case case:
match match:
case case:
pass
if all(version.is_python2() for version in target_versions):
# Python 2-only code, so try Python 2 grammars.
return [
# Python 2.7 with future print_function import
pygram.python_grammar_no_print_statement,
# Python 2.7
pygram.python_grammar,
]
re.match()
match = a
with match() as match:
match = f"{match}"
def test_patma_139(self):
x = False
match x:
case bool(z):
y = 0
self.assertIs(x, False)
self.assertEqual(y, 0)
self.assertIs(z, x)
# Python 3-compatible code, so only try Python 3 grammar.
grammars = []
if supports_feature(target_versions, Feature.PATTERN_MATCHING):
# Python 3.10+
grammars.append(pygram.python_grammar_soft_keywords)
# If we have to parse both, try to parse async as a keyword first
if not supports_feature(
target_versions, Feature.ASYNC_IDENTIFIERS
) and not supports_feature(target_versions, Feature.PATTERN_MATCHING):
# Python 3.7-3.9
grammars.append(
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords
)
if not supports_feature(target_versions, Feature.ASYNC_KEYWORDS):
# Python 3.0-3.6
grammars.append(pygram.python_grammar_no_print_statement_no_exec_statement)
def test_patma_155(self):
x = 0
y = None
match x:
case 1e1000:
y = 0
self.assertEqual(x, 0)
self.assertIs(y, None)
x = range(3)
match x:
case [y, case as x, z]:
w = 0
# At least one of the above branches must have been taken, because every Python
# version has exactly one of the two 'ASYNC_*' flags
return grammars
def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -> Node:
"""Given a string with source, return the lib2to3 Node."""
if not src_txt.endswith("\n"):
src_txt += "\n"
grammars = get_grammars(set(target_versions))
re.match()
match = a
with match() as match:
match = f"{match}"
re.match()
match = a
with match() as match:
match = f"{match}"
```
## Black Differences
```diff
--- Black
+++ Ruff
@@ -46,7 +46,7 @@
def test_patma_139(self):
x = False
match x:
- case bool(z):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
y = 0
self.assertIs(x, False)
self.assertEqual(y, 0)
```
## Ruff Output
```py
re.match()
match = a
with match() as match:
match = f"{match}"
re.match()
match = a
with match() as match:
match = f"{match}"
def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
if not target_versions:
# No target_version specified, so try all grammars.
return [
# Python 3.7+
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords,
# Python 3.0-3.6
pygram.python_grammar_no_print_statement_no_exec_statement,
# Python 2.7 with future print_function import
pygram.python_grammar_no_print_statement,
# Python 2.7
pygram.python_grammar,
]
match match:
case case:
match match:
case case:
pass
if all(version.is_python2() for version in target_versions):
# Python 2-only code, so try Python 2 grammars.
return [
# Python 2.7 with future print_function import
pygram.python_grammar_no_print_statement,
# Python 2.7
pygram.python_grammar,
]
re.match()
match = a
with match() as match:
match = f"{match}"
def test_patma_139(self):
x = False
match x:
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
y = 0
self.assertIs(x, False)
self.assertEqual(y, 0)
self.assertIs(z, x)
# Python 3-compatible code, so only try Python 3 grammar.
grammars = []
if supports_feature(target_versions, Feature.PATTERN_MATCHING):
# Python 3.10+
grammars.append(pygram.python_grammar_soft_keywords)
# If we have to parse both, try to parse async as a keyword first
if not supports_feature(
target_versions, Feature.ASYNC_IDENTIFIERS
) and not supports_feature(target_versions, Feature.PATTERN_MATCHING):
# Python 3.7-3.9
grammars.append(
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords
)
if not supports_feature(target_versions, Feature.ASYNC_KEYWORDS):
# Python 3.0-3.6
grammars.append(pygram.python_grammar_no_print_statement_no_exec_statement)
def test_patma_155(self):
x = 0
y = None
match x:
case 1e1000:
y = 0
self.assertEqual(x, 0)
self.assertIs(y, None)
x = range(3)
match x:
case [y, case as x, z]:
w = 0
# At least one of the above branches must have been taken, because every Python
# version has exactly one of the two 'ASYNC_*' flags
return grammars
def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -> Node:
"""Given a string with source, return the lib2to3 Node."""
if not src_txt.endswith("\n"):
src_txt += "\n"
grammars = get_grammars(set(target_versions))
re.match()
match = a
with match() as match:
match = f"{match}"
re.match()
match = a
with match() as match:
match = f"{match}"
```
## Black Output
```py
re.match()
match = a
with match() as match:
match = f"{match}"
re.match()
match = a
with match() as match:
match = f"{match}"
def get_grammars(target_versions: Set[TargetVersion]) -> List[Grammar]:
if not target_versions:
# No target_version specified, so try all grammars.
return [
# Python 3.7+
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords,
# Python 3.0-3.6
pygram.python_grammar_no_print_statement_no_exec_statement,
# Python 2.7 with future print_function import
pygram.python_grammar_no_print_statement,
# Python 2.7
pygram.python_grammar,
]
match match:
case case:
match match:
case case:
pass
if all(version.is_python2() for version in target_versions):
# Python 2-only code, so try Python 2 grammars.
return [
# Python 2.7 with future print_function import
pygram.python_grammar_no_print_statement,
# Python 2.7
pygram.python_grammar,
]
re.match()
match = a
with match() as match:
match = f"{match}"
def test_patma_139(self):
x = False
match x:
case bool(z):
y = 0
self.assertIs(x, False)
self.assertEqual(y, 0)
self.assertIs(z, x)
# Python 3-compatible code, so only try Python 3 grammar.
grammars = []
if supports_feature(target_versions, Feature.PATTERN_MATCHING):
# Python 3.10+
grammars.append(pygram.python_grammar_soft_keywords)
# If we have to parse both, try to parse async as a keyword first
if not supports_feature(
target_versions, Feature.ASYNC_IDENTIFIERS
) and not supports_feature(target_versions, Feature.PATTERN_MATCHING):
# Python 3.7-3.9
grammars.append(
pygram.python_grammar_no_print_statement_no_exec_statement_async_keywords
)
if not supports_feature(target_versions, Feature.ASYNC_KEYWORDS):
# Python 3.0-3.6
grammars.append(pygram.python_grammar_no_print_statement_no_exec_statement)
def test_patma_155(self):
x = 0
y = None
match x:
case 1e1000:
y = 0
self.assertEqual(x, 0)
self.assertIs(y, None)
x = range(3)
match x:
case [y, case as x, z]:
w = 0
# At least one of the above branches must have been taken, because every Python
# version has exactly one of the two 'ASYNC_*' flags
return grammars
def lib2to3_parse(src_txt: str, target_versions: Iterable[TargetVersion] = ()) -> Node:
"""Given a string with source, return the lib2to3 Node."""
if not src_txt.endswith("\n"):
src_txt += "\n"
grammars = get_grammars(set(target_versions))
re.match()
match = a
with match() as match:
match = f"{match}"
re.match()
match = a
with match() as match:
match = f"{match}"
```

View file

@ -127,50 +127,15 @@ def where_is(point):
current_room = current_room.neighbor(direction)
match command.split():
@@ -60,33 +60,33 @@
print("Sorry, you can't go that way")
@@ -62,7 +62,7 @@
match event.get():
- case Click(position=(x, y)):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Click(position=(x, y)):
handle_click_at(x, y)
- case KeyPress(key_name="Q") | Quit():
+ case NOT_YET_IMPLEMENTED_PatternMatchOf | (y):
game.quit()
- case KeyPress(key_name="up arrow"):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case KeyPress(key_name="up arrow"):
game.go_north()
- case KeyPress():
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
pass # Ignore other keystrokes
case other_event:
raise ValueError(f"Unrecognized event: {other_event}")
match event.get():
- case Click((x, y), button=Button.LEFT): # This is a left click
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0): # This is a left click
handle_click_at(x, y)
- case Click():
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
pass # ignore other clicks
def where_is(point):
match point:
- case Point(x=0, y=0):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print("Origin")
- case Point(x=0, y=y):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print(f"Y={y}")
- case Point(x=x, y=0):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print(f"X={x}")
- case Point():
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print("Somewhere else")
case _:
print("Not a point")
```
## Ruff Output
@ -238,33 +203,33 @@ match command.split():
print("Sorry, you can't go that way")
match event.get():
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Click(position=(x, y)):
handle_click_at(x, y)
case NOT_YET_IMPLEMENTED_PatternMatchOf | (y):
game.quit()
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case KeyPress(key_name="up arrow"):
game.go_north()
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case KeyPress():
pass # Ignore other keystrokes
case other_event:
raise ValueError(f"Unrecognized event: {other_event}")
match event.get():
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0): # This is a left click
case Click((x, y), button=Button.LEFT): # This is a left click
handle_click_at(x, y)
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Click():
pass # ignore other clicks
def where_is(point):
match point:
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(x=0, y=0):
print("Origin")
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(x=0, y=y):
print(f"Y={y}")
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(x=x, y=0):
print(f"X={x}")
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point():
print("Somewhere else")
case _:
print("Not a point")

View file

@ -65,21 +65,14 @@ match match(
```diff
--- Black
+++ Ruff
@@ -1,35 +1,34 @@
match something:
- case b():
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
print(1 + 1)
- case c(
- very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1
- ):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
@@ -6,30 +6,35 @@
):
print(1)
- case c(
case c(
- very_complex=True,
- perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1,
- ):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
+ very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1
):
print(2)
case a:
pass
@ -109,10 +102,10 @@ match match(
+)
re.match()
match match():
- case case(
case case(
- arg, # comment
- ):
+ case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
+ arg # comment
):
pass
```
@ -120,11 +113,15 @@ match match(
```py
match something:
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case b():
print(1 + 1)
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case c(
very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1
):
print(1)
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case c(
very_complex=True, perhaps_even_loooooooooooooooooooooooooooooooooooooong=-1
):
print(2)
case a:
pass
@ -151,7 +148,9 @@ re.match(
)
re.match()
match match():
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case case(
arg # comment
):
pass
```

View file

@ -116,11 +116,11 @@ def location(point):
match point:
case Point(x=0, y =0 ) : # fmt: skip
print("Origin is the point's location.")
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(x=0, y=y):
print(f"Y={y} and the point is on the y-axis.")
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(x=x, y=0):
print(f"X={x} and the point is on the x-axis.")
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point():
print("The point is located somewhere else on the plane.")
case _:
print("Not a point")
@ -133,12 +133,9 @@ match points:
Point(0, 0)
]: # fmt: skip
print("The origin is the only point in the list.")
case [NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0)]:
case [Point(x, y)]:
print(f"A single point {x}, {y} is in the list.")
case [
NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0),
NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0),
]:
case [Point(0, y1), Point(0, y2)]:
print(f"Two points on the Y axis at {y1}, {y2} are in the list.")
case _:
print("Something else is found in the list.")
@ -158,7 +155,7 @@ match test_variable:
match point:
case Point(x, y) if x == y: # fmt: skip
print(f"The point is located on the diagonal Y=X at {x}.")
case NOT_YET_IMPLEMENTED_PatternMatchClass(0, 0):
case Point(x, y):
print(f"Point is not on the diagonal.")
```

View file

@ -270,6 +270,7 @@ match foo:
y = 1
match foo:
case [1, 2, *rest]:
pass
@ -405,6 +406,64 @@ match foo:
b,
}:
pass
match pattern_match_class:
case Point2D(
# own line
):
...
case (
Point2D
# own line
()
):
...
case Point2D( # end of line line
):
...
case Point2D( # end of line
0, 0
):
...
case Point2D(0, 0):
...
case Point2D(
( # end of line
# own line
0
), 0):
...
case Point3D(x=0, y=0, z=000000000000000000000000000000000000000000000000000000000000000000000000000000000):
...
case Bar(0, a=None, b="hello"):
...
case FooBar(# leading
# leading
# leading
# leading
0 # trailing
# trailing
# trailing
# trailing
):
...
case A(
b # b
= # c
2 # d
# e
):
pass
```
## Output
@ -825,6 +884,73 @@ match foo:
**b,
}:
pass
match pattern_match_class:
case Point2D(
# own line
):
...
case (
Point2D
# own line
()
):
...
case Point2D( # end of line line
):
...
case Point2D(
# end of line
0,
0,
):
...
case Point2D(0, 0):
...
case Point2D(
( # end of line
# own line
0
),
0,
):
...
case Point3D(
x=0,
y=0,
z=000000000000000000000000000000000000000000000000000000000000000000000000000000000,
):
...
case Bar(0, a=None, b="hello"):
...
case FooBar(
# leading
# leading
# leading
# leading
0 # trailing
# trailing
# trailing
# trailing
):
...
case A(
b=# b
# c
2 # d
# e
):
pass
```