Skip to content

Use TypeAlias where possible for type aliases - #7630

Merged
JelleZijlstra merged 3 commits into
python:masterfrom
AlexWaygood:typealias
Apr 16, 2022
Merged

Use TypeAlias where possible for type aliases#7630
JelleZijlstra merged 3 commits into
python:masterfrom
AlexWaygood:typealias

Conversation

@AlexWaygood

@AlexWaygoodAlexWaygood commented Apr 16, 2022

Copy link
Copy Markdown
Member

This PR was accomplished using the following steps:

  1. Run the below script on typeshed
  2. Revert a few places where PEP 612 and PEP 604 combined cause some mypy false-positive errors.
  3. Add a few uses of TypeAlias that the script missed, remove a few where the script was overzealous.
  4. Manually review the whole thing.
Script I used for step one:
importastimportreimportsubprocessimportsysfromcontextlibimportcontextmanagerfromdataclassesimportdataclassfromitertoolsimportchainfrompathlibimportPathfromtypesimportModuleTypefromtypingimportIterator, NamedTupleFAILURES= []
@dataclassclassNestingCounter:
"""Class to help the PyiVisitor keep track of internal state"""nesting: int=0@contextmanagerdefenabled(self) ->Iterator[None]:
self.nesting+=1try:
yieldfinally:
self.nesting-=1@propertydefactive(self) ->bool:
"""Determine whether the level of nesting is currently non-zero"""returnbool(self.nesting)
deffix_bad_syntax(path: Path) ->None:
withopen(path) asf:
stub=f.read()
lines=stub.splitlines()
tree=ast.parse(stub)
typealias_import_needed=FalseclassStubVisitor(ast.NodeVisitor):
def__init__(self) ->None:
# Mapping of all assignments in the file that could be type aliases# (This excludes assignments to function calls and ellipses, etc.)self.maybe_typealias_assignments: dict[str, ast.Assign] = {}
# Set of all names and attributes that are used as annotations in the fileself.all_annotations: set[str] =set()
self.in_class=NestingCounter()
defvisit_ClassDef(self, node: ast.ClassDef) ->None:
withself.in_class.enabled():
self.generic_visit(node)
defvisit_AnnAssign(self, node: ast.AnnAssign) ->None:
self.all_annotations.add(ast.unparse(node.annotation))
defvisit_Assign(self, node: ast.Assign) ->None:
nonlocaltypealias_import_neededtarget=node.targets[0]
assertisinstance(target, ast.Name)
target_name=target.idif (self.in_class.activeandtarget_name=="__match_args__") or (
target_name=="__all__"andnotself.in_class.active
):
returnassignment=node.valueifisinstance(assignment, (ast.Ellipsis, ast.Call, ast.Num, ast.Str, ast.Bytes)):
returnifisinstance(assignment, ast.Name):
self.maybe_typealias_assignments[target_name] =nodeelifisinstance(assignment, ast.Attribute):
ifisinstance(assignment.value, ast.Name):
self.maybe_typealias_assignments[target_name] =nodeelse:
typealias_import_needed=Truelines[node.lineno-1] =re.sub(f"{target_name} = ", f"{target_name}: TypeAlias = ", lines[node.lineno-1])
defrun(self, tree: ast.AST) ->None:
nonlocaltypealias_import_neededself.visit(tree)
forannotationinself.all_annotations:
ifannotationinself.maybe_typealias_assignments:
node=self.maybe_typealias_assignments[annotation]
typealias_import_needed=Truelines[node.lineno-1] =re.sub(f"{annotation} = ", f"{annotation}: TypeAlias = ", lines[node.lineno-1])
StubVisitor().visit(tree)
ifnottypealias_import_needed:
returntree=ast.parse("\n".join(lines))
typealias_imported=FalseclassTypeAliasImportFinder(ast.NodeVisitor):
defvisit_ImportFrom(self, node: ast.ImportFrom) ->None:
nonlocaltypealias_importedifnode.module!="typing_extensions":
returnifany(cls.name=="TypeAlias"forclsinnode.names):
typealias_imported=TruereturnTypeAliasImportFinder().visit(tree)
ifnottypealias_imported:
lines= ["from typing_extensions import TypeAlias"] +lineswithopen(path, "w") asf:
f.write("\n".join(lines) +"\n")
defmain() ->None:
print("STARTING RUN: Will attempt to fix new syntax in the stubs directory...\n\n")
forpathinchain(Path("stdlib").rglob("*.pyi"), Path("stubs").rglob("*.pyi")):
if"@python2"inpath.parts:
continueprint(f"Attempting to convert {path} to new syntax.")
fix_bad_syntax(path)
print("\n\nSTARTING ISORT...\n\n")
subprocess.run([sys.executable, "-m", "isort", "."])
print("\n\nSTARTING BLACK...\n\n")
subprocess.run([sys.executable, "-m", "black", "."])
ifFAILURES:
print("\n\nFAILED to convert the following files to new syntax:\n")
forpathinFAILURES:
print(f"- {path}")
else:
print("\n\nThere were ZERO failures in converting to new syntax. HOORAY!!\n\n")
print('\n\nRunning "check_new_syntax.py"...\n\n')
subprocess.run([sys.executable, "tests/check_new_syntax.py"])
if__name__=="__main__":
main()

@github-actions

This comment has been minimized.

@AlexWaygood
AlexWaygood marked this pull request as ready for review April 16, 2022 00:47
@github-actions

Copy link
Copy Markdown
Contributor

According to mypy_primer, this change has no effect on the checked open source code. 🤖🎉

@JelleZijlstraJelleZijlstra left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, thanks for doing all this!

LPWIN32_FIND_DATAW = pointer[WIN32_FIND_DATAW]
PWORD = pointer[WORD]
LPWORD = pointer[WORD]
PBOOL: TypeAlias = pointer[BOOL]

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks a bit odd but seems like they're indeed intended as aliases.

Comment threadstdlib/imaplib.pyi
_AnyResponseData: TypeAlias = list[None] | list[bytes | tuple[bytes, bytes]]

_list = list # conflicts with a method named "list"
_T = TypeVar("_T")

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good catch, though maybe we should do from builtins import list as _list instead

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done in #7634!

Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@AlexWaygood@JelleZijlstra