Skip to content

Instantly share code, notes, and snippets.

@garthk
Last active September 3, 2024 07:01
Show Gist options
  • Save garthk/1b95aa165968dd72cd1ed786450d4e50 to your computer and use it in GitHub Desktop.
Save garthk/1b95aa165968dd72cd1ed786450d4e50 to your computer and use it in GitHub Desktop.
Remove annotations from scripts so they work on Python 3.6
from __future__ import annotations
import ast
import sys
from pathlib import Path
__all__ = ["remove_annotations"]
class AnnotationRemovalTransformer(ast.NodeTransformer):
"""Remove annotations for Python 3.6 compatibility."""
def visit_AnnAssign(self, node: ast.AnnAssign) -> Optional[ast.Assign]:
if node.value:
return ast.Assign([node.target], node.value)
return None
def visit_ImportFrom(self, node: ast.ImportFrom) -> Optional[ast.ImportFrom]:
if node.module != "__future__":
return node
node.names = [alias for alias in node.names if alias.name != "annotations"]
return node if node.names else None
def generic_visit(self, node: ast.AST) -> ast.AST:
node = super().generic_visit(node)
if hasattr(node, "returns"):
node.returns = None # type: ignore[attr-defined]
if hasattr(node, "annotation"):
node.annotation = None # type: ignore[attr-defined]
if hasattr(node, "body") and not node.body: # type: ignore[attr-defined]
node.body.append(ast.Pass()) # type: ignore[attr-defined]
return node
def remove_annotations(raw_source: Path) -> str:
"""Remove annotations from Python source code."""
raw_ast = ast.parse(raw_source)
clean_ast = ast.fix_missing_locations(AnnotationRemovalTransformer().visit(raw_ast))
return ast.unparse(clean_ast) + "\n"
if __name__ == "__main__":
sys.stdout.write(remove_annotations(sys.stdin.read()))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment