Skip to content

Commit ae3a477

Browse files
authored
Add parser deps to rustpython_ast_pyo3 (#75)
1 parent 531aeb3 commit ae3a477

File tree

5 files changed

+192
-0
lines changed

5 files changed

+192
-0
lines changed

ast-pyo3/.gitignore

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/target
2+
3+
# Byte-compiled / optimized / DLL files
4+
__pycache__/
5+
.pytest_cache/
6+
*.py[cod]
7+
8+
# C extensions
9+
*.so
10+
11+
# Distribution / packaging
12+
.Python
13+
.venv/
14+
env/
15+
bin/
16+
build/
17+
develop-eggs/
18+
dist/
19+
eggs/
20+
lib/
21+
lib64/
22+
parts/
23+
sdist/
24+
var/
25+
include/
26+
man/
27+
venv/
28+
*.egg-info/
29+
.installed.cfg
30+
*.egg
31+
32+
# Installer logs
33+
pip-log.txt
34+
pip-delete-this-directory.txt
35+
pip-selfcheck.json
36+
37+
# Unit test / coverage reports
38+
htmlcov/
39+
.tox/
40+
.coverage
41+
.cache
42+
nosetests.xml
43+
coverage.xml
44+
45+
# Translations
46+
*.mo
47+
48+
# Mr Developer
49+
.mr.developer.cfg
50+
.project
51+
.pydevproject
52+
53+
# Rope
54+
.ropeproject
55+
56+
# Django stuff:
57+
*.log
58+
*.pot
59+
60+
.DS_Store
61+
62+
# Sphinx documentation
63+
docs/_build/
64+
65+
# PyCharm
66+
.idea/
67+
68+
# VSCode
69+
.vscode/
70+
71+
# Pyenv
72+
.python-version

ast-pyo3/Cargo.toml

+5
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,13 @@ edition = "2021"
88
# It reimplements AST types, but currently both slower than python AST types and limited to use in other API
99
wrapper = []
1010

11+
[lib]
12+
name = "rustpython_ast"
13+
crate-type = ["cdylib"]
14+
1115
[dependencies]
1216
rustpython-ast = { workspace = true, features = ["location"] }
17+
rustpython-parser = { workspace = true }
1318
num-complex = { workspace = true }
1419
once_cell = { workspace = true }
1520

ast-pyo3/pyproject.toml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[build-system]
2+
requires = ["maturin>=0.15,<0.16"]
3+
build-backend = "maturin"
4+
5+
[project]
6+
name = "rustpython_ast"
7+
requires-python = ">=3.7"
8+
classifiers = [
9+
"Programming Language :: Rust",
10+
"Programming Language :: Python :: Implementation :: CPython",
11+
]
12+
13+
14+
[tool.maturin]
15+
features = ["pyo3/extension-module"]

ast-pyo3/src/lib.rs

+44
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,47 @@ mod py_ast;
33
pub mod wrapper;
44

55
pub use py_ast::{init, PyNode, ToPyAst};
6+
use pyo3::prelude::*;
7+
use rustpython_parser::ast::{source_code::SourceLocator, Fold};
8+
9+
#[pyfunction]
10+
#[pyo3(signature = (source, filename="<unknown>", *, type_comments=false, locate=true))]
11+
pub fn parse<'py>(
12+
source: &str,
13+
filename: &str,
14+
type_comments: bool,
15+
locate: bool,
16+
py: Python<'py>,
17+
) -> PyResult<&'py PyAny> {
18+
if type_comments {
19+
todo!("'type_comments' is not implemented yet");
20+
}
21+
let parsed = rustpython_parser::parse(source, rustpython_parser::Mode::Module, filename)
22+
.map_err(|e| PyErr::new::<pyo3::exceptions::PySyntaxError, _>(e.to_string()))?;
23+
if locate {
24+
let parsed = SourceLocator::new(source).fold(parsed).unwrap();
25+
parsed.module().unwrap().to_py_ast(py)
26+
} else {
27+
parsed.module().unwrap().to_py_ast(py)
28+
}
29+
}
30+
31+
#[pymodule]
32+
fn rustpython_ast(py: Python, m: &PyModule) -> PyResult<()> {
33+
py_ast::init(py)?;
34+
35+
#[cfg(feature = "wrapper")]
36+
{
37+
let ast = PyModule::new(py, "ast")?;
38+
wrapper::located::add_to_module(py, ast)?;
39+
m.add_submodule(ast)?;
40+
41+
let ast = PyModule::new(py, "unlocated_ast")?;
42+
wrapper::ranged::add_to_module(py, ast)?;
43+
m.add_submodule(ast)?;
44+
}
45+
46+
m.add_function(wrap_pyfunction!(parse, m)?)?;
47+
48+
Ok(())
49+
}

ast-pyo3/test_ast.py

+56
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import re
2+
import difflib
3+
import pytest
4+
5+
import ast as py_ast
6+
import rustpython_ast as rust_ast
7+
8+
9+
from glob import glob
10+
11+
files = {}
12+
for path in glob("../../cpython/Lib/**/*.py"):
13+
try:
14+
txt = open(path, "r").read()
15+
except UnicodeDecodeError:
16+
continue
17+
18+
# try:
19+
# if py_ast.dump(py_ast.parse(txt)) != py_ast.dump(rust_ast.parse(txt)):
20+
# continue
21+
# except SyntaxError:
22+
# continue
23+
files[path] = txt
24+
25+
26+
@pytest.mark.parametrize("path", files.keys())
27+
def test_roundtrip(path):
28+
txt = files[path]
29+
module_p = py_ast.parse(txt)
30+
dump_p = py_ast.dump(module_p, indent=True)
31+
module_r = rust_ast.parse(txt)
32+
dump_r = py_ast.dump(module_r, indent=True)
33+
p = re.compile("object at 0x[0-9a-f]+")
34+
dump_p2 = re.sub(p, "object at 0x????????", dump_p)
35+
dump_r2 = re.sub(p, "object at 0x????????", dump_r)
36+
try:
37+
assert dump_p2 == dump_r2
38+
except AssertionError:
39+
last_sign = ' '
40+
for s in difflib.ndiff(dump_p2, dump_r2):
41+
if s[0]==' ': continue
42+
if s[0] == last_sign:
43+
print(s[2:], end='')
44+
else:
45+
print()
46+
print(s, end='')
47+
last_sign = s[0]
48+
# with open("dump_code.py", "w") as f:
49+
# f.write(path)
50+
# f.write('\n')
51+
# f.write(txt)
52+
# with open("dump_p.txt", "w") as f:
53+
# f.write(dump_p2)
54+
# with open("dump_r.txt", "w") as f:
55+
# f.write(dump_r2)
56+
raise

0 commit comments

Comments
 (0)