
Python has a built in ast module that turns source code into a tree of nodes
you can inspect, transform, and walk. It is the foundation for linters,
formatters, and all sorts of code analysis tools in the Python ecosystem. There
has never been a good equivalent for people working in a JavaScript or
TypeScript environment who need to understand Python code without shelling out
to a Python process. That is the gap PyAST fills.
What is PyAST
PyAST is a TypeScript library that parses Python source code and produces an
Abstract Syntax Tree that mirrors the shape of CPython’s own ast module,
following the same
ASDL grammar specification Python
itself is built on. Node types, field names, and position metadata like
lineno, col_offset, end_lineno, and end_col_offset all match what you
would get from ast.parse() in Python.
It is not just a one way parser either. PyAST can also convert an AST back into
Python source code with unparse(), which makes round trip parsing,
transforming, and regenerating code possible entirely in JavaScript.
You can try it directly in the browser at pyast.nepcodex.com, and the source is on GitHub at github.com/kriss-u/py-ast.
Why build a Python parser in TypeScript
Most tools that need to understand Python source code end up spawning a Python subprocess, which means dragging a Python runtime into a Node.js or browser environment, managing IPC, and dealing with startup overhead. For tools that live natively in JavaScript, editors, static analysis tools, educational playgrounds, code transformation pipelines, that dependency is awkward at best.
PyAST removes that dependency entirely. It is a pure TypeScript implementation with no native bindings and no subprocess, so it runs anywhere JavaScript runs, including directly in the browser.
What it can do
At its core, PyAST covers the same ground as Python’s ast module.
- Lexical analysis - a full tokenizer for Python source, including f-strings, indentation sensitive blocks, and the usual set of operators and literals.
- AST generation -
parse()builds a complete tree for any valid Python source, whether it is a bare expression, a single statement, or a full module. - Code generation -
unparse()converts an AST back into readable Python source. - Traversal -
walk()iterates over every node in a tree, andNodeVisitorandNodeTransformergive you a structured way to visit or rewrite specific node types. - JSON serialization - dump an AST to JSON for storage or for feeding into other tools.
- Safe literal evaluation -
literalEval()evaluates Python literals (numbers, strings, lists, dicts, tuples, sets, booleans,None) without executing arbitrary code, similar toast.literal_eval().
Parsing looks about the way you would expect if you have used Python’s own ast
module:
import { parse, unparse } from "py-ast";
const pythonCode = `
def fibonacci(n):
if n <= 1:
return n
else:
return fibonacci(n-1) + fibonacci(n-2)
result = fibonacci(10)
`;
const ast = parse(pythonCode);
console.log(ast.nodeType); // "Module"
const regeneratedCode = unparse(ast);
console.log(regeneratedCode);
Traversal follows the same visitor pattern Python developers already know from
ast.NodeVisitor:
import { NodeVisitor, parse } from "py-ast";
class CodeAnalyzer extends NodeVisitor {
functions: string[] = [];
classes: string[] = [];
visitFunctionDef(node: any) {
this.functions.push(node.name);
this.genericVisit(node);
}
visitClassDef(node: any) {
this.classes.push(node.name);
this.genericVisit(node);
}
}
const analyzer = new CodeAnalyzer();
analyzer.visit(parse(pythonCode));
console.log(analyzer.functions);
console.log(analyzer.classes);
And NodeTransformer lets you rewrite parts of the tree and get valid Python
back out through unparse(), which is the pattern you would reach for to build
a small code refactoring tool or a linter with autofix support.
PyAST also handles the parts of Python that tend to trip up naive parsers, comprehensions, async/await, decorators, f-strings, and optional comment parsing that attaches hash comments to the AST with their line numbers.
Try it in the playground
Reading about a parser is one thing, seeing it work is another. The playground runs PyAST directly in the browser. Paste in Python code and you get the parsed AST, the JSON representation, and the unparsed output, all live, with no backend involved.
It is a fast way to check how a particular piece of syntax gets represented in
the tree, or to confirm that a round trip through parse() and unparse()
preserves the behavior you expect.
Installing it
PyAST is published on npm and JSR as py-ast:
npm install py-ast
import {
literalEval,
NodeTransformer,
NodeVisitor,
parse,
unparse,
walk,
} from "py-ast";
Full API documentation is available at kriss-u.github.io/py-ast.
Where this is useful
The direct motivation was tooling: linters, static analyzers, and educational tools that want to reason about Python code without shipping a Python interpreter alongside them. A browser based Python teaching tool can show a live AST as a student types. A JavaScript based build pipeline can statically check Python scripts without invoking a subprocess. A code migration tool can parse, transform, and regenerate Python source entirely within a Node.js process.
Try it yourself
Head over to the playground and paste in some Python code to see the AST come out live. If you want to use it in a project, install it from npm, or dig into the source and contribute on GitHub.
Feedback and contributions are always welcome.




