Skip to content

Commit c68c8d8

Browse files
authored
Remove use of deprecated AST nodes (#397)
These all cause DeprecationWarnings to be emitted on py312. Part of #392
1 parent e8582f4 commit c68c8d8

1 file changed

Lines changed: 63 additions & 83 deletions

File tree

pyi.py

Lines changed: 63 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -404,14 +404,7 @@ def _is_type_or_Type(node: ast.expr) -> bool:
404404

405405

406406
def _is_None(node: ast.expr) -> bool:
407-
# <=3.7: `BaseException | None` parses as:
408-
# BinOp(left=Name(id='BaseException'), op=BitOr(), right=NameConstant(value=None))`
409-
# >=3.8: `BaseException | None` parses as
410-
# BinOp(left=Name(id='BaseException'), op=BitOr(), right=Constant(value=None))`
411-
#
412-
# ast.NameConstant is deprecated in 3.8+, but doesn't raise a DeprecationWarning,
413-
# and the isinstance() check still works
414-
return isinstance(node, ast.NameConstant) and node.value is None
407+
return isinstance(node, ast.Constant) and node.value is None
415408

416409

417410
class ExitArgAnalysis(NamedTuple):
@@ -553,8 +546,10 @@ def _unparse_func_node(node: ast.FunctionDef | ast.AsyncFunctionDef) -> str:
553546
return re.sub(r"\s+", " ", unparse(node))
554547

555548

556-
def _is_list_of_str_nodes(seq: list[ast.expr | None]) -> TypeGuard[list[ast.Str]]:
557-
return all(isinstance(item, ast.Str) for item in seq)
549+
def _is_list_of_str_nodes(seq: list[ast.expr | None]) -> TypeGuard[list[ast.Constant]]:
550+
return all(
551+
isinstance(item, ast.Constant) and type(item.value) is str for item in seq
552+
)
558553

559554

560555
def _is_bad_TypedDict(node: ast.Call) -> bool:
@@ -582,7 +577,7 @@ def _is_bad_TypedDict(node: ast.Call) -> bool:
582577
if not _is_list_of_str_nodes(typed_dict_fields):
583578
return False
584579

585-
fieldnames = [field.s for field in typed_dict_fields]
580+
fieldnames = [field.value for field in typed_dict_fields]
586581

587582
return all(
588583
fieldname.isidentifier() and not iskeyword(fieldname)
@@ -740,13 +735,17 @@ def _is_valid_default_value_with_annotation(
740735

741736
# `...`, bools, None, str, bytes,
742737
# positive ints, positive floats, positive complex numbers with no real part
743-
if isinstance(node, (ast.Ellipsis, ast.NameConstant, ast.Str, ast.Bytes, ast.Num)):
738+
if isinstance(node, ast.Constant):
744739
return True
745740

746741
# Negative ints, negative floats, negative complex numbers with no real part,
747742
# some constants from the math module
748743
if isinstance(node, ast.UnaryOp) and isinstance(node.op, ast.USub):
749-
if isinstance(node.operand, ast.Num):
744+
numeric_types = {int, float, complex}
745+
if (
746+
isinstance(node.operand, ast.Constant)
747+
and type(node.operand.value) in numeric_types
748+
):
750749
return True
751750
if isinstance(node.operand, ast.Attribute) and isinstance(
752751
node.operand.value, ast.Name
@@ -762,19 +761,19 @@ def _is_valid_default_value_with_annotation(
762761
if (
763762
isinstance(node, ast.BinOp)
764763
and isinstance(node.op, (ast.Add, ast.Sub))
765-
and isinstance(node.right, ast.Num)
766-
and type(node.right.n) is complex
764+
and isinstance(node.right, ast.Constant)
765+
and type(node.right.value) is complex
767766
):
768767
left = node.left
769768
# ...Where the real part is positive:
770-
if isinstance(left, ast.Num) and type(left.n) is not complex:
769+
if isinstance(left, ast.Constant) and type(left.value) in {int, float}:
771770
return True
772771
# ...Where the real part is negative:
773772
if (
774773
isinstance(left, ast.UnaryOp)
775774
and isinstance(left.op, ast.USub)
776-
and isinstance(left.operand, ast.Num)
777-
and type(left.operand.n) is not complex
775+
and isinstance(left.operand, ast.Constant)
776+
and type(left.operand.value) in {int, float}
778777
):
779778
return True
780779
return False
@@ -809,10 +808,8 @@ def _is_valid_pep_604_union(node: ast.expr) -> TypeGuard[ast.BinOp]:
809808
def _is_valid_default_value_without_annotation(node: ast.expr) -> bool:
810809
"""Is `node` a valid default for an assignment without an annotation?"""
811810
return (
812-
isinstance(
813-
node, (ast.Call, ast.Name, ast.Attribute, ast.Subscript, ast.Ellipsis)
814-
)
815-
or _is_None(node)
811+
isinstance(node, (ast.Call, ast.Name, ast.Attribute, ast.Subscript))
812+
or (isinstance(node, ast.Constant) and node.value in {None, ...})
816813
or _is_valid_pep_604_union(node)
817814
)
818815

@@ -1150,45 +1147,23 @@ def visit_Call(self, node: ast.Call) -> None:
11501147
for kw in node.keywords:
11511148
self.visit(kw)
11521149

1153-
def _check_for_Y053(self, node: ast.Constant | ast.Str | ast.Bytes) -> None:
1154-
if len(node.s) > 50:
1155-
self.error(node, Y053)
1156-
1157-
def _check_for_Y054(self, node: ast.Constant | ast.Num) -> None:
1158-
# The maximum character limit is arbitrary, but here's what it's based on:
1159-
# Hex representation of 32-bit integers tend to be 10 chars.
1160-
# So is the decimal representation
1161-
# of the maximum positive signed 32-bit integer.
1162-
# 0xFFFFFFFF --> 4294967295
1163-
if len(str(node.n)) > 10:
1164-
self.error(node, Y054)
1165-
1166-
# 3.8+
11671150
def visit_Constant(self, node: ast.Constant) -> None:
11681151
if isinstance(node.value, str) and not self.string_literals_allowed.active:
11691152
self.error(node, Y020)
11701153
elif isinstance(node.value, (str, bytes)):
1171-
self._check_for_Y053(node)
1154+
if len(node.value) > 50:
1155+
self.error(node, Y053)
11721156
elif isinstance(node.value, (int, float, complex)):
1173-
self._check_for_Y054(node)
1174-
1175-
# 3.7
1176-
def visit_Str(self, node: ast.Str) -> None:
1177-
if self.string_literals_allowed.active:
1178-
self._check_for_Y053(node)
1179-
else:
1180-
self.error(node, Y020)
1181-
1182-
# 3.7
1183-
def visit_Bytes(self, node: ast.Bytes) -> None:
1184-
self._check_for_Y053(node)
1185-
1186-
# 3.7
1187-
def visit_Num(self, node: ast.Num) -> None:
1188-
self._check_for_Y054(node)
1157+
if len(str(node.value)) > 10:
1158+
# The maximum character limit is arbitrary, but here's what it's based on:
1159+
# Hex representation of 32-bit integers tend to be 10 chars.
1160+
# So is the decimal representation
1161+
# of the maximum positive signed 32-bit integer.
1162+
# 0xFFFFFFFF --> 4294967295
1163+
self.error(node, Y054)
11891164

11901165
def visit_Expr(self, node: ast.Expr) -> None:
1191-
if isinstance(node.value, ast.Str):
1166+
if isinstance(node.value, ast.Constant) and isinstance(node.value.value, str):
11921167
self.error(node, Y021)
11931168
else:
11941169
self.generic_visit(node)
@@ -1288,16 +1263,12 @@ def _check_for_Y051_violations(self, analysis: UnionAnalysis) -> None:
12881263
literal_classes_present: defaultdict[str, list[_SliceContents]]
12891264
literal_classes_present = defaultdict(list)
12901265
for literal in analysis.combined_literal_members:
1291-
if isinstance(literal, ast.Str):
1292-
literal_classes_present["str"].append(literal)
1293-
elif isinstance(literal, ast.Bytes):
1294-
literal_classes_present["bytes"].append(literal)
1295-
elif isinstance(literal, ast.Num):
1296-
if type(literal.n) is int:
1297-
literal_classes_present["int"].append(literal)
1298-
elif isinstance(literal, ast.NameConstant):
1299-
if type(literal.value) is bool:
1300-
literal_classes_present["bool"].append(literal)
1266+
interesting_builtins = {str, bytes, int, bool}
1267+
if (
1268+
isinstance(literal, ast.Constant)
1269+
and type(literal.value) in interesting_builtins
1270+
):
1271+
literal_classes_present[type(literal.value).__name__].append(literal)
13011272
for cls, literals in literal_classes_present.items():
13021273
if cls in analysis.builtins_classes_in_union:
13031274
first_literal_present = literals[0]
@@ -1460,10 +1431,9 @@ def _check_subscript_version_check(self, node: ast.Compare) -> None:
14601431
version_info = node.left
14611432
if isinstance(version_info, ast.Subscript):
14621433
slc = version_info.slice
1463-
# TODO: ast.Num works, but is deprecated
1464-
if isinstance(slc, ast.Num):
1434+
if isinstance(slc, ast.Constant):
14651435
# anything other than the integer 0 doesn't make much sense
1466-
if isinstance(slc.n, int) and slc.n == 0:
1436+
if isinstance(slc.value, int) and slc.value == 0:
14671437
must_be_single = True
14681438
else:
14691439
self.error(node, Y003)
@@ -1474,11 +1444,11 @@ def _check_subscript_version_check(self, node: ast.Compare) -> None:
14741444
return
14751445
elif (
14761446
# allow only [:1] and [:2]
1477-
isinstance(slc.upper, ast.Num)
1478-
and isinstance(slc.upper.n, int)
1479-
and slc.upper.n in (1, 2)
1447+
isinstance(slc.upper, ast.Constant)
1448+
and isinstance(slc.upper.value, int)
1449+
and slc.upper.value in {1, 2}
14801450
):
1481-
can_have_strict_equals = slc.upper.n
1451+
can_have_strict_equals = slc.upper.value
14821452
else:
14831453
self.error(node, Y003)
14841454
return
@@ -1501,12 +1471,17 @@ def _check_version_check(
15011471
) -> None:
15021472
comparator = node.comparators[0]
15031473
if must_be_single:
1504-
if not isinstance(comparator, ast.Num) or not isinstance(comparator.n, int):
1474+
if not isinstance(comparator, ast.Constant) or not isinstance(
1475+
comparator.value, int
1476+
):
15051477
self.error(node, Y003)
15061478
elif not isinstance(comparator, ast.Tuple):
15071479
self.error(node, Y003)
15081480
else:
1509-
if not all(isinstance(elt, ast.Num) for elt in comparator.elts):
1481+
if not all(
1482+
isinstance(elt, ast.Constant) and type(elt.value) is int
1483+
for elt in comparator.elts
1484+
):
15101485
self.error(node, Y003)
15111486
elif len(comparator.elts) > 2:
15121487
# mypy only supports major and minor version checks
@@ -1532,11 +1507,11 @@ def _check_platform_check(self, node: ast.Compare) -> None:
15321507
return
15331508

15341509
comparator = node.comparators[0]
1535-
if isinstance(comparator, ast.Str):
1510+
if isinstance(comparator, ast.Constant) and type(comparator.value) is str:
15361511
# other values are possible but we don't need them right now
15371512
# this protects against typos
1538-
if comparator.s not in ("linux", "win32", "cygwin", "darwin"):
1539-
self.error(node, Y008.format(platform=comparator.s))
1513+
if comparator.value not in {"linux", "win32", "cygwin", "darwin"}:
1514+
self.error(node, Y008.format(platform=comparator.value))
15401515
else:
15411516
self.error(node, Y007)
15421517

@@ -1562,8 +1537,10 @@ def visit_ClassDef(self, node: ast.ClassDef) -> None:
15621537
# empty class body should contain "..." not "pass"
15631538
if len(node.body) == 1:
15641539
statement = node.body[0]
1565-
if isinstance(statement, ast.Expr) and isinstance(
1566-
statement.value, ast.Ellipsis
1540+
if (
1541+
isinstance(statement, ast.Expr)
1542+
and isinstance(statement.value, ast.Constant)
1543+
and statement.value.value is ...
15671544
):
15681545
return
15691546
elif isinstance(statement, ast.Pass):
@@ -1575,8 +1552,10 @@ def visit_ClassDef(self, node: ast.ClassDef) -> None:
15751552
if isinstance(statement, ast.Pass):
15761553
self.error(statement, Y012)
15771554
# "..." should not be used in non-empty class body
1578-
elif isinstance(statement, ast.Expr) and isinstance(
1579-
statement.value, ast.Ellipsis
1555+
elif (
1556+
isinstance(statement, ast.Expr)
1557+
and isinstance(statement.value, ast.Constant)
1558+
and statement.value.value is ...
15801559
):
15811560
self.error(statement, Y013)
15821561

@@ -1904,11 +1883,12 @@ def _visit_function(self, node: ast.FunctionDef | ast.AsyncFunctionDef) -> None:
19041883
# normally, should just be "..."
19051884
if isinstance(statement, ast.Pass):
19061885
self.error(statement, Y009)
1907-
# Ellipsis is fine. Str (docstrings) is not but we produce
1908-
# tailored error message for it elsewhere.
1886+
# ... is fine. Docstrings are not but we produce
1887+
# tailored error message for them elsewhere.
19091888
elif not (
19101889
isinstance(statement, ast.Expr)
1911-
and isinstance(statement.value, (ast.Ellipsis, ast.Str))
1890+
and isinstance(statement.value, ast.Constant)
1891+
and isinstance(statement.value.value, (str, type(...)))
19121892
):
19131893
self.error(statement, Y010)
19141894

0 commit comments

Comments
 (0)