Skip to content

Commit d34e992

Browse files
authored
String Reading and Path Typing (#35)
* Python .gitignore * Direcrt string reading and str | Path typing * Mypy CI * PR changes
1 parent a4d14f7 commit d34e992

File tree

5 files changed

+214
-18
lines changed

5 files changed

+214
-18
lines changed

.github/workflows/main.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,10 +41,11 @@ jobs:
4141
- name: Cargo Tests
4242
if: success() || failure()
4343
run: cargo test
44-
- run: pip install dist/*.whl
44+
- run: pip install mypy dist/*.whl
4545
- name: Python Tests
46-
run: python3 simple.py
47-
working-directory: tests
46+
run: python3 tests/simple.py
47+
- name: Typying Check
48+
run: mypy tests/simple.py
4849

4950
get_dynamic_version:
5051
runs-on: ubuntu-latest

.gitignore

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,162 @@
33
**/.DS_Store
44
BUGS
55
**/.env
6+
**/.direnv
7+
# These are backup files generated by rustfmt
8+
**/*.rs.bk
9+
10+
# Byte-compiled / optimized / DLL files
11+
__pycache__/
12+
*.py[cod]
13+
*$py.class
14+
15+
# C extensions
16+
*.so
17+
18+
# Distribution / packaging
19+
.Python
20+
build/
21+
develop-eggs/
22+
dist/
23+
downloads/
24+
eggs/
25+
.eggs/
26+
lib/
27+
lib64/
28+
parts/
29+
sdist/
30+
var/
31+
wheels/
32+
share/python-wheels/
33+
*.egg-info/
34+
.installed.cfg
35+
*.egg
36+
MANIFEST
37+
38+
# PyInstaller
39+
# Usually these files are written by a python script from a template
40+
# before PyInstaller builds the exe, so as to inject date/other infos into it.
41+
*.manifest
42+
*.spec
43+
44+
# Installer logs
45+
pip-log.txt
46+
pip-delete-this-directory.txt
47+
48+
# Unit test / coverage reports
49+
htmlcov/
50+
.tox/
51+
.nox/
52+
.coverage
53+
.coverage.*
54+
.cache
55+
nosetests.xml
56+
coverage.xml
57+
*.cover
58+
*.py,cover
59+
.hypothesis/
60+
.pytest_cache/
61+
cover/
62+
63+
# Translations
64+
*.mo
65+
*.pot
66+
67+
# Django stuff:
68+
*.log
69+
local_settings.py
70+
db.sqlite3
71+
db.sqlite3-journal
72+
73+
# Flask stuff:
74+
instance/
75+
.webassets-cache
76+
77+
# Scrapy stuff:
78+
.scrapy
79+
80+
# Sphinx documentation
81+
docs/_build/
82+
83+
# PyBuilder
84+
.pybuilder/
85+
target/
86+
87+
# Jupyter Notebook
88+
.ipynb_checkpoints
89+
90+
# IPython
91+
profile_default/
92+
ipython_config.py
93+
94+
# pyenv
95+
# For a library or package, you might want to ignore these files since the code is
96+
# intended to run in multiple environments; otherwise, check them in:
97+
# .python-version
98+
99+
# pipenv
100+
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
101+
# However, in case of collaboration, if having platform-specific dependencies or dependencies
102+
# having no cross-platform support, pipenv may install dependencies that don't work, or not
103+
# install all needed dependencies.
104+
#Pipfile.lock
105+
106+
# poetry
107+
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
108+
# This is especially recommended for binary packages to ensure reproducibility, and is more
109+
# commonly ignored for libraries.
110+
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
111+
#poetry.lock
112+
113+
# pdm
114+
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
115+
#pdm.lock
116+
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
117+
# in version control.
118+
# https://pdm.fming.dev/latest/usage/project/#working-with-version-control
119+
.pdm.toml
120+
.pdm-python
121+
.pdm-build/
122+
123+
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
124+
__pypackages__/
125+
126+
# Celery stuff
127+
celerybeat-schedule
128+
celerybeat.pid
129+
130+
# SageMath parsed files
131+
*.sage.py
132+
133+
# Environments
134+
.env
6135
.venv
136+
env/
137+
venv/
138+
ENV/
139+
env.bak/
140+
venv.bak/
141+
142+
# Spyder project settings
143+
.spyderproject
144+
.spyproject
145+
146+
# Rope project settings
147+
.ropeproject
148+
149+
# mkdocs documentation
150+
/site
151+
152+
# mypy
153+
.mypy_cache/
154+
.dmypy.json
155+
dmypy.json
156+
157+
# Pyre type checker
158+
.pyre/
159+
160+
# pytype static type analyzer
161+
.pytype/
162+
163+
# Cython debug symbols
164+
cython_debug/

src/python_wrapper/cvldoc_parser.pyi

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from enum import Enum
2-
from pathlib import Path
3-
from typing import Any, List, Dict, Optional
2+
from os import PathLike
3+
from typing import Any, Dict, List, Optional, Union
44

55
class DocumentationTag:
66
kind: str
@@ -51,4 +51,5 @@ class CvlElement:
5151
def element_returns(self) -> Optional[str]: ...
5252
def element_params(self) -> Optional[List[tuple[str, str]]]: ...
5353

54-
def parse(path: Path) -> List[CvlElement]: ...
54+
def parse(path: Union[str, PathLike]) -> List[CvlElement]: ...
55+
def parse_string(src: str) -> List[CvlElement]: ...

src/python_wrapper/src/lib.rs

Lines changed: 18 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -27,28 +27,35 @@ fn handle_io_error(path: &Path, e: std::io::Error) -> PyErr {
2727
}
2828
}
2929

30-
/// takes a path to a file a(s a string). returns a list of parsed cvldocs,
30+
/// takes a source code as a string. returns a list of parsed cvldocs,
3131
/// or an appropriate error in the case of a failure.
3232
///
3333
/// throws:
34-
/// - `OSError` if file reading failed.
3534
/// - `RuntimeError` if source code parsing failed.
3635
#[pyfunction]
37-
fn parse(py: Python, path: PathBuf) -> PyResult<Vec<CvlElementPy>> {
38-
let src = file_contents(path.as_path())?;
39-
40-
let elements = Builder::new(&src).build().map_err(|_| {
41-
let display = path.display();
42-
let desc = format!("failed to parse source file: {display}");
43-
PyRuntimeError::new_err(desc)
44-
})?;
36+
fn parse_string(py: Python, src: String) -> PyResult<Vec<CvlElementPy>> {
37+
let elements = Builder::new(&src)
38+
.build()
39+
.map_err(|_| PyRuntimeError::new_err("Failed to parse source code"))?;
4540

4641
elements
4742
.into_iter()
4843
.map(|cvl_element| CvlElementPy::new(py, cvl_element))
4944
.collect()
5045
}
5146

47+
/// takes a path to a file a(s a string). returns a list of parsed cvldocs,
48+
/// or an appropriate error in the case of a failure.
49+
///
50+
/// throws:
51+
/// - `OSError` if file reading failed.
52+
/// - `RuntimeError` if source code parsing failed.
53+
#[pyfunction]
54+
fn parse(py: Python, path: PathBuf) -> PyResult<Vec<CvlElementPy>> {
55+
let src = file_contents(path.as_path())?;
56+
parse_string(py, src)
57+
}
58+
5259
#[pymodule]
5360
fn cvldoc_parser(_py: Python, module: &PyModule) -> PyResult<()> {
5461
module.add_class::<CvlElementPy>()?;
@@ -59,6 +66,7 @@ fn cvldoc_parser(_py: Python, module: &PyModule) -> PyResult<()> {
5966
module.add_class::<DocumentationTagPy>()?;
6067

6168
wrap_pyfunction!(parse, module).and_then(|function| module.add_function(function))?;
69+
wrap_pyfunction!(parse_string, module).and_then(|function| module.add_function(function))?;
6270

6371
Ok(())
6472
}

tests/simple.py

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,32 @@
1+
from pathlib import Path
2+
13
import cvldoc_parser
24

3-
parsed = cvldoc_parser.parse("definition_test.spec")
4-
assert len(parsed) == 3, "should parse to 3 elements"
5+
6+
def as_list(
7+
elements: list[cvldoc_parser.CvlElement],
8+
) -> list[tuple[str, str | None, str | None, list[tuple[str, str]] | None]]:
9+
return [
10+
(
11+
x.raw(),
12+
x.element_name(),
13+
x.element_returns(),
14+
x.element_params(),
15+
)
16+
for x in elements
17+
]
18+
19+
20+
spec_file = Path(__file__).parent / "definition_test.spec"
21+
22+
p_file = cvldoc_parser.parse(spec_file)
23+
24+
assert len(p_file) == 3, "should parse to 3 elements"
25+
26+
p_file_as_string = cvldoc_parser.parse(spec_file.as_posix())
27+
p_from_string = cvldoc_parser.parse_string(spec_file.read_text())
28+
29+
p = map(as_list, [p_file, p_file_as_string, p_from_string])
30+
assert all(
31+
x == y for x in p for y in p
32+
), "all three parsers should parse the same elements"

0 commit comments

Comments
 (0)