Skip to content

Commit f096b9a

Browse files
Python: Upgrade ty to 0.0.17 and fix type errors (#6737)
* Python: Upgrade ty to 0.0.17 and fix type errors Upgrade the ty type checker from 0.0.15 to 0.0.17, reducing diagnostics from 92 to 33 by fixing real type errors and updating stale suppression comments. Changes: - Bump ty>=0.0.15 to ty>=0.0.17 in pyproject.toml - Update 4 stale `ty: ignore[possibly-missing-attribute]` to `ty: ignore[unresolved-attribute]` (rule renamed in 0.0.17) - Add `invalid-method-override = "ignore"` for ~30 visitor pattern parameter name mismatches, remove 5 now-stale inline suppressions - Fix 3 `invalid-parameter-default` errors: `Tree = None` to `Optional[Tree] = None` in format visitors - Fix 5 format visitor `visit()` return types with proper cast - Fix `_concatenate_prefix` parameter type `J` to `J2` to match return - Remove 6 redundant casts in format visitors - Add 5 asserts for potential None dereferences after `__convert()` and `visit_and_cast()` calls - Extract `_callable_arg_count()` helper to avoid `__code__` access on union callable types - Add inline `ty: ignore[unresolved-import]` for false positives (pytest, string.templatelib, PyComment, Markup types) * Python: Fix invalid-method-override errors and remove global ignore Rename visitor method parameters to match base class signatures and fix type annotations so the global `invalid-method-override = "ignore"` in pyproject.toml can be removed. Add targeted inline `ty: ignore` comments only for the 3 cases where overrides intentionally narrow types.
1 parent 35d5ed3 commit f096b9a

19 files changed

Lines changed: 129 additions & 119 deletions

rewrite-python/rewrite/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ dev = [
3636
"ruff>=0.1.0",
3737
]
3838
typing = [
39-
"ty>=0.0.15", # Required for type attribution with Java recipes
39+
"ty>=0.0.17", # Required for type attribution with Java recipes
4040
]
4141
publish = [
4242
"build>=1.0.0",

rewrite-python/rewrite/src/rewrite/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
# helps pytest to rewrite the assert statements in test.py
44
try:
5-
import pytest
5+
import pytest # ty: ignore[unresolved-import]
66
pytest.register_assert_rewrite("rewrite.test")
77
except ImportError:
88
pass # pytest not available, skip assert rewriting

rewrite-python/rewrite/src/rewrite/parser.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def with_erroneous(self, erroneous: Optional[SourceFile]) -> 'ParseError':
141141
return self if erroneous is self._erroneous else replace(self, _erroneous=erroneous)
142142

143143
def printer(self, cursor: Cursor) -> TreeVisitor[Tree, PrintOutputCapture[P]]:
144-
return PrinterFactory.current().create_printer(cursor) # ty: ignore[possibly-missing-attribute] # PrinterFactory.current() is always set
144+
return PrinterFactory.current().create_printer(cursor) # ty: ignore[unresolved-attribute] # PrinterFactory.current() is always set
145145

146146
def is_acceptable(self, v: TreeVisitor[Any, P], p: P) -> bool:
147147
return v.is_adaptable_to(ParseErrorVisitor)

rewrite-python/rewrite/src/rewrite/python/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@
6767
VariableScope,
6868
YieldFrom,
6969
)
70-
from rewrite.python.support_types import PyComment
70+
from rewrite.python.support_types import PyComment # ty: ignore[unresolved-import]
7171
from rewrite.python.visitor import PythonVisitor
7272
from rewrite.python.style import (
7373
SpacesStyle,

rewrite-python/rewrite/src/rewrite/python/_parser_visitor.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -792,6 +792,7 @@ def visit_Import(self, node):
792792
if len(node.names) == 1:
793793
prefix = self.__source_before('import')
794794
imp = self.__convert(node.names[0])
795+
assert imp is not None
795796
return imp.replace(prefix=prefix, qualid=imp.qualid.replace(prefix=imp.prefix))
796797

797798
prefix = self.__source_before('import')
@@ -1705,6 +1706,7 @@ def visit_BinOp(self, node):
17051706
def visit_BoolOp(self, node):
17061707
prefix = self.__whitespace()
17071708
left = self.__convert(node.values[0])
1709+
assert left is not None
17081710
for right_expr in node.values[1:]:
17091711
left = j.Binary(
17101712
random_id(),
@@ -2246,7 +2248,7 @@ def __map_decorator(self, decorator) -> j.Annotation:
22462248
# Wrap name in extra parentheses if present
22472249
if extra_parens:
22482250
# Set the inner prefix on the name
2249-
name = name.replace(prefix=extra_parens[-1][1]) # ty: ignore[possibly-missing-attribute] # recursive call returns unknown
2251+
name = name.replace(prefix=extra_parens[-1][1]) # ty: ignore[unresolved-attribute] # recursive call returns unknown
22502252

22512253
# Wrap in extra parentheses (innermost to outermost)
22522254
wrapped: Expression = name
@@ -2731,7 +2733,7 @@ def visit_Tuple(self, node):
27312733
extra_parens[0][1] if extra_parens else prefix,
27322734
Markers.EMPTY,
27332735
py.CollectionLiteral.Kind.TUPLE,
2734-
elements.replace(markers= # ty: ignore[possibly-missing-attribute] # complex union type
2736+
elements.replace(markers= # ty: ignore[unresolved-attribute] # complex union type
27352737
Markers.build(random_id(), [OmitParentheses(random_id())])) if omit_parens else elements,
27362738
self._type_mapping.type(node)
27372739
)
@@ -3322,6 +3324,7 @@ def __map_fstring_as_literal(self, node: ast.JoinedStr, leading_prefix: Space, t
33223324
self._type_mapping.type(node)
33233325
)
33243326
is_first = False
3327+
assert res is not None
33253328
return res
33263329

33273330
def __map_fstring(self, node, prefix: Space, tok: TokenInfo, value_idx: int = 0, *,

rewrite-python/rewrite/src/rewrite/python/format/blank_lines.py

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,23 @@
1111

1212

1313
class BlankLinesVisitor(PythonVisitor):
14-
def __init__(self, style: BlankLinesStyle, stop_after: Tree = None):
14+
def __init__(self, style: BlankLinesStyle, stop_after: Optional[Tree] = None):
1515
self._style = style
1616
self._stop_after = stop_after
1717
self._stop = False
1818

19-
def visit_compilation_unit(self, compilation_unit: CompilationUnit, p: P) -> J:
20-
if not compilation_unit.prefix.comments:
21-
compilation_unit = compilation_unit.replace(prefix=Space.EMPTY)
22-
return super().visit_compilation_unit(compilation_unit, p)
19+
def visit_compilation_unit(self, cu: CompilationUnit, p: P) -> J:
20+
if not cu.prefix.comments:
21+
cu = cu.replace(prefix=Space.EMPTY)
22+
return super().visit_compilation_unit(cu, p)
2323

24-
def visit_statement(self, statement: Statement, p: P) -> J:
25-
statement = super().visit_statement(statement, p)
24+
def visit_statement(self, stmt: Statement, p: P) -> J:
25+
stmt = super().visit_statement(stmt, p)
2626

2727
parent_cursor = self.cursor.parent_tree_cursor()
2828
top_level = isinstance(parent_cursor.value, CompilationUnit)
2929

30-
if isinstance(statement, (Import, MultiImport)):
30+
if isinstance(stmt, (Import, MultiImport)):
3131
parent_cursor.put_message('prev_import', True)
3232
prev_import = False
3333
else:
@@ -36,43 +36,43 @@ def visit_statement(self, statement: Statement, p: P) -> J:
3636
parent_cursor.put_message('prev_import', False)
3737

3838
if top_level:
39-
if statement == cast(CompilationUnit, parent_cursor.value).statements[0]:
40-
statement = statement.replace(prefix=statement.prefix.replace(whitespace=''))
39+
if stmt == cast(CompilationUnit, parent_cursor.value).statements[0]:
40+
stmt = stmt.replace(prefix=stmt.prefix.replace(whitespace=''))
4141
else:
42-
min_lines = max(self._style.minimum.around_top_level_classes_functions if isinstance(statement, (ClassDeclaration, MethodDeclaration)) else 0,
42+
min_lines = max(self._style.minimum.around_top_level_classes_functions if isinstance(stmt, (ClassDeclaration, MethodDeclaration)) else 0,
4343
self._style.minimum.after_top_level_imports if prev_import else 0)
44-
statement = _adjusted_lines_for_tree(statement, min_lines, self._style.keep_maximum.in_declarations)
44+
stmt = _adjusted_lines_for_tree(stmt, min_lines, self._style.keep_maximum.in_declarations)
4545
else:
4646
in_block = isinstance(parent_cursor.value, Block)
4747
in_class = in_block and isinstance(parent_cursor.parent_tree_cursor().value, ClassDeclaration)
4848
min_lines = 0
4949
if in_class:
50-
is_first = cast(Block, parent_cursor.value).statements[0] is statement
51-
if not is_first and isinstance(statement, MethodDeclaration):
50+
is_first = cast(Block, parent_cursor.value).statements[0] is stmt
51+
if not is_first and isinstance(stmt, MethodDeclaration):
5252
min_lines = max(min_lines, self._style.minimum.around_method)
53-
elif not is_first and isinstance(statement, ClassDeclaration):
53+
elif not is_first and isinstance(stmt, ClassDeclaration):
5454
min_lines = max(min_lines, self._style.minimum.around_class)
55-
elif is_first and isinstance(statement, MethodDeclaration):
55+
elif is_first and isinstance(stmt, MethodDeclaration):
5656
min_lines = max(min_lines, self._style.minimum.before_first_method)
5757

5858
# This seems to correspond to how IntelliJ interprets this configuration
5959
max_lines = self._style.keep_maximum.in_declarations if \
60-
isinstance(statement, (ClassDeclaration, MethodDeclaration)) else \
60+
isinstance(stmt, (ClassDeclaration, MethodDeclaration)) else \
6161
self._style.keep_maximum.in_code
6262

6363
if prev_import:
6464
min_lines = max(min_lines, self._style.minimum.after_local_imports)
6565

66-
statement = _adjusted_lines_for_tree(statement, min_lines, max_lines)
67-
return statement
66+
stmt = _adjusted_lines_for_tree(stmt, min_lines, max_lines)
67+
return stmt
6868

6969
def post_visit(self, tree: T, p: P) -> Optional[T]:
7070
if self._stop_after and tree == self._stop_after:
7171
self._stop = True
7272
return tree
7373

7474
def visit(self, tree: Optional[Tree], p: P, parent: Optional[Cursor] = None) -> Optional[T]:
75-
return tree if self._stop else super().visit(tree, p, parent)
75+
return cast(Optional[T], tree if self._stop else super().visit(tree, p, parent))
7676

7777

7878
def _adjusted_lines_for_right_padded(tree: JRightPadded[J2], min_lines: int, max_lines: int) -> JRightPadded[J2]:

rewrite-python/rewrite/src/rewrite/python/format/minimum_viable_spacing.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ def post_visit(self, tree: T, p: P) -> Optional[T]:
3333
return tree
3434

3535
def visit(self, tree: Optional[Tree], p: P, parent: Optional[Cursor] = None) -> Optional[T]:
36-
return tree if self._stop else super().visit(tree, p, parent)
36+
return cast(Optional[T], tree if self._stop else super().visit(tree, p, parent))
3737

3838

3939
def _common_margin(s1, s2):

rewrite-python/rewrite/src/rewrite/python/format/normalize_format.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111

1212

1313
class NormalizeFormatVisitor(PythonVisitor):
14-
def __init__(self, stop_after: Tree = None):
14+
def __init__(self, stop_after: Optional[Tree] = None):
1515
self._stop_after = stop_after
1616
self._stop = False
1717

18-
def visit_class_declaration(self, cd: ClassDeclaration, p: P) -> J:
19-
cd = cast(ClassDeclaration, super().visit_class_declaration(cd, p))
18+
def visit_class_declaration(self, class_decl: ClassDeclaration, p: P) -> J:
19+
cd = cast(ClassDeclaration, super().visit_class_declaration(class_decl, p))
2020
if cd.leading_annotations:
2121
cd = _concatenate_prefix(cd, Space.first_prefix(cd.leading_annotations))
2222
cd = cd.replace(leading_annotations=Space.format_first_prefix(cd.leading_annotations, Space.EMPTY))
@@ -26,8 +26,8 @@ def visit_class_declaration(self, cd: ClassDeclaration, p: P) -> J:
2626
cd = cd.padding.replace(kind=cd.padding.kind.with_prefix(Space.EMPTY))
2727
return cd
2828

29-
def visit_method_declaration(self, md: MethodDeclaration, p: P) -> J:
30-
md = cast(MethodDeclaration, super().visit_method_declaration(md, p))
29+
def visit_method_declaration(self, method: MethodDeclaration, p: P) -> J:
30+
md = cast(MethodDeclaration, super().visit_method_declaration(method, p))
3131
if md.leading_annotations:
3232
md = _concatenate_prefix(md, Space.first_prefix(md.leading_annotations))
3333
md = md.replace(leading_annotations=Space.format_first_prefix(md.leading_annotations, Space.EMPTY))
@@ -46,7 +46,7 @@ def post_visit(self, tree: T, p: P) -> Optional[T]:
4646
return tree
4747

4848
def visit(self, tree: Optional[Tree], p: P, parent: Optional[Cursor] = None) -> Optional[T]:
49-
return tree if self._stop else super().visit(tree, p, parent)
49+
return cast(Optional[T], tree if self._stop else super().visit(tree, p, parent))
5050

5151

5252
def _common_margin(s1, s2):
@@ -62,7 +62,7 @@ def _common_margin(s1, s2):
6262
return s2 if len(s2) < len(s1) else s1
6363

6464

65-
def _concatenate_prefix(j: J, prefix: Space) -> J2:
65+
def _concatenate_prefix(j: J2, prefix: Space) -> J2:
6666
if prefix.is_empty():
6767
return j
6868

rewrite-python/rewrite/src/rewrite/python/format/normalize_line_breaks_visitor.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ def process_comment(comment: Comment) -> Comment:
3434

3535
return s.replace(comments=list_map(process_comment, s.comments))
3636

37-
def post_visit(self, tree: T, _: object) -> Optional[T]:
37+
def post_visit(self, tree: T, p: P) -> Optional[T]:
3838
if self._stop_after and tree == self._stop_after:
3939
self._stop = True
4040
return tree

rewrite-python/rewrite/src/rewrite/python/format/normalize_tabs_or_spaces.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
# TODO consider supporting multiline string literals
1414
class NormalizeTabsOrSpacesVisitor(PythonVisitor):
1515

16-
def __init__(self, style: TabsAndIndentsStyle, stop_after: Tree = None):
16+
def __init__(self, style: TabsAndIndentsStyle, stop_after: Optional[Tree] = None):
1717
self._stop_after = stop_after
1818
self._style = style
1919
self._stop = False
2020

21-
def visit_space(self, space: Space, p):
21+
def visit_space(self, space: Optional[Space], p):
2222
if not space or space is Space.EMPTY:
2323
return space
2424

@@ -81,4 +81,4 @@ def post_visit(self, tree: T, p: P) -> Optional[T]:
8181
return tree
8282

8383
def visit(self, tree: Optional[Tree], p: P, parent: Optional[Cursor] = None) -> Optional[T]:
84-
return tree if self._stop else super().visit(tree, p, parent)
84+
return cast(Optional[T], tree if self._stop else super().visit(tree, p, parent))

0 commit comments

Comments
 (0)