-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* docs: add mkdocs * docs: add mike + automatic push from main
- Loading branch information
Showing
11 changed files
with
1,077 additions
and
9 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
name: Documentation | ||
|
||
on: | ||
workflow_dispatch: | ||
push: | ||
branches: [main] | ||
|
||
permissions: | ||
contents: write | ||
|
||
jobs: | ||
Documentation: | ||
runs-on: ubuntu-latest | ||
steps: | ||
- uses: actions/checkout@v4 | ||
- uses: actions/setup-python@v5 | ||
with: | ||
python-version: "3.12" | ||
|
||
- name: Install poetry | ||
run: | | ||
pip install poetry | ||
poetry install --only docs | ||
- name: Set up Git | ||
run: | | ||
git config user.name ${{ github.actor }} | ||
git config user.email ${{ github.actor }}@users.noreply.github.com | ||
- name: Build documentation | ||
run: | | ||
git fetch origin gh-pages | ||
poetry run mike delete main | ||
poetry run mike deploy --push main |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,3 +23,6 @@ coverage.xml | |
# Data | ||
data/ | ||
*.db | ||
|
||
# MkDocs | ||
site |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
# Changelog |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
# API Reference | ||
|
||
::: persil | ||
options: | ||
show_source: true |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,146 @@ | ||
# Overview | ||
|
||
Persil is a pure-python parsing library that draws much (most, let's be honest) | ||
of its inspiration from the excellent [Parsy](https://github.com/python-parsy/parsy) library. | ||
|
||
Hence the name, "Persil" ([pɛʁ.sil] or [pɛʁ.si]), the French word for parsley | ||
-a most questionable pun on `Parsy -> Parsley -> Persil`, | ||
in case anyone missed it. | ||
|
||
Like Parsy, Persil is a _"monadic parser combinator library for LL(infinity) grammars"_. | ||
As a rough approximation, you can think of Persil as a typed "fork" of Parsy. | ||
However, although the APIs are very similar, there are [notable differences](#compatibility-with-parsy) | ||
that you might want to review if you're coming from Parsy. | ||
|
||
If you're merely looking for a _somewhat_ type-aware version of Parsy, you may be looking for | ||
`parsy-stubs`. Mypy can use it to infer most of the types, but you'll find that | ||
shortcuts had to be taken in many cases. | ||
|
||
## Getting started | ||
|
||
Persil is a pure-Python library. You can install it with pip: | ||
|
||
```shell | ||
pip install persil | ||
``` | ||
|
||
Then, you can play with persil much the same way you would with Parsy, | ||
and enjoy the great developer experience that type-hinting brings to Persil. | ||
|
||
### A basic example | ||
|
||
```python | ||
from persil import regex | ||
|
||
year = regex(r"\d{4}").map(int) | ||
``` | ||
|
||
This example should drive home the point that Persil is heavily inspired by Parsy. | ||
The only difference in this particular case is type-safety: | ||
the persil version knows that `year` is a parser that expects | ||
a `str`, and outputs an `int`. | ||
|
||
### More complex parsers | ||
|
||
Parsy uses generator functions as a most elegant solution to define complex parser. | ||
|
||
While you can still use this approach with Persil, you're encouraged to favour | ||
the `from_streamer` decorator: | ||
|
||
```python | ||
@from_streamer | ||
def parser( | ||
stream: Stream[str], | ||
) -> CustomType: | ||
a = stream(parser_a) | ||
b = stream(parser_b) | ||
c = stream(parser_c) | ||
|
||
return CustomType(a, b, c) | ||
``` | ||
|
||
The equivalent code, using `generate` instead (deprecated in Persil): | ||
|
||
```python | ||
@generate | ||
def parser() -> Generator[Parser, Any, CustomType]: | ||
a = yield parser_a | ||
b = yield parser_b | ||
c = yield parser_c | ||
|
||
return CustomType(a, b, c) | ||
``` | ||
|
||
The main issue with `generate` is that intermediate parsers cannot be typed, | ||
whereas `Stream.__call__` plays nice with modern Python tooling like mypy. | ||
|
||
## Relation with Parsy | ||
|
||
First of all, I am not affiliated in any way with the Parsy project. | ||
|
||
### Rationale | ||
|
||
Parsy's last commit is from a year ago at the time of writing. Moreover, although the authors | ||
have started some development to propose a typed version of their library, efforts | ||
in that area have stalled for two years. | ||
|
||
### Compatibility with Parsy | ||
|
||
Although Persil draws most of its inspiration from Parsy, maintaining a one-for-one | ||
equivalence with the latter's API **is NOT among Persil's goal**. | ||
|
||
For those coming from Parsy, here are some notable differences: | ||
|
||
- the `Result` type is now a union between `Ok` and `Err`, which allow for a more type-safe API. | ||
- `Err` is its own error: it inherits from `Exception` and can be raised. | ||
- Persil introduces the `Stream` class, a wrapper around the input that can apply parsers sequentially, | ||
keeping track of the book-keeping. | ||
|
||
## Performance tips | ||
|
||
Since Persil takes a functional approach, every transformation on a parser produces a new parser. | ||
With that in mind, the way you define/use/combine parsers may substantially affect performance. | ||
|
||
Consider the following example: | ||
|
||
```python | ||
from datetime import datetime | ||
|
||
from persil import Stream, from_stream, regex, string | ||
|
||
|
||
@from_stream | ||
def datetime_parser(stream: Stream[str]) -> datetime: | ||
year = stream.apply(regex(r"\d{4}").map(int)) | ||
stream.apply(string("/")) | ||
month = stream.apply(regex(r"\d{2}").map(int)) | ||
stream.apply(string("/")) | ||
day = stream.apply(regex(r"\d{2}").map(int)) | ||
return datetime(year, month, day) | ||
``` | ||
|
||
The resulting `datetime_parser` will re-create three new regex parsers **every time** it is run. | ||
|
||
A much better alternative: | ||
|
||
```python | ||
from datetime import datetime | ||
|
||
from persil import Stream, from_stream, regex, string | ||
|
||
|
||
year_parser = regex(r"\d{4}").map(int) | ||
day_month_parser = regex(r"\d{2}").map(int) | ||
slash_parser = string("/") | ||
|
||
@from_stream | ||
def datetime_parser(stream: Stream[str]) -> datetime: | ||
year = stream.apply(year_parser) | ||
stream.apply(slash_parser) | ||
month = stream.apply(day_month_parser) | ||
stream.apply(slash_parser) | ||
day = stream.apply(day_month_parser) | ||
return datetime(year, month, day) | ||
``` | ||
|
||
That way, the lower-level parsers are only defined once. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
from pathlib import Path | ||
|
||
from mkdocs.config import Config | ||
from mkdocs.structure.files import File, Files | ||
from mkdocs.structure.pages import Page | ||
|
||
ADD_TO_DOCS = [ | ||
"changelog.md", | ||
] | ||
VIRTUAL_FILES = dict[str, str]() | ||
|
||
|
||
def on_files(files: Files, config: Config): | ||
""" | ||
Add virtual files. | ||
""" | ||
|
||
all_files = [file for file in files] | ||
|
||
for path in ADD_TO_DOCS: | ||
content = Path(path).read_text() | ||
VIRTUAL_FILES[path] = content | ||
|
||
file = File( | ||
path, | ||
config["docs_dir"], | ||
config["site_dir"], | ||
config["use_directory_urls"], | ||
) | ||
all_files.append(file) | ||
|
||
return Files(all_files) | ||
|
||
|
||
def on_page_read_source(page: Page, config: Config) -> str | None: | ||
if page.file.src_path in VIRTUAL_FILES: | ||
return VIRTUAL_FILES[page.file.src_path] | ||
return None |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,52 @@ | ||
site_name: Persil | ||
|
||
repo_url: https://github.com/bdura/persil | ||
|
||
theme: | ||
name: material | ||
palette: | ||
- scheme: default | ||
toggle: | ||
icon: material/brightness-4 | ||
name: Switch to dark mode | ||
- scheme: slate | ||
toggle: | ||
icon: material/brightness-7 | ||
name: Switch to light mode | ||
|
||
markdown_extensions: | ||
- admonition | ||
- pymdownx.superfences | ||
- pymdownx.highlight | ||
- pymdownx.inlinehilite | ||
- pymdownx.snippets | ||
- pymdownx.tabbed: | ||
alternate_style: true | ||
- footnotes | ||
|
||
nav: | ||
- index.md | ||
- api-reference.md | ||
- changelog.md | ||
|
||
watch: | ||
- persil/ | ||
|
||
plugins: | ||
- search | ||
- mkdocstrings: | ||
handlers: | ||
python: | ||
options: | ||
allow_inspection: true | ||
docstring_style: numpy | ||
docstring_section_style: spacy | ||
heading_level: 2 | ||
members_order: source | ||
show_bases: false | ||
show_signature: false | ||
merge_init_into_class: true | ||
- mike | ||
|
||
hooks: | ||
- docs/scripts/plugin.py |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.