11#!/usr/bin/env python3
2+ from __future__ import annotations
3+
24import argparse
35import ast
46import importlib
911import textwrap
1012import traceback
1113from collections import defaultdict
12- from typing import Any , Dict , Iterator , List , Optional , Set , Tuple , cast
14+ from typing import Any , Iterator , cast
1315
1416__all__ = ["pypprint" ]
1517__version__ = "1.3.0"
@@ -51,17 +53,17 @@ class NameFinder(ast.NodeVisitor):
5153 """
5254
5355 def __init__ (self , * trees : ast .AST ) -> None :
54- self ._scopes : List [ Set [str ]] = [set ()]
55- self ._comprehension_scopes : List [int ] = []
56+ self ._scopes : list [ set [str ]] = [set ()]
57+ self ._comprehension_scopes : list [int ] = []
5658
57- self .undefined : Set [str ] = set ()
58- self .wildcard_imports : List [str ] = []
59+ self .undefined : set [str ] = set ()
60+ self .wildcard_imports : list [str ] = []
5961 for tree in trees :
6062 self .visit (tree )
6163 assert len (self ._scopes ) == 1
6264
6365 @property
64- def top_level_defined (self ) -> Set [str ]:
66+ def top_level_defined (self ) -> set [str ]:
6567 return self ._scopes [0 ]
6668
6769 def flexible_visit (self , value : Any ) -> None :
@@ -73,7 +75,7 @@ def flexible_visit(self, value: Any) -> None:
7375 self .visit (value )
7476
7577 def generic_visit (self , node : ast .AST ) -> None :
76- def order (f_v : Tuple [str , Any ]) -> int :
78+ def order (f_v : tuple [str , Any ]) -> int :
7779 # This ordering fixes comprehensions, dict comps, loops, assignments
7880 return {"generators" : - 3 , "iter" : - 3 , "key" : - 2 , "value" : - 1 }.get (f_v [0 ], 0 )
7981
@@ -138,7 +140,7 @@ def visit_ClassDef(self, node: ast.ClassDef) -> None:
138140 # Classes are not okay with self-reference, so define ``name`` afterwards
139141 self ._scopes [- 1 ].add (node .name )
140142
141- def visit_function_helper (self , node : Any , name : Optional [ str ] = None ) -> None :
143+ def visit_function_helper (self , node : Any , name : str | None = None ) -> None :
142144 # Functions are okay with recursion, but not self-reference while defining default values
143145 self .flexible_visit (node .args )
144146 if name is not None :
@@ -247,22 +249,22 @@ def __init__(self) -> None:
247249 raise PypError (f"Config has invalid syntax{ error } " ) from e
248250
249251 # List of config parts
250- self .parts : List [ast .stmt ] = config_ast .body
252+ self .parts : list [ast .stmt ] = config_ast .body
251253 # Maps from a name to index of config part that defines it
252- self .name_to_def : Dict [str , int ] = {}
253- self .def_to_names : Dict [int , List [str ]] = defaultdict (list )
254+ self .name_to_def : dict [str , int ] = {}
255+ self .def_to_names : dict [int , list [str ]] = defaultdict (list )
254256 # Maps from index of config part to undefined names it needs
255- self .requires : Dict [int , Set [str ]] = defaultdict (set )
257+ self .requires : dict [int , set [str ]] = defaultdict (set )
256258 # Modules from which automatic imports work without qualification, ordered by AST encounter
257- self .wildcard_imports : List [str ] = []
259+ self .wildcard_imports : list [str ] = []
258260
259261 self .shebang : str = "#!/usr/bin/env python3"
260262 if config_contents .startswith ("#!" ):
261263 self .shebang = "\n " .join (
262264 itertools .takewhile (lambda line : line .startswith ("#" ), config_contents .splitlines ())
263265 )
264266
265- top_level : Tuple [Any , ...] = (ast .FunctionDef , ast .AsyncFunctionDef , ast .ClassDef )
267+ top_level : tuple [Any , ...] = (ast .FunctionDef , ast .AsyncFunctionDef , ast .ClassDef )
266268 top_level += (ast .Import , ast .ImportFrom , ast .Assign , ast .AnnAssign , ast .If , ast .Try )
267269 for index , part in enumerate (self .parts ):
268270 if not isinstance (part , top_level ):
@@ -298,13 +300,13 @@ class PypTransform:
298300
299301 def __init__ (
300302 self ,
301- before : List [str ],
302- code : List [str ],
303- after : List [str ],
303+ before : list [str ],
304+ code : list [str ],
305+ after : list [str ],
304306 define_pypprint : bool ,
305307 config : PypConfig ,
306308 ) -> None :
307- def parse_input (code : List [str ]) -> ast .Module :
309+ def parse_input (code : list [str ]) -> ast .Module :
308310 try :
309311 return ast .parse (textwrap .dedent ("\n " .join (code ).strip ()))
310312 except SyntaxError as e :
@@ -326,9 +328,9 @@ def parse_input(code: List[str]) -> ast.Module:
326328 raise PypError ("Config __pyp_after__ not supported" )
327329
328330 f = NameFinder (self .before_tree , self .tree , self .after_tree )
329- self .defined : Set [str ] = f .top_level_defined
330- self .undefined : Set [str ] = f .undefined
331- self .wildcard_imports : List [str ] = f .wildcard_imports
331+ self .defined : set [str ] = f .top_level_defined
332+ self .undefined : set [str ] = f .undefined
333+ self .wildcard_imports : list [str ] = f .wildcard_imports
332334 # We'll always use sys in ``build_input``, so add it to undefined.
333335 # This lets config define it or lets us automatically import it later
334336 # (If before defines it, we'll just let it override the import...)
@@ -338,11 +340,11 @@ def parse_input(code: List[str]) -> ast.Module:
338340 self .config = config
339341
340342 # The print statement ``build_output`` will add, if it determines it needs to.
341- self .implicit_print : Optional [ ast .Call ] = None
343+ self .implicit_print : ast .Call | None = None
342344
343345 def build_missing_config (self ) -> None :
344346 """Modifies the AST to define undefined names defined in config."""
345- config_definitions : Set [str ] = set ()
347+ config_definitions : set [str ] = set ()
346348 attempt_to_define = set (self .undefined )
347349 while attempt_to_define :
348350 can_define = attempt_to_define & set (self .config .name_to_def )
@@ -406,7 +408,7 @@ def build_output(self) -> None:
406408 if self .undefined & {"print" , "pprint" , "pp" , "pypprint" }: # has an explicit print
407409 return
408410
409- def inner (body : List [ast .stmt ], use_pypprint : bool = False ) -> bool :
411+ def inner (body : list [ast .stmt ], use_pypprint : bool = False ) -> bool :
410412 if not body :
411413 return False
412414 if isinstance (body [- 1 ], ast .Pass ):
@@ -642,7 +644,7 @@ def run_pyp(args: argparse.Namespace) -> None:
642644 # On error, reconstruct a traceback into the generated code
643645 # Also add some diagnostics for ModuleNotFoundError and NameError
644646 try :
645- line_to_node : Dict [int , ast .AST ] = {}
647+ line_to_node : dict [int , ast .AST ] = {}
646648 for node in dfs_walk (tree ):
647649 line_to_node .setdefault (getattr (node , "lineno" , - 1 ), node )
648650
@@ -699,7 +701,7 @@ def code_for_line(lineno: int) -> str:
699701 ) from e
700702
701703
702- def parse_options (args : List [str ]) -> argparse .Namespace :
704+ def parse_options (args : list [str ]) -> argparse .Namespace :
703705 parser = argparse .ArgumentParser (
704706 prog = "pyp" ,
705707 formatter_class = argparse .RawDescriptionHelpFormatter ,
0 commit comments