Skip to content

Commit b72142e

Browse files
Toilalclaude
andcommitted
docs: add zensical documentation site published to GitHub Pages
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DgcYQy7Mnh2afkmSg295uU
1 parent 8979cb3 commit b72142e

12 files changed

Lines changed: 959 additions & 1 deletion

File tree

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
name: mkdocs-develop
2+
on:
3+
push:
4+
branches:
5+
- develop
6+
# Per-target group: coalesce rapid develop pushes to the latest build, without
7+
# cancelling the main/preview deploys. Concurrent pushes to disjoint gh-pages
8+
# folders are handled by the deploy action's fetch+rebase+retry.
9+
concurrency:
10+
group: gh-pages-develop
11+
cancel-in-progress: true
12+
jobs:
13+
deploy:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v7
17+
- name: Install uv
18+
uses: astral-sh/setup-uv@v8.2.0
19+
with:
20+
python-version: "3.13"
21+
enable-cache: true
22+
23+
- run: uv run --only-group docs zensical build --strict
24+
25+
- name: Deploy develop preview 🚀
26+
uses: JamesIves/github-pages-deploy-action@v4
27+
with:
28+
token: ${{ secrets.GITHUB_TOKEN }}
29+
branch: gh-pages
30+
folder: site
31+
# Publish the develop build under /dev/ without touching the stable site at the root.
32+
target-folder: dev
33+
clean: true
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
name: mkdocs-preview
2+
on:
3+
pull_request:
4+
types: [opened, synchronize, reopened, closed]
5+
# Per-PR group: a new push (or the close/cleanup) supersedes an in-flight build
6+
# for the same PR, without touching other PRs' or the main/develop deploys.
7+
# Concurrent pushes to disjoint gh-pages folders are handled by the deploy
8+
# action's fetch+rebase+retry.
9+
concurrency:
10+
group: gh-pages-preview-${{ github.head_ref }}
11+
cancel-in-progress: true
12+
permissions:
13+
contents: write
14+
jobs:
15+
deploy:
16+
# Same-repo docs/* PRs only: fork PRs get a read-only token and cannot push to gh-pages.
17+
if: >-
18+
github.event.action != 'closed'
19+
&& github.event.pull_request.head.repo.full_name == github.repository
20+
&& startsWith(github.head_ref, 'docs/')
21+
runs-on: ubuntu-latest
22+
steps:
23+
- uses: actions/checkout@v7
24+
- name: Compute preview slug
25+
id: slug
26+
run: |
27+
name="${GITHUB_HEAD_REF#docs/}"
28+
name="$(printf '%s' "$name" | tr '/' '-' | tr -cd '[:alnum:]._-')"
29+
echo "name=$name" >> "$GITHUB_OUTPUT"
30+
- name: Install uv
31+
uses: astral-sh/setup-uv@v8.2.0
32+
with:
33+
python-version: "3.13"
34+
enable-cache: true
35+
36+
- run: uv run --only-group docs zensical build --strict
37+
38+
- name: Deploy MR preview 🚀
39+
uses: JamesIves/github-pages-deploy-action@v4
40+
with:
41+
token: ${{ secrets.GITHUB_TOKEN }}
42+
branch: gh-pages
43+
folder: site
44+
# Publish under preview/<branch-without-docs>/ without touching the rest of gh-pages.
45+
target-folder: preview/${{ steps.slug.outputs.name }}
46+
clean: true
47+
48+
cleanup:
49+
# When a docs/* PR is merged or closed, remove its preview folder.
50+
if: >-
51+
github.event.action == 'closed'
52+
&& github.event.pull_request.head.repo.full_name == github.repository
53+
&& startsWith(github.head_ref, 'docs/')
54+
runs-on: ubuntu-latest
55+
steps:
56+
- uses: actions/checkout@v7
57+
with:
58+
ref: gh-pages
59+
- name: Remove preview folder
60+
run: |
61+
name="${GITHUB_HEAD_REF#docs/}"
62+
name="$(printf '%s' "$name" | tr '/' '-' | tr -cd '[:alnum:]._-')"
63+
if [ -d "preview/$name" ]; then
64+
git config user.name "github-actions[bot]"
65+
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
66+
git rm -r "preview/$name"
67+
git commit -m "chore(docs): remove MR preview for ${name}"
68+
git push
69+
else
70+
echo "No preview folder preview/${name} to remove."
71+
fi

.github/workflows/mkdocs.yml

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
name: mkdocs
2+
on:
3+
push:
4+
branches:
5+
- main
6+
# Per-target group: a release deploy is never cancelled by the develop/preview
7+
# workflows. Concurrent pushes to disjoint gh-pages folders are handled by the
8+
# deploy action's fetch+rebase+retry.
9+
concurrency:
10+
group: gh-pages-main
11+
cancel-in-progress: false
12+
jobs:
13+
deploy:
14+
runs-on: ubuntu-latest
15+
steps:
16+
- uses: actions/checkout@v7
17+
- name: Install uv
18+
uses: astral-sh/setup-uv@v8.2.0
19+
with:
20+
python-version: "3.13"
21+
enable-cache: true
22+
23+
- run: uv run --only-group docs zensical build --strict
24+
25+
- name: Deploy 🚀
26+
uses: JamesIves/github-pages-deploy-action@v4
27+
with:
28+
token: ${{ secrets.GITHUB_TOKEN }}
29+
branch: gh-pages
30+
folder: site
31+
clean: true
32+
# Preserve the develop preview (dev/) and the docs/* MR previews (preview/)
33+
# published by the mkdocs-develop and mkdocs-preview workflows.
34+
clean-exclude: |
35+
dev
36+
preview

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ __pycache__/
44
dist/
55
build/
66

7+
# Zensical documentation build output
8+
/site
9+
710
# Python dist
811
*.egg-info/
912
.eggs/

README.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,13 @@ Install
2626
$ pip install rebulk
2727
```
2828

29+
Documentation
30+
=============
31+
32+
Full documentation is published on GitHub Pages:
33+
<https://toilal.github.io/rebulk>. A preview of the in-development `develop`
34+
branch is available at <https://toilal.github.io/rebulk/dev/>.
35+
2936
Usage
3037
=====
3138

docs/api.md

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
# API reference
2+
3+
This page lists the public symbols exported from the `rebulk` package (its `__all__`).
4+
5+
```python
6+
from rebulk import (
7+
Rebulk, Key, Rule, CustomRule,
8+
AppendMatch, RemoveMatch, RenameMatch, AppendTags, RemoveTags,
9+
ConflictSolver, PrivateRemover,
10+
PRE_PROCESS, POST_PROCESS, REGEX_ENABLED,
11+
)
12+
```
13+
14+
## Rebulk
15+
16+
`class Rebulk(...)`
17+
18+
Main entry point. A `Builder` that holds patterns, rules and nested Rebulk objects, and
19+
runs the matching pipeline. Key methods:
20+
21+
- `string(*patterns, key=None, **options)` — add one or more string patterns.
22+
- `regex(*patterns, key=None, **options)` — add one or more regex patterns.
23+
- `functional(*patterns, key=None, **options)` — add one or more functional patterns.
24+
- `chain(**options) -> ChainBuilder` — start a chain of patterns (closed with `close()`).
25+
- `pattern(*patterns)` — add already-built `Pattern` objects.
26+
- `rules(*rules)` — register rules from a class, instance or module.
27+
- `rebulk(*rebulks)` — nest other `Rebulk` objects to compose their patterns and rules.
28+
- `defaults(**kwargs)` / `string_defaults` / `regex_defaults` / `functional_defaults` /
29+
`chain_defaults` — set default options for subsequently built patterns.
30+
- `declare_keys(*keys)` — declare typed `Key` objects whose converters are inherited by
31+
later patterns as per-name formatters.
32+
- `matches(string, context=None) -> Matches` — run the pipeline and return the results.
33+
- `check_keys(*, allowed_unused=()) -> list[str]` — declared key names no pattern can
34+
produce (guards against typos / stale names).
35+
- `effective_patterns(context=None)`, `effective_rules(context=None)`,
36+
`effective_keys(context=None)` — the patterns / rules / keys active for a given context.
37+
38+
## Key
39+
40+
`class Key(name, value_type, formatter=None)`
41+
42+
A frozen, generic dataclass binding a match `name` to its `value_type` for type-safe
43+
retrieval. `formatter`, if given, is the `(str) -> T` converter applied to the matched
44+
substring; it defaults to `value_type` itself. Passing a key to a builder method (`key=`)
45+
wires up both the match name and the formatter, so `matches[key]` and `matches.all(key)`
46+
return precise types instead of `Any`. `value_type` must be scalar — a dataclass or
47+
`TypedDict` is rejected (assemble those with `Matches.to(...)`). The `converter` property
48+
returns the effective `(str) -> T` converter.
49+
50+
## Rule / CustomRule
51+
52+
`class Rule` — abstract base for a rule. Implement `when(matches, context)` (return truthy
53+
to trigger) and either `then(matches, when_response, context)` or a `consequence` class
54+
attribute. Class attributes `priority` (int, higher first) and `dependency` (another rule
55+
class) control ordering. `CustomRule` is the generic, typed rule base re-exported for
56+
building custom rules.
57+
58+
## Consequences
59+
60+
Consequence classes applied when a rule triggers (used as a rule's `consequence`):
61+
62+
- `RemoveMatch` — remove the matched objects from the result.
63+
- `AppendMatch` — append new matches to the result.
64+
- `RenameMatch` — rename the matched objects.
65+
- `AppendTags` — add tags to the matched objects.
66+
- `RemoveTags` — remove tags from the matched objects.
67+
68+
## Default processors
69+
70+
- `ConflictSolver` — default `Rule` (priority `PRE_PROCESS`) that keeps the longer match
71+
when matches overlap.
72+
- `PrivateRemover` — default `Rule` (priority `POST_PROCESS`) that removes matches flagged
73+
as `private` from the final output.
74+
75+
## Constants
76+
77+
- `PRE_PROCESS` (`2048`) — priority for rules running before the standard rules.
78+
- `POST_PROCESS` (`-2048`) — priority for rules running after the standard rules.
79+
- `REGEX_ENABLED``True` when the optional [`regex`](https://pypi.python.org/pypi/regex)
80+
backend is active (`REBULK_REGEX_ENABLED=1` and `regex` importable), otherwise `False`.
81+
82+
## Other useful modules
83+
84+
These are not in `__all__` but are part of the public surface:
85+
86+
- `rebulk.match` — the `Match` and `Matches` classes and the `MatchesDict` mapping.
87+
- `rebulk.validators` — reusable validator functions (typically wired with
88+
`functools.partial`), e.g. surrounding-character checks.
89+
- `rebulk.debug` — debug switches such as `CHECK_DECLARED_KEYS` (declared-key value-type
90+
contract check, off by default).
91+
- `rebulk.introspector` — utilities to extract pattern / rule metadata from a configured
92+
`Rebulk`.

docs/index.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
# Rebulk
2+
3+
Rebulk is a Python library that performs advanced searches in strings that would be
4+
hard to implement using the [`re` module](https://docs.python.org/3/library/re.html)
5+
or [string methods](https://docs.python.org/3/library/stdtypes.html#str) alone.
6+
7+
It provides building blocks such as `Patterns`, `Match` and `Rule` that let you build
8+
a custom and complex string matcher through a readable, extendable, fluent API.
9+
10+
The project is hosted on GitHub: <https://github.com/Toilal/rebulk>.
11+
12+
## Features
13+
14+
- **Composable patterns** — declare `string`, `regex` and `functional` patterns in bulk
15+
on a single `Rebulk` object using a fluent, chainable API.
16+
- **Rich match model** — every result is a `Match` object with `value`, `start`, `end`,
17+
`span`, `name`, `tags` and nested `children`; the `Matches` collection offers indexed
18+
lookups by name, tag, position and proximity.
19+
- **Rule engine** — express conditional post-processing (filtering, renaming, tagging,
20+
appending) as `Rule` classes with priorities and dependencies.
21+
- **Chains and repeaters** — compose ordered sequences of patterns with regex-like
22+
quantifiers (`?`, `*`, `+`, `{n,m}`).
23+
- **Typed retrieval** — bind a match name to its value type with `Key` for type-safe
24+
access, and project matches onto a dataclass or `TypedDict` with `Matches.to(...)`.
25+
- **Optional `regex` backend** — enable the [`regex`](https://pypi.python.org/pypi/regex)
26+
module (repeated captures) at runtime with `REBULK_REGEX_ENABLED=1`.
27+
- **Fully typed** — the package ships a `py.typed` marker and passes `mypy --strict`.
28+
29+
## Installation
30+
31+
```sh
32+
pip install rebulk
33+
```
34+
35+
To enable the optional [`regex`](https://pypi.python.org/pypi/regex) backend, install the
36+
`native` extra and set the environment variable at runtime:
37+
38+
```sh
39+
pip install "rebulk[native]"
40+
export REBULK_REGEX_ENABLED=1
41+
```
42+
43+
Rebulk requires Python 3.10 or later and has no runtime dependencies (the `regex`
44+
backend is optional).
45+
46+
## Quickstart
47+
48+
Regular expression, string and function based patterns are declared on a `Rebulk`
49+
object. It uses a fluent API to chain `string`, `regex` and `functional` methods to
50+
define the pattern types.
51+
52+
```python
53+
>>> from rebulk import Rebulk
54+
>>> bulk = Rebulk().string('brown').regex(r'qu\w+').functional(lambda s: (20, 25))
55+
56+
```
57+
58+
Once the `Rebulk` object is fully configured, call `matches` with an input string to
59+
retrieve all `Match` objects found by the registered patterns.
60+
61+
```python
62+
>>> bulk.matches("The quick brown fox jumps over the lazy dog")
63+
[<brown:(10, 15)>, <quick:(4, 9)>, <jumps:(20, 25)>]
64+
65+
```
66+
67+
If several `Match` objects are found at the same position, only the longer one is kept
68+
(this is the job of the default `ConflictSolver` rule).
69+
70+
```python
71+
>>> bulk = Rebulk().string('lakers').string('la')
72+
>>> bulk.matches("the lakers are from la")
73+
[<lakers:(4, 10)>, <la:(20, 22)>]
74+
75+
```
76+
77+
## Where to go next
78+
79+
- [Usage](usage.md) — build a `Rebulk`, declare the three pattern types, configure
80+
pattern options, and read back `Match` / `Matches`.
81+
- [Rules & processors](rules.md) — write `Rule` classes and use the built-in
82+
consequences and default processors.
83+
- [API reference](api.md) — the public symbols exported from the `rebulk` package.
84+
- [v5 design notes](v5-design.md) — background design notes.

0 commit comments

Comments
 (0)