Skip to content

Commit bedb208

Browse files
rustyconoverclaude
andcommitted
Rewrite README for clarity: smoother, more scannable, current commands
Tighten the prose, lead with a single quickstart, collapse the transport sections into a small table, and replace the stale --rootdir/--with pytest invocation with 'uv run --frozen pytest'. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 5301eb5 commit bedb208

1 file changed

Lines changed: 56 additions & 119 deletions

File tree

README.md

Lines changed: 56 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -9,164 +9,101 @@
99
[![Python](https://img.shields.io/pypi/pyversions/vgi-easter.svg)](https://pypi.org/project/vgi-easter/)
1010
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
1111

12-
A minimal [VGI (Vector Gateway Interface)](https://github.com/Query-farm) worker
13-
that computes the date of **Western (Gregorian) Easter Sunday** for a given year
14-
and exposes it to DuckDB as a SQL scalar function.
12+
A tiny [VGI (Vector Gateway Interface)](https://github.com/Query-farm) worker
13+
that gives DuckDB one SQL function — `easter_date(year)` — returning the date of
14+
Western (Gregorian) Easter Sunday. It has no external data and almost no code,
15+
which makes it a clean, copyable example of a VGI scalar-function worker.
16+
17+
## Quick start
18+
19+
In DuckDB:
1520

1621
```sql
1722
ATTACH 'easter' (TYPE 'vgi', LOCATION 'uvx vgi-easter');
1823

19-
SELECT easter.easter_date(2025);
20-
-- 2025-04-20
24+
SELECT easter.easter_date(2025); -- 2025-04-20
2125

2226
SELECT year, easter.easter_date(year) AS easter
23-
FROM range(2020, 2031) t(year);
24-
-- 2020 2020-04-12
25-
-- 2021 2021-04-04
26-
-- ...
27+
FROM range(2020, 2025) t(year);
2728
```
2829

29-
The function `easter_date(year)` takes a `BIGINT` year and returns a `DATE`,
30-
computed with the Anonymous Gregorian algorithm (the Meeus/Jones/Butcher
31-
*Computus*). It is pure standard-library arithmetic — no network calls, no
32-
external data — which makes this repo a clean, self-contained example of a VGI
33-
scalar-function worker.
30+
DuckDB launches the worker for you, and `easter_date` then behaves like a native
31+
function (a null year yields a null date). The `uvx vgi-easter` location fetches
32+
the worker on demand; to keep it around, `pip install vgi-easter`.
3433

3534
## How it works
3635

37-
VGI lets a worker process publish catalogs, schemas, and functions that DuckDB
38-
can `ATTACH` and query as if they were native. Data crosses the boundary as
39-
Apache Arrow IPC, so values stay columnar end to end.
36+
A VGI worker publishes catalogs, schemas, and functions that DuckDB can `ATTACH`
37+
and query as if they were built in. Values cross the boundary as Apache Arrow,
38+
so they stay columnar end to end.
4039

41-
This worker publishes one catalog:
40+
This worker publishes a single function:
4241

4342
```
44-
easter (catalog, data version 1.0.0)
45-
└── main (schema)
46-
└── easter_date(year BIGINT) -> DATE
43+
easter (catalog)
44+
└── main (schema)
45+
└── easter_date(year BIGINT) DATE
4746
```
4847

49-
`year` propagates nulls — a null year yields a null date.
50-
51-
The entire implementation lives in [`easter_worker.py`](easter_worker.py):
52-
53-
- `_easter_sunday(year)` — the Computus, returning a `datetime.date`.
54-
- `EasterDateFunction` — a `ScalarFunction` mapping an `Int64Array` of years to
55-
a `date32` array, with metadata and SQL examples for catalog introspection.
56-
- `EasterCatalog` / `EasterWorker` — wire the function into a VGI catalog that
57-
advertises a stable `data_version` (`1.0.0`) and a git-SHA
58-
`implementation_version` (from `VGI_EASTER_GIT_COMMIT`).
48+
The whole implementation is ~160 lines in
49+
[`easter_worker.py`](easter_worker.py): the date calculation
50+
(`_easter_sunday`, the Anonymous Gregorian *Computus* — pure standard library),
51+
a `ScalarFunction` that maps an Arrow array of years to dates, and a few lines
52+
wiring it into a catalog.
5953

60-
## Requirements
54+
## Running it
6155

62-
- Python **3.13+**
63-
- [`uv`](https://docs.astral.sh/uv/) (recommended) or `pip`
64-
65-
The only dependency is [`vgi-python`](https://pypi.org/project/vgi-python/) (the
66-
`http` extra adds the HTTP-server transport); it is published on PyPI, so no
67-
sibling checkouts are needed.
68-
69-
## Installing
70-
71-
```bash
72-
# Install from PyPI (provides the vgi-easter and vgi-easter-http commands)
73-
pip install vgi-easter
74-
# or run it ad hoc without installing
75-
uvx vgi-easter
76-
```
77-
78-
## Running
79-
80-
The worker supports both VGI transports. Two console scripts are installed:
81-
`vgi-easter` (stdio) and `vgi-easter-http` (HTTP server).
82-
83-
### stdio (DuckDB spawns the worker)
84-
85-
DuckDB runs the worker as a subprocess and talks to it over stdin/stdout. No
86-
server to manage — point the LOCATION at the installed command (or `uvx
87-
vgi-easter` to fetch it on demand):
88-
89-
```sql
90-
ATTACH 'easter' (TYPE 'vgi', LOCATION 'uvx vgi-easter');
91-
SELECT easter.easter_date(2025);
92-
DETACH easter;
93-
```
56+
Once installed, the package gives you one command per VGI transport:
9457

95-
### HTTP
58+
| Command | Transport | Use it when |
59+
| ----------------- | --------- | -------------------------------------------------------- |
60+
| `vgi-easter` | stdio | DuckDB spawns the worker as a subprocess (the quickstart)|
61+
| `vgi-easter-http` | HTTP | you want a long-running server to attach to |
9662

97-
Start the worker as an HTTP server (`vgi-easter-http` calls
98-
`EasterWorker.main_http()`):
63+
To run over HTTP, start the server and attach to its URL:
9964

10065
```bash
10166
VGI_SIGNING_KEY=dev vgi-easter-http --host 0.0.0.0 --port 8000
10267
```
10368

104-
Then attach over HTTP (the VGI extension auto-loads `httpfs`):
105-
10669
```sql
10770
ATTACH 'easter' (TYPE 'vgi', LOCATION 'http://localhost:8000');
108-
SELECT easter.easter_date(2025);
10971
```
11072

111-
### From a source checkout
112-
113-
The two modules also carry inline [PEP 723](https://peps.python.org/pep-0723/)
114-
metadata, so you can run them directly without installing:
73+
Working from a checkout instead? Both modules carry
74+
[PEP 723](https://peps.python.org/pep-0723/) metadata, so `uv run
75+
easter_worker.py` (stdio) and `uv run serve.py` (HTTP) run without installing
76+
anything.
11577

116-
```bash
117-
uv run --python 3.13 easter_worker.py # stdio
118-
VGI_SIGNING_KEY=dev uv run --python 3.13 serve.py --host 0.0.0.0 --port 8000 # HTTP
119-
```
78+
## Configuration
12079

121-
## Testing
80+
| Variable | Purpose |
81+
| --------------------------------- | ------------------------------------------------------------- |
82+
| `VGI_SIGNING_KEY` | Signing key for HTTP state tokens (required by the HTTP server).|
83+
| `VGI_HTTP_HOST` / `VGI_HTTP_PORT` | HTTP bind address (default: all interfaces / `8000`). |
84+
| `VGI_EASTER_GIT_COMMIT` | Reported as the catalog's `implementation_version`. |
85+
| `VGI_WORKER_DEBUG` | Set to `1` for debug logging. |
12286

123-
### Unit tests (pytest)
87+
## Development
12488

125-
The `tests/` suite checks the Computus against known Easter dates (including the
126-
March 22 / April 25 extremes) and the Arrow `compute()` path including null
127-
propagation:
89+
Requires Python 3.13+ and [`uv`](https://docs.astral.sh/uv/); the only
90+
dependency is [`vgi-python`](https://pypi.org/project/vgi-python/).
12891

12992
```bash
130-
uv run --python 3.13 \
131-
--with pytest --with vgi-python \
132-
pytest tests/ --rootdir=. -o "addopts=" -q
93+
uv run --frozen pytest tests/ -q
13394
```
13495

135-
The `--rootdir=. -o "addopts="` flags keep pytest from picking up an upstream
136-
`pyproject.toml` that injects `--mypy --ruff`. `conftest.py` puts the repo root
137-
on `sys.path` so the tests can `import easter_worker`.
138-
139-
### SQL integration tests (sqllogictest)
140-
141-
`test/sql/` contains [sqllogictest](https://duckdb.org/dev/sqllogictest/intro)
142-
files that run against the worker through the real DuckDB VGI extension:
96+
The `tests/` suite covers the Easter calculation (including the March 22 /
97+
April 25 extremes) and the Arrow compute path. A separate
98+
[sqllogictest](https://duckdb.org/dev/sqllogictest/intro) suite in `test/sql/`
99+
drives the worker through the **real** DuckDB `vgi` extension. CI runs both on
100+
Linux, macOS, and Windows — see [`ci/README.md`](ci/README.md).
143101

144-
- `easter_catalog.test` — catalog discovery, `data_version_spec`, `ATTACH` and
145-
schema introspection.
146-
- `easter_function.test` — scalar evaluation, the `DATE` result type, and null
147-
propagation.
102+
## Releasing
148103

149-
Both are gated on `require-env VGI_EASTER_WORKER`, so point that at a worker
150-
LOCATION (a stdio command or an HTTP URL) and run them with the DuckDB
151-
`unittest` binary built with the VGI extension.
152-
153-
## Environment variables
154-
155-
| Variable | Purpose |
156-
| ------------------------- | ------------------------------------------------------------------- |
157-
| `VGI_SIGNING_KEY` | Stable key for state-token signing (required for the HTTP server). |
158-
| `VGI_EASTER_GIT_COMMIT` | Git SHA reported as the catalog's `implementation_version`. |
159-
| `VGI_HTTP_PORT` / `VGI_HTTP_HOST` | HTTP bind address (defaults: `8000` / all interfaces). |
160-
| `VGI_WORKER_DEBUG` | Set to `1` for debug logging. |
161-
162-
## Publishing
163-
164-
This repo is a packaged distribution (`vgi-easter`) built with hatchling:
165-
166-
```bash
167-
uv build # writes dist/*.whl and dist/*.tar.gz
168-
uv publish # upload to PyPI (needs a token)
169-
```
104+
`vgi-easter` is built with hatchling and published to PyPI by CI when a GitHub
105+
Release is created (it runs the test suites, then `uv build && uv publish`). To
106+
cut a release, bump `version` in `pyproject.toml` and publish a GitHub Release.
170107

171108
## License
172109

0 commit comments

Comments
 (0)