Fix feature detection for parenthesized context managers (#4104)
This commit is contained in:
parent
eb7661f8ab
commit
ebd543c0ac
@ -10,6 +10,7 @@
|
|||||||
|
|
||||||
- Fix bug where `# fmt: off` automatically dedents when used with the `--line-ranges`
|
- Fix bug where `# fmt: off` automatically dedents when used with the `--line-ranges`
|
||||||
option, even when it is not within the specified line range. (#4084)
|
option, even when it is not within the specified line range. (#4084)
|
||||||
|
- Fix feature detection for parenthesized context managers (#4104)
|
||||||
|
|
||||||
### Preview style
|
### Preview style
|
||||||
|
|
||||||
|
@ -1351,7 +1351,7 @@ def get_features_used( # noqa: C901
|
|||||||
if (
|
if (
|
||||||
len(atom_children) == 3
|
len(atom_children) == 3
|
||||||
and atom_children[0].type == token.LPAR
|
and atom_children[0].type == token.LPAR
|
||||||
and atom_children[1].type == syms.testlist_gexp
|
and _contains_asexpr(atom_children[1])
|
||||||
and atom_children[2].type == token.RPAR
|
and atom_children[2].type == token.RPAR
|
||||||
):
|
):
|
||||||
features.add(Feature.PARENTHESIZED_CONTEXT_MANAGERS)
|
features.add(Feature.PARENTHESIZED_CONTEXT_MANAGERS)
|
||||||
@ -1384,6 +1384,22 @@ def get_features_used( # noqa: C901
|
|||||||
return features
|
return features
|
||||||
|
|
||||||
|
|
||||||
|
def _contains_asexpr(node: Union[Node, Leaf]) -> bool:
|
||||||
|
"""Return True if `node` contains an as-pattern."""
|
||||||
|
if node.type == syms.asexpr_test:
|
||||||
|
return True
|
||||||
|
elif node.type == syms.atom:
|
||||||
|
if (
|
||||||
|
len(node.children) == 3
|
||||||
|
and node.children[0].type == token.LPAR
|
||||||
|
and node.children[2].type == token.RPAR
|
||||||
|
):
|
||||||
|
return _contains_asexpr(node.children[1])
|
||||||
|
elif node.type == syms.testlist_gexp:
|
||||||
|
return any(_contains_asexpr(child) for child in node.children)
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
def detect_target_versions(
|
def detect_target_versions(
|
||||||
node: Node, *, future_imports: Optional[Set[str]] = None
|
node: Node, *, future_imports: Optional[Set[str]] = None
|
||||||
) -> Set[TargetVersion]:
|
) -> Set[TargetVersion]:
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
# flags: --minimum-version=3.8 --no-preview-line-length-1
|
# flags: --minimum-version=3.8
|
||||||
if (foo := 0):
|
if (foo := 0):
|
||||||
pass
|
pass
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@
|
|||||||
List,
|
List,
|
||||||
Optional,
|
Optional,
|
||||||
Sequence,
|
Sequence,
|
||||||
|
Set,
|
||||||
Type,
|
Type,
|
||||||
TypeVar,
|
TypeVar,
|
||||||
Union,
|
Union,
|
||||||
@ -874,71 +875,88 @@ def test_get_features_used_decorator(self) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
def test_get_features_used(self) -> None:
|
def test_get_features_used(self) -> None:
|
||||||
node = black.lib2to3_parse("def f(*, arg): ...\n")
|
self.check_features_used("def f(*, arg): ...\n", set())
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
self.check_features_used(
|
||||||
node = black.lib2to3_parse("def f(*, arg,): ...\n")
|
"def f(*, arg,): ...\n", {Feature.TRAILING_COMMA_IN_DEF}
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.TRAILING_COMMA_IN_DEF})
|
|
||||||
node = black.lib2to3_parse("f(*arg,)\n")
|
|
||||||
self.assertEqual(
|
|
||||||
black.get_features_used(node), {Feature.TRAILING_COMMA_IN_CALL}
|
|
||||||
)
|
)
|
||||||
node = black.lib2to3_parse("def f(*, arg): f'string'\n")
|
self.check_features_used("f(*arg,)\n", {Feature.TRAILING_COMMA_IN_CALL})
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.F_STRINGS})
|
self.check_features_used("def f(*, arg): f'string'\n", {Feature.F_STRINGS})
|
||||||
node = black.lib2to3_parse("123_456\n")
|
self.check_features_used("123_456\n", {Feature.NUMERIC_UNDERSCORES})
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.NUMERIC_UNDERSCORES})
|
self.check_features_used("123456\n", set())
|
||||||
node = black.lib2to3_parse("123456\n")
|
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
|
||||||
source, expected = read_data("cases", "function")
|
source, expected = read_data("cases", "function")
|
||||||
node = black.lib2to3_parse(source)
|
|
||||||
expected_features = {
|
expected_features = {
|
||||||
Feature.TRAILING_COMMA_IN_CALL,
|
Feature.TRAILING_COMMA_IN_CALL,
|
||||||
Feature.TRAILING_COMMA_IN_DEF,
|
Feature.TRAILING_COMMA_IN_DEF,
|
||||||
Feature.F_STRINGS,
|
Feature.F_STRINGS,
|
||||||
}
|
}
|
||||||
self.assertEqual(black.get_features_used(node), expected_features)
|
self.check_features_used(source, expected_features)
|
||||||
node = black.lib2to3_parse(expected)
|
self.check_features_used(expected, expected_features)
|
||||||
self.assertEqual(black.get_features_used(node), expected_features)
|
|
||||||
source, expected = read_data("cases", "expression")
|
source, expected = read_data("cases", "expression")
|
||||||
node = black.lib2to3_parse(source)
|
self.check_features_used(source, set())
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
self.check_features_used(expected, set())
|
||||||
node = black.lib2to3_parse(expected)
|
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
self.check_features_used("lambda a, /, b: ...\n", {Feature.POS_ONLY_ARGUMENTS})
|
||||||
node = black.lib2to3_parse("lambda a, /, b: ...")
|
self.check_features_used("def fn(a, /, b): ...", {Feature.POS_ONLY_ARGUMENTS})
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.POS_ONLY_ARGUMENTS})
|
|
||||||
node = black.lib2to3_parse("def fn(a, /, b): ...")
|
self.check_features_used("def fn(): yield a, b", set())
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.POS_ONLY_ARGUMENTS})
|
self.check_features_used("def fn(): return a, b", set())
|
||||||
node = black.lib2to3_parse("def fn(): yield a, b")
|
self.check_features_used("def fn(): yield *b, c", {Feature.UNPACKING_ON_FLOW})
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
self.check_features_used(
|
||||||
node = black.lib2to3_parse("def fn(): return a, b")
|
"def fn(): return a, *b, c", {Feature.UNPACKING_ON_FLOW}
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
|
||||||
node = black.lib2to3_parse("def fn(): yield *b, c")
|
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.UNPACKING_ON_FLOW})
|
|
||||||
node = black.lib2to3_parse("def fn(): return a, *b, c")
|
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.UNPACKING_ON_FLOW})
|
|
||||||
node = black.lib2to3_parse("x = a, *b, c")
|
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
|
||||||
node = black.lib2to3_parse("x: Any = regular")
|
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
|
||||||
node = black.lib2to3_parse("x: Any = (regular, regular)")
|
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
|
||||||
node = black.lib2to3_parse("x: Any = Complex(Type(1))[something]")
|
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
|
||||||
node = black.lib2to3_parse("x: Tuple[int, ...] = a, b, c")
|
|
||||||
self.assertEqual(
|
|
||||||
black.get_features_used(node), {Feature.ANN_ASSIGN_EXTENDED_RHS}
|
|
||||||
)
|
)
|
||||||
node = black.lib2to3_parse("try: pass\nexcept Something: pass")
|
self.check_features_used("x = a, *b, c", set())
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
|
||||||
node = black.lib2to3_parse("try: pass\nexcept (*Something,): pass")
|
self.check_features_used("x: Any = regular", set())
|
||||||
self.assertEqual(black.get_features_used(node), set())
|
self.check_features_used("x: Any = (regular, regular)", set())
|
||||||
node = black.lib2to3_parse("try: pass\nexcept *Group: pass")
|
self.check_features_used("x: Any = Complex(Type(1))[something]", set())
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.EXCEPT_STAR})
|
self.check_features_used(
|
||||||
node = black.lib2to3_parse("a[*b]")
|
"x: Tuple[int, ...] = a, b, c", {Feature.ANN_ASSIGN_EXTENDED_RHS}
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.VARIADIC_GENERICS})
|
)
|
||||||
node = black.lib2to3_parse("a[x, *y(), z] = t")
|
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.VARIADIC_GENERICS})
|
self.check_features_used("try: pass\nexcept Something: pass", set())
|
||||||
node = black.lib2to3_parse("def fn(*args: *T): pass")
|
self.check_features_used("try: pass\nexcept (*Something,): pass", set())
|
||||||
self.assertEqual(black.get_features_used(node), {Feature.VARIADIC_GENERICS})
|
self.check_features_used(
|
||||||
|
"try: pass\nexcept *Group: pass", {Feature.EXCEPT_STAR}
|
||||||
|
)
|
||||||
|
|
||||||
|
self.check_features_used("a[*b]", {Feature.VARIADIC_GENERICS})
|
||||||
|
self.check_features_used("a[x, *y(), z] = t", {Feature.VARIADIC_GENERICS})
|
||||||
|
self.check_features_used("def fn(*args: *T): pass", {Feature.VARIADIC_GENERICS})
|
||||||
|
|
||||||
|
self.check_features_used("with a: pass", set())
|
||||||
|
self.check_features_used("with a, b: pass", set())
|
||||||
|
self.check_features_used("with a as b: pass", set())
|
||||||
|
self.check_features_used("with a as b, c as d: pass", set())
|
||||||
|
self.check_features_used("with (a): pass", set())
|
||||||
|
self.check_features_used("with (a, b): pass", set())
|
||||||
|
self.check_features_used("with (a, b) as (c, d): pass", set())
|
||||||
|
self.check_features_used(
|
||||||
|
"with (a as b): pass", {Feature.PARENTHESIZED_CONTEXT_MANAGERS}
|
||||||
|
)
|
||||||
|
self.check_features_used(
|
||||||
|
"with ((a as b)): pass", {Feature.PARENTHESIZED_CONTEXT_MANAGERS}
|
||||||
|
)
|
||||||
|
self.check_features_used(
|
||||||
|
"with (a, b as c): pass", {Feature.PARENTHESIZED_CONTEXT_MANAGERS}
|
||||||
|
)
|
||||||
|
self.check_features_used(
|
||||||
|
"with (a, (b as c)): pass", {Feature.PARENTHESIZED_CONTEXT_MANAGERS}
|
||||||
|
)
|
||||||
|
self.check_features_used(
|
||||||
|
"with ((a, ((b as c)))): pass", {Feature.PARENTHESIZED_CONTEXT_MANAGERS}
|
||||||
|
)
|
||||||
|
|
||||||
|
def check_features_used(self, source: str, expected: Set[Feature]) -> None:
|
||||||
|
node = black.lib2to3_parse(source)
|
||||||
|
actual = black.get_features_used(node)
|
||||||
|
msg = f"Expected {expected} but got {actual} for {source!r}"
|
||||||
|
try:
|
||||||
|
self.assertEqual(actual, expected, msg=msg)
|
||||||
|
except AssertionError:
|
||||||
|
DebugVisitor.show(node)
|
||||||
|
raise
|
||||||
|
|
||||||
def test_get_features_used_for_future_flags(self) -> None:
|
def test_get_features_used_for_future_flags(self) -> None:
|
||||||
for src, features in [
|
for src, features in [
|
||||||
|
Loading…
Reference in New Issue
Block a user