-
-
Notifications
You must be signed in to change notification settings - Fork 2
reimplement 91X in libcst #143
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,10 +7,11 @@ | |
|
||
import ast | ||
from fnmatch import fnmatch | ||
from typing import TYPE_CHECKING, NamedTuple, TypeVar | ||
from typing import TYPE_CHECKING, Any, NamedTuple, TypeVar | ||
|
||
import libcst as cst | ||
import libcst.matchers as m | ||
from libcst.helpers import ensure_type, get_full_name_for_node_or_raise | ||
|
||
from ..base import Statement | ||
from . import ( | ||
|
@@ -27,6 +28,7 @@ | |
|
||
T = TypeVar("T", bound=Flake8TrioVisitor) | ||
T_CST = TypeVar("T_CST", bound=Flake8TrioVisitor_cst) | ||
T_EITHER = TypeVar("T_EITHER", Flake8TrioVisitor, Flake8TrioVisitor_cst) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think here you mean There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Confused me for a minute why the multi-arg form doesn't give errors when passed subtypes, but figured out that it transforms the type. |
||
|
||
|
||
def error_class(error_class: type[T]) -> type[T]: | ||
|
@@ -41,7 +43,7 @@ def error_class_cst(error_class: type[T_CST]) -> type[T_CST]: | |
return error_class | ||
|
||
|
||
def disabled_by_default(error_class: type[T]) -> type[T]: | ||
def disabled_by_default(error_class: type[T_EITHER]) -> type[T_EITHER]: | ||
assert error_class.error_codes | ||
default_disabled_error_codes.extend(error_class.error_codes) | ||
return error_class | ||
|
@@ -86,6 +88,19 @@ def fnmatch_qualified_name(name_list: list[ast.expr], *patterns: str) -> str | N | |
return None | ||
|
||
|
||
def fnmatch_qualified_name_cst( | ||
name_list: Iterable[cst.Decorator], *patterns: str | ||
) -> str | None: | ||
for name in name_list: | ||
qualified_name = get_full_name_for_node_or_raise(name) | ||
|
||
for pattern in patterns: | ||
# strip leading "@"s for when we're working with decorators | ||
if fnmatch(qualified_name, pattern.lstrip("@")): | ||
return pattern | ||
return None | ||
|
||
|
||
# used in 103/104 and 910/911 | ||
def iter_guaranteed_once(iterable: ast.expr) -> bool: | ||
# static container with an "elts" attribute | ||
|
@@ -112,6 +127,8 @@ def iter_guaranteed_once(iterable: ast.expr) -> bool: | |
return True | ||
else: | ||
return True | ||
return False | ||
|
||
jakkdl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
# check for range() with literal parameters | ||
if ( | ||
isinstance(iterable, ast.Call) | ||
|
@@ -125,6 +142,62 @@ def iter_guaranteed_once(iterable: ast.expr) -> bool: | |
return False | ||
|
||
|
||
def cst_literal_eval(node: cst.BaseExpression) -> Any: | ||
ast_node = cst.Module([cst.SimpleStatementLine([cst.Expr(node)])]).code | ||
try: | ||
return ast.literal_eval(ast_node) | ||
except Exception: # noqa: PIE786 | ||
return None | ||
Comment on lines
+145
to
+150
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. major hoops to have to run through, but didn't find any better way of doing it for now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Seems surprising to return We might also want to open an upstream issue - there's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
|
||
|
||
def iter_guaranteed_once_cst(iterable: cst.BaseExpression) -> bool: | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ugly, but works pretty much like the ast version. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd be inclined to do the same "unparse and use ast" trick - if not we'll need some serious testing to be sure they're equivalent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you mean using |
||
# static container with an "elts" attribute | ||
if elts := getattr(iterable, "elements", []): | ||
for elt in elts: | ||
assert isinstance( | ||
elt, | ||
( | ||
cst.Element, | ||
cst.DictElement, | ||
cst.StarredElement, | ||
cst.StarredDictElement, | ||
), | ||
) | ||
# recurse starred expression | ||
if isinstance(elt, (cst.StarredElement, cst.StarredDictElement)): | ||
if iter_guaranteed_once_cst(elt.value): | ||
return True | ||
else: | ||
return True | ||
return False | ||
|
||
if isinstance(iterable, cst.SimpleString): | ||
return len(ast.literal_eval(iterable.value)) > 0 | ||
|
||
# check for range() with literal parameters | ||
if m.matches( | ||
iterable, | ||
m.Call( | ||
func=m.Name("range"), | ||
), | ||
): | ||
try: | ||
return ( | ||
len( | ||
range( | ||
*[ | ||
cst_literal_eval(a.value) | ||
for a in ensure_type(iterable, cst.Call).args | ||
] | ||
jakkdl marked this conversation as resolved.
Show resolved
Hide resolved
|
||
) | ||
) | ||
> 0 | ||
) | ||
except Exception: # noqa: PIE786 | ||
return False | ||
return False | ||
|
||
|
||
# used in 102, 103 and 104 | ||
def critical_except(node: ast.ExceptHandler) -> Statement | None: | ||
def has_exception(node: ast.expr) -> str | None: | ||
|
Uh oh!
There was an error while loading. Please reload this page.