mirror of
https://github.com/davidhalter/typeshed.git
synced 2025-12-28 06:36:54 +08:00
Check for PEP 604 usage in CI (#5903)
Since this is a common review issue and our stubs have all been converted Co-authored-by: hauntsaninja <>
This commit is contained in:
8
.github/workflows/tests.yml
vendored
8
.github/workflows/tests.yml
vendored
@@ -17,6 +17,14 @@ jobs:
|
||||
- run: pip install toml
|
||||
- run: ./tests/check_consistent.py
|
||||
|
||||
pep-604:
|
||||
name: Check for PEP 604 usage
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: actions/setup-python@v2
|
||||
- run: ./tests/check_pep_604.py
|
||||
|
||||
flake8:
|
||||
name: Lint with flake8
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
75
tests/check_pep_604.py
Executable file
75
tests/check_pep_604.py
Executable file
@@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import ast
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def check_pep_604(tree: ast.AST, path: Path) -> list[str]:
|
||||
errors = []
|
||||
|
||||
class UnionFinder(ast.NodeVisitor):
|
||||
def visit_Subscript(self, node: ast.Subscript) -> None:
|
||||
if (
|
||||
isinstance(node.value, ast.Name)
|
||||
and node.value.id == "Union"
|
||||
and isinstance(node.slice, ast.Tuple)
|
||||
):
|
||||
new_syntax = " | ".join(ast.unparse(x) for x in node.slice.elts)
|
||||
errors.append(
|
||||
(f"{path}:{node.lineno}: Use PEP 604 syntax for Union, e.g. `{new_syntax}`")
|
||||
)
|
||||
if (
|
||||
isinstance(node.value, ast.Name)
|
||||
and node.value.id == "Optional"
|
||||
):
|
||||
new_syntax = f"{ast.unparse(node.slice)} | None"
|
||||
errors.append(
|
||||
(f"{path}:{node.lineno}: Use PEP 604 syntax for Optional, e.g. `{new_syntax}`")
|
||||
)
|
||||
|
||||
# This doesn't check type aliases (or type var bounds, etc), since those are not
|
||||
# currently supported
|
||||
class AnnotationFinder(ast.NodeVisitor):
|
||||
def visit_AnnAssign(self, node: ast.AnnAssign) -> None:
|
||||
UnionFinder().visit(node.annotation)
|
||||
|
||||
def visit_arg(self, node: ast.arg) -> None:
|
||||
if node.annotation is not None:
|
||||
UnionFinder().visit(node.annotation)
|
||||
|
||||
def visit_FunctionDef(self, node: ast.FunctionDef) -> None:
|
||||
if node.returns is not None:
|
||||
UnionFinder().visit(node.returns)
|
||||
self.generic_visit(node)
|
||||
|
||||
def visit_AsyncFunctionDef(self, node: ast.AsyncFunctionDef) -> None:
|
||||
if node.returns is not None:
|
||||
UnionFinder().visit(node.returns)
|
||||
self.generic_visit(node)
|
||||
|
||||
AnnotationFinder().visit(tree)
|
||||
return errors
|
||||
|
||||
|
||||
def main() -> None:
|
||||
errors = []
|
||||
for path in Path(".").glob("**/*.pyi"):
|
||||
if "@python2" in path.parts:
|
||||
continue
|
||||
if "stubs/protobuf/google/protobuf" in str(path): # TODO: fix protobuf stubs
|
||||
continue
|
||||
if "stubs/dateparser/" in str(path): # TODO: fix dateparser
|
||||
continue
|
||||
|
||||
with open(path) as f:
|
||||
tree = ast.parse(f.read())
|
||||
errors.extend(check_pep_604(tree, path))
|
||||
|
||||
if errors:
|
||||
print("\n".join(errors))
|
||||
sys.exit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Reference in New Issue
Block a user