@@ -150,6 +150,13 @@ def _expr_as_dict(expression: Expr, **kwargs: Any) -> dict[str, Any]:
150150 return fields
151151
152152
153+ _modern_types = {
154+ "typing.Tuple" : "tuple" ,
155+ "typing.Dict" : "dict" ,
156+ "typing.List" : "list" ,
157+ "typing.Set" : "set" ,
158+ }
159+
153160# YORE: EOL 3.9: Remove block.
154161_dataclass_opts : dict [str , bool ] = {}
155162if sys .version_info >= (3 , 10 ):
@@ -265,6 +272,11 @@ def iterate(self, *, flat: bool = True) -> Iterator[str | Expr]:
265272 yield "."
266273 yield from _yield (value , flat = flat , outer_precedence = precedence )
267274
275+ def modernize (self ) -> ExprName | ExprAttribute :
276+ if modern := _modern_types .get (self .canonical_path ):
277+ return ExprName (modern , parent = self .last .parent )
278+ return self
279+
268280 def append (self , value : ExprName ) -> None :
269281 """Append a name to this attribute.
270282
@@ -716,6 +728,11 @@ def __eq__(self, other: object) -> bool:
716728 def iterate (self , * , flat : bool = True ) -> Iterator [ExprName ]: # noqa: ARG002
717729 yield self
718730
731+ def modernize (self ) -> ExprName :
732+ if modern := _modern_types .get (self .canonical_path ):
733+ return ExprName (modern , parent = self .parent )
734+ return self
735+
719736 @property
720737 def path (self ) -> str :
721738 """The full, resolved name.
@@ -878,7 +895,7 @@ class ExprSubscript(Expr):
878895
879896 left : str | Expr
880897 """Left part."""
881- slice : Expr
898+ slice : str | Expr
882899 """Slice part."""
883900
884901 def iterate (self , * , flat : bool = True ) -> Iterator [str | Expr ]:
@@ -888,6 +905,33 @@ def iterate(self, *, flat: bool = True) -> Iterator[str | Expr]:
888905 yield from _yield (self .slice , flat = flat , outer_precedence = _OperatorPrecedence .NONE )
889906 yield "]"
890907
908+ @staticmethod
909+ def _to_binop (elements : Sequence [Expr ], op : str ) -> ExprBinOp :
910+ if len (elements ) == 2 : # noqa: PLR2004
911+ left , right = elements
912+ if isinstance (left , Expr ):
913+ left = left .modernize ()
914+ if isinstance (right , Expr ):
915+ right = right .modernize ()
916+ return ExprBinOp (left = left , operator = op , right = right )
917+
918+ left = ExprSubscript ._to_binop (elements [:- 1 ], op = op )
919+ right = elements [- 1 ]
920+ if isinstance (right , Expr ):
921+ right = right .modernize ()
922+ return ExprBinOp (left = left , operator = op , right = right )
923+
924+ def modernize (self ) -> ExprBinOp | ExprSubscript :
925+ if self .canonical_path == "typing.Union" :
926+ return self ._to_binop (self .slice .elements , op = "|" ) # type: ignore[union-attr]
927+ if self .canonical_path == "typing.Optional" :
928+ left = self .slice if isinstance (self .slice , str ) else self .slice .modernize ()
929+ return ExprBinOp (left = left , operator = "|" , right = "None" )
930+ return ExprSubscript (
931+ left = self .left if isinstance (self .left , str ) else self .left .modernize (),
932+ slice = self .slice if isinstance (self .slice , str ) else self .slice .modernize (),
933+ )
934+
891935 @property
892936 def path (self ) -> str :
893937 """The path of this subscript's left part."""
@@ -922,6 +966,12 @@ def iterate(self, *, flat: bool = True) -> Iterator[str | Expr]:
922966 if not self .implicit :
923967 yield ")"
924968
969+ def modernize (self ) -> ExprTuple :
970+ return ExprTuple (
971+ elements = [el if isinstance (el , str ) else el .modernize () for el in self .elements ],
972+ implicit = self .implicit ,
973+ )
974+
925975
926976# YORE: EOL 3.9: Replace `**_dataclass_opts` with `slots=True` within line.
927977@dataclass (eq = True , ** _dataclass_opts )
0 commit comments