diff --git a/.gitignore b/.gitignore index 7ebe127..629ea0c 100644 --- a/.gitignore +++ b/.gitignore @@ -54,7 +54,8 @@ Thumbs.db .ai/ nitin_docs/ -# Sphinx build output +# MkDocs build output +site/ docs/_build/ diff --git a/.readthedocs.yaml b/.readthedocs.yaml index 8726591..97dd9b5 100644 --- a/.readthedocs.yaml +++ b/.readthedocs.yaml @@ -21,8 +21,8 @@ build: UV_PROJECT_ENVIRONMENT="${READTHEDOCS_VIRTUALENV_PATH}" \ ~/.local/bin/uv sync --frozen --group docs -sphinx: - configuration: docs/conf.py +mkdocs: + configuration: mkdocs.yml formats: - pdf diff --git a/AGENTS.md b/AGENTS.md index aad4168..e4bebd9 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -96,8 +96,8 @@ Full reference, generated from docstrings, is at `docs/api/`. | Artifact | Purpose | |---|---| -| [`docs/llms.txt`](docs/llms.txt) | Flat index of every doc page with one-line summaries. Cheap to grep, good for in-context injection. | -| [`docs/api/`](docs/api/) | Sphinx `autoclass` reference for every public symbol. Source of truth for method signatures. | +| [`llms.txt`](https://docs.redisvl.com/projects/sql-redis/llms.txt) | Auto-generated flat index of every doc page with one-line summaries. Built by `mkdocs-llmstxt` at deploy time. Good for in-context injection. | +| [`docs/api/`](docs/api/) | mkdocstrings reference for every public symbol, generated from Google-style docstrings. Source of truth for method signatures. | | [`docs/user_guide/how_to_guides/`](docs/user_guide/how_to_guides/) | Task-oriented recipes (vector search, GEO, dates, async, parameters). | | [`docs/concepts/`](docs/concepts/) | Why-style explanation: architecture, parameter substitution, schema-aware translation. | | [`docs/for-ais-only/`](docs/for-ais-only/) | Internal repo map for agents modifying the library. | @@ -107,7 +107,7 @@ Full reference, generated from docstrings, is at `docs/api/`. sql-redis sits in the [Redis AI Hub](https://redis.io/ai-hub/) under the *Experimental* tier as "SQL for Redis". Public docs URL: [`docs.redisvl.com/projects/sql-redis/`](https://docs.redisvl.com/projects/sql-redis/). -The hub's docs standards (Diataxis layout, autoclass-driven API reference, +The hub's docs standards (Diataxis layout, docstring-driven API reference, AI-agent affordances) are documented at [`HUB_DOCS_STANDARDS.md`](https://github.com/redis/docs/blob/main/HUB_DOCS_STANDARDS.md) in the hub repo. diff --git a/Makefile b/Makefile index 37ff08e..1bb1454 100644 --- a/Makefile +++ b/Makefile @@ -51,12 +51,11 @@ build: ## Build wheel and source distribution docs-build: ## Build documentation @echo "๐Ÿ“š Building documentation" - uv run --group docs make -C docs html + DISABLE_MKDOCS_2_WARNING=true uv run --group docs mkdocs build --strict docs-serve: ## Serve documentation locally at http://localhost:8000 @echo "๐ŸŒ Serving documentation at http://localhost:8000" - @echo "๐Ÿ“ Make sure docs are built first with 'make docs-build'" - uv run python -m http.server --directory docs/_build/html + DISABLE_MKDOCS_2_WARNING=true uv run --group docs mkdocs serve --dev-addr 127.0.0.1:8000 clean: ## Clean up build artifacts and caches @echo "๐Ÿงน Cleaning up directory" @@ -69,6 +68,7 @@ clean: ## Clean up build artifacts and caches find . -type d -name "build" -exec rm -rf {} + 2>/dev/null || true find . -type d -name "*.egg-info" -exec rm -rf {} + 2>/dev/null || true find . -type f -name "*.log" -exec rm -rf {} + 2>/dev/null || true + rm -rf site help: ## Show this help message @echo "Available commands:" diff --git a/README.md b/README.md index 2881170..3ed0e8c 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,17 @@ [![Status: Experimental](https://img.shields.io/badge/status-experimental-orange)](https://redis.io/ai-hub/) -Query Redis collections with familiar SQL on top of RediSearch and RedisVL indexes. Converts SQL `SELECT` statements into Redis `FT.SEARCH` and `FT.AGGREGATE` commands. +A SQL-to-Redis translator that converts SQL `SELECT` statements into Redis `FT.SEARCH` and `FT.AGGREGATE` commands. Query Redis collections with familiar SQL on top of RediSearch and RedisVL indexes. > **Status: Experimental.** sql-redis is in the [Redis AI Hub](https://redis.io/ai-hub/) under the Experimental tier. The API can change between minor releases. Not yet production-ready; we are validating the design and the SQL surface in real use. +A Diataxis-aligned documentation site (concepts, how-to guides, API reference, examples) lives in [`docs/`](docs/) and will be published at **docs.redisvl.com/projects/sql-redis/** once the hub site is live. Until then, this README is the canonical reference. Build the site locally with: + +```bash +uv sync --group docs +make docs-serve # http://localhost:8000 +``` + ## Install ```bash @@ -19,8 +26,9 @@ from redis import Redis from sql_redis import create_executor client = Redis() -executor = create_executor(client) +executor = create_executor(client) # lazy schema loading; no I/O yet +# Simple query result = executor.execute(""" SELECT title, price FROM products @@ -31,42 +39,417 @@ result = executor.execute(""" for row in result.rows: print(row[b"title"], row[b"price"]) + +# Vector search with parameter substitution +result = executor.execute( + """ + SELECT title, vector_distance(embedding, :vec) AS score + FROM products + LIMIT 5 + """, + params={"vec": vector_bytes}, +) ``` -## Documentation +Pass `decode_responses=True` to the `Redis` client if you want string keys instead of bytes. -Full documentation is published at **[docs.redisvl.com/projects/sql-redis/](https://docs.redisvl.com/projects/sql-redis/)**. +## What's implemented -- **Getting started:** [User Guide](https://docs.redisvl.com/projects/sql-redis/en/latest/user_guide/getting-started.html) -- **How-to guides:** [How-to Guides](https://docs.redisvl.com/projects/sql-redis/en/latest/user_guide/how_to_guides/) -- **Concepts and design:** [Concepts](https://docs.redisvl.com/projects/sql-redis/en/latest/concepts/) -- **API reference:** [API](https://docs.redisvl.com/projects/sql-redis/en/latest/api/) -- **SQL syntax catalog:** [SQL Syntax](https://docs.redisvl.com/projects/sql-redis/en/latest/api/sql-syntax.html) +- [x] Basic `SELECT` with field selection +- [x] `WHERE` with TEXT, NUMERIC, TAG, GEO field types +- [x] Comparison operators: `=`, `!=`, `<`, `<=`, `>`, `>=`, `BETWEEN`, `IN` +- [x] Boolean operators: `AND`, `OR`, `NOT` +- [x] Aggregations: `COUNT`, `SUM`, `AVG`, `MIN`, `MAX` +- [x] `GROUP BY` with multiple aggregations +- [x] `ORDER BY` with `ASC`/`DESC` +- [x] `LIMIT` and `OFFSET` pagination +- [x] Computed fields: `price * 0.9 AS discounted` +- [x] Vector KNN search: `vector_distance(field, :param)` +- [x] Hybrid search (filters + vector) +- [x] Full-text search: exact phrase, fuzzy, proximity, OR/union, LIKE patterns, BM25 scoring +- [x] GEO field queries with full operator support +- [x] Date functions: `YEAR()`, `MONTH()`, `DAY()`, `DATE_FORMAT()`, etc. +- [x] `IS NULL` / `IS NOT NULL` via `ismissing()` (requires Redis 7.4+) +- [x] `exists()` function for field presence checks -## For AI agents +## What's not implemented (yet) -- **[`AGENTS.md`](AGENTS.md):** how to use sql-redis from an agent, including gotchas and the error model. -- **[`docs/llms.txt`](docs/llms.txt):** flat index of every doc page with one-line summaries. -- **[`docs/for-ais-only/`](docs/for-ais-only/):** repository map, build and test guide, and intentional failure modes for agents modifying the library. +- [ ] JOINs (Redis doesn't support cross-index joins) +- [ ] Subqueries +- [ ] HAVING clause +- [ ] DISTINCT +- [ ] Index creation from SQL (`CREATE INDEX`) -To build the docs locally: +The translator raises `ValueError` for unsupported clauses; do not retry with rephrasing. -```bash -uv sync --group docs -make docs-build -make docs-serve # http://localhost:8000 +## How-to guides + +The next sections are task-oriented recipes. Each shows the SQL syntax, the RediSearch command produced, and any gotchas. + +- [TEXT search](#text-search) +- [`IS NULL` / `IS NOT NULL`](#is-null--is-not-null-ismissing) +- [`exists()` field presence](#exists--field-presence-check) +- [DATE / DATETIME handling](#datedatetime-handling) +- [Date functions](#date-functions) +- [GEO field support](#geo-field-support) + +### TEXT search + +Full-text search on TEXT fields with multiple search modes: + +| Feature | SQL Syntax | RediSearch Output | Notes | +|---------|-----------|-------------------|-------| +| Exact phrase | `title = 'gaming laptop'` | `@title:"gaming laptop"` | Stopwords stripped | +| Tokenized search | `fulltext(title, 'gaming laptop')` | `@title:(gaming laptop)` | Stopwords stripped | +| Fuzzy LD=1 | `fuzzy(title, 'laptap')` | `@title:%laptap%` | | +| Fuzzy LD=2 | `fuzzy(title, 'laptap', 2)` | `@title:%%laptap%%` | | +| Fuzzy LD=3 | `fuzzy(title, 'laptap', 3)` | `@title:%%%laptap%%%` | | +| OR / union | `fulltext(title, 'laptop OR tablet')` | `@title:(laptop\|tablet)` | | +| Prefix | `title LIKE 'lap%'` | `@title:lap*` | | +| Suffix | `title LIKE '%top'` | `@title:*top` | | +| Contains | `title LIKE '%apt%'` | `@title:*apt*` | | +| Proximity (slop) | `fulltext(title, 'gaming laptop', 2)` | `@title:(gaming laptop) => { $slop: 2; }` | | +| Proximity + order | `fulltext(title, 'gaming laptop', 2, true)` | `@title:(gaming laptop) => { $slop: 2; $inorder: true; }` | | +| Optional term | `fulltext(title, 'laptop ~gaming')` | `@title:(laptop ~gaming)` | | +| BM25 score | `SELECT score() AS relevance FROM idx` | `FT.SEARCH ... WITHSCORES` | | +| Negation | `NOT fulltext(title, 'refurbished')` | `-@title:refurbished` | | + +**Examples:** + +```sql +-- Exact phrase match (stopwords like "of" are stripped automatically) +SELECT * FROM products WHERE title = 'bank of america' +-- Produces: @title:"bank america" + +-- Fuzzy search for typos (Levenshtein distance 2) +SELECT * FROM products WHERE fuzzy(title, 'laptap', 2) + +-- OR search across terms +SELECT * FROM products WHERE fulltext(title, 'laptop OR tablet OR phone') + +-- Proximity: terms within 3 words of each other, in order +SELECT * FROM products WHERE fulltext(title, 'gaming laptop', 3, true) + +-- Suffix/contains pattern matching +SELECT * FROM products WHERE title LIKE '%phone%' + +-- BM25 relevance scoring +SELECT title, score() AS relevance FROM products WHERE fulltext(title, 'laptop') + +-- Multi-field search +SELECT * FROM products WHERE fulltext(title, 'laptop') OR fulltext(description, 'laptop') +``` + +**Stopword handling:** + +Both `=` (exact phrase) and `fulltext()` (tokenized search) automatically strip [Redis default stopwords](https://redis.io/docs/latest/develop/ai/search-and-query/advanced-concepts/stopwords/) before sending queries to RediSearch. This is necessary because RediSearch does not index stopwords, so including them in queries causes syntax errors or failed matches. A `UserWarning` is emitted when stopwords are removed. + +For example, `WHERE title = 'bank of america'` produces `@title:"bank america"` because "of" is a default stopword and is never stored in the inverted index. The stripped phrase still matches correctly because the indexer assigns consecutive token positions after dropping stopwords. + +To include stopwords in your queries, create your index with `STOPWORDS 0`: + +``` +FT.CREATE myindex ON HASH PREFIX 1 doc: STOPWORDS 0 SCHEMA title TEXT +``` + +**Notes:** +- `=` on TEXT fields performs **exact phrase** matching (double-quoted) +- `fulltext()` performs **tokenized** AND search (parenthesized) +- Both operators strip stopwords and emit a warning when they do +- `fuzzy()` and `fulltext()` only work on TEXT fields; using them on TAG or NUMERIC raises `ValueError` +- OR must be **uppercase**: `'laptop OR tablet'` triggers union; lowercase `'laptop or tablet'` is treated as a regular three-word AND search +- Special characters (`@`, `|`, `-`, `*`, `+`, etc.) in search terms are automatically escaped + +### IS NULL / IS NOT NULL (ismissing) + +Check for missing (absent) fields using standard SQL `IS NULL` / `IS NOT NULL` syntax. Requires **Redis 7.4+** (RediSearch 2.10+) with `INDEXMISSING` declared on the field. + +| SQL | RediSearch Output | +|-----|-------------------| +| `WHERE email IS NULL` | `ismissing(@email)` | +| `WHERE email IS NOT NULL` | `-ismissing(@email)` | + +```sql +-- Find users without an email +SELECT * FROM users WHERE email IS NULL + +-- Find users with an email +SELECT * FROM users WHERE email IS NOT NULL + +-- Combine with other filters +SELECT * FROM users WHERE category = 'eng' AND email IS NULL +``` + +**Note:** The field must be declared with `INDEXMISSING` in the index schema. A warning is emitted at translation time as a reminder. + +### exists() โ€” Field presence check + +Check whether a field has a value using `exists()` in SELECT or HAVING. This uses `FT.AGGREGATE` with `APPLY exists(@field)`. + +```sql +-- Check if fields exist (returns 1 or 0) +SELECT name, exists(email) AS has_email FROM users + +-- Filter to only rows where a field exists +SELECT name FROM users HAVING exists(email) = 1 + +-- Combine with other computed fields +SELECT name, exists(email) AS has_email, exists(phone) AS has_phone FROM users +``` + +**Note:** `exists()` is different from `IS NOT NULL` โ€” it works via `FT.AGGREGATE APPLY` and doesn't require `INDEXMISSING` on the field, but returns `1`/`0` rather than filtering rows directly. + +### DATE/DATETIME handling + +Redis does not have a native DATE field type. Dates are stored as **NUMERIC fields** with Unix timestamps. + +**sql-redis automatically converts ISO 8601 date literals to Unix timestamps:** + +```sql +-- Date literal (automatically converted to timestamp 1704067200) +SELECT * FROM events WHERE created_at > '2024-01-01' + +-- Datetime literal with time +SELECT * FROM events WHERE created_at > '2024-01-01T12:00:00' + +-- Date range with BETWEEN +SELECT * FROM events WHERE created_at BETWEEN '2024-01-01' AND '2024-01-31' + +-- Multiple date conditions +SELECT * FROM events WHERE created_at > '2024-01-01' AND created_at < '2024-12-31' +``` + +**Supported date formats:** +- Date: `'2024-01-01'` (interpreted as midnight UTC) +- Datetime: `'2024-01-01T12:00:00'` or `'2024-01-01 12:00:00'` +- Datetime with timezone: `'2024-01-01T12:00:00Z'`, `'2024-01-01T12:00:00+00:00'` + +**Note:** All dates without timezone are interpreted as UTC. You can also use raw Unix timestamps if preferred: + +```sql +SELECT * FROM events WHERE created_at > 1704067200 ``` +### Date functions + +Extract date parts using SQL functions that map to Redis `APPLY` expressions: + +| SQL Function | Redis Function | Description | +|--------------|----------------|-------------| +| `YEAR(field)` | `year(@field)` | Extract year (e.g., 2024) | +| `MONTH(field)` | `monthofyear(@field)` | Extract month (0-11) | +| `DAY(field)` | `dayofmonth(@field)` | Extract day of month (1-31) | +| `HOUR(field)` | `hour(@field)` | Round to hour | +| `MINUTE(field)` | `minute(@field)` | Round to minute | +| `DAYOFWEEK(field)` | `dayofweek(@field)` | Day of week (0=Sunday) | +| `DAYOFYEAR(field)` | `dayofyear(@field)` | Day of year (0-365) | +| `DATE_FORMAT(field, fmt)` | `timefmt(@field, fmt)` | Format timestamp | + +**Examples:** + +```sql +-- Extract year and month +SELECT name, YEAR(created_at) AS year, MONTH(created_at) AS month FROM events + +-- Filter by year +SELECT name FROM events WHERE YEAR(created_at) = 2024 + +-- Group by date parts +SELECT YEAR(created_at) AS year, COUNT(*) FROM events GROUP BY year + +-- Format dates +SELECT name, DATE_FORMAT(created_at, '%Y-%m-%d') AS date FROM events +``` + +**Note:** Redis's `monthofyear()` returns 0-11 (not 1-12), and `dayofweek()` returns 0 for Sunday. + +**Limitations:** +- `NOT YEAR(field) = 2024` is not supported (raises `ValueError`) +- `DATE_FORMAT()` is only supported in SELECT, not in WHERE (raises `ValueError`) +- Date functions combined with `OR` are not supported (raises `ValueError`) + +### GEO field support + +GEO fields are fully implemented with standard SQL-like syntax: + +| Feature | Status | +|---------|--------| +| Coordinate order | `POINT(lon, lat)` โ€” matches Redis native format | +| Default unit | Meters (`m`) โ€” SQL standard | +| All operators | `<`, `<=`, `>`, `>=`, `BETWEEN` | +| Distance calculation | `geo_distance()` in SELECT clause | +| Combined filters | GEO + TEXT/TAG/NUMERIC | + +**Coordinate order: `POINT(lon, lat)`** + +Use **longitude first**, matching Redis's native GEO format: + +```sql +-- San Francisco coordinates: lon=-122.4194, lat=37.7749 +SELECT name FROM stores WHERE geo_distance(location, POINT(-122.4194, 37.7749)) < 5000 +``` + +**Units:** + +| Unit | Code | Example | +|------|------|---------| +| Meters | `m` | `geo_distance(location, POINT(-122.4194, 37.7749)) < 5000` | +| Kilometers | `km` | `geo_distance(location, POINT(-122.4194, 37.7749), 'km') < 5` | +| Miles | `mi` | `geo_distance(location, POINT(-122.4194, 37.7749), 'mi') < 3` | +| Feet | `ft` | `geo_distance(location, POINT(-122.4194, 37.7749), 'ft') < 16400` | + +Default is meters when no unit is specified. + +**Operators:** + +```sql +-- Less than (uses optimized GEOFILTER) +SELECT name FROM stores WHERE geo_distance(location, POINT(-122.4194, 37.7749)) < 5000 + +-- Less than or equal (uses optimized GEOFILTER) +SELECT name FROM stores WHERE geo_distance(location, POINT(-122.4194, 37.7749)) <= 5000 + +-- Greater than (uses FT.AGGREGATE with FILTER) +SELECT name FROM stores WHERE geo_distance(location, POINT(-122.4194, 37.7749)) > 100000 + +-- Greater than or equal (uses FT.AGGREGATE with FILTER) +SELECT name FROM stores WHERE geo_distance(location, POINT(-122.4194, 37.7749)) >= 100000 + +-- Between (uses FT.AGGREGATE with FILTER) +SELECT name FROM stores WHERE geo_distance(location, POINT(-122.4194, 37.7749), 'km') BETWEEN 10 AND 100 +``` + +**Distance calculation in SELECT:** + +```sql +-- Get distance to each store (returns meters) +SELECT name, geo_distance(location, POINT(-122.4194, 37.7749)) AS distance +FROM stores + +-- With explicit unit +SELECT name, geo_distance(location, POINT(-122.4194, 37.7749), 'km') AS distance_km +FROM stores +``` + +**Combined filters:** + +```sql +-- GEO + TAG filter +SELECT name FROM stores +WHERE category = 'retail' AND geo_distance(location, POINT(-122.4194, 37.7749)) < 5000 + +-- GEO + NUMERIC filter +SELECT name FROM stores +WHERE rating >= 4.0 AND geo_distance(location, POINT(-122.4194, 37.7749), 'mi') < 10 + +-- GEO + TEXT filter +SELECT name FROM stores +WHERE name = 'Downtown' AND geo_distance(location, POINT(-122.4194, 37.7749)) < 10000 +``` + +## Concepts and design + +The Diataxis "explanation" tier: why the library is shaped the way it is. The full versions live under [`docs/concepts/`](docs/concepts/). + +### Why SQL instead of a pandas-like Python DSL? + +| Approach | Example | Trade-offs | +|----------|---------|------------| +| **SQL** | `SELECT * FROM products WHERE price > 100` | Universal, well-understood, tooling exists | +| **Pandas-like** | `df[df.price > 100]` | Pythonic but limited to Python, no standard | +| **Builder pattern** | `query.select("*").where(price__gt=100)` | Type-safe but verbose, learning curve | + +We chose SQL because: + +1. **Universality** โ€” SQL is the lingua franca of data. Developers, analysts, and tools all speak it. +2. **No new DSL to learn** โ€” Users already know SQL. A pandas-like API requires learning our specific dialect. +3. **Tooling compatibility** โ€” SQL strings can be generated by ORMs, query builders, or AI assistants. +4. **Clear mapping** โ€” SQL semantics map reasonably well to RediSearch operations (SELECTโ†’LOAD, WHEREโ†’filter, GROUP BYโ†’GROUPBY). + +The downside is losing Python's type checking and IDE support, but for a query interface, the universality trade-off is worth it. + +### Why sqlglot instead of writing a custom parser? + +Options considered: custom parser (regex / hand-rolled recursive descent), PLY/Lark (parser generators), sqlparse (tokenizer only), and sqlglot (production SQL parser). We chose sqlglot because: + +1. **Battle-tested** โ€” Used in production by companies like Tobiko (SQLMesh). Handles edge cases we'd miss. +2. **Full AST** โ€” Provides a complete abstract syntax tree, not just tokens. We can traverse and analyze queries properly. +3. **Dialect support** โ€” Handles SQL variations. Users can write MySQL-style or PostgreSQL-style queries. +4. **Active maintenance** โ€” Regular releases, responsive maintainers, good documentation. + +Writing a custom parser would be error-prone and time-consuming for a POC. sqlglot lets us focus on the translation logic rather than parsing edge cases. + +### Why schema-aware translation? + +Redis field types determine query syntax: + +| Field Type | Redis Syntax | Example | +|------------|--------------|---------| +| TEXT | `@field:term` | `@title:laptop` | +| NUMERIC | `@field:[min max]` | `@price:[100 500]` | +| TAG | `@field:{value}` | `@category:{books}` | + +Without schema knowledge, we can't translate `category = 'books'` correctly โ€” it could be `@category:books` (TEXT search) or `@category:{books}` (TAG exact match). The `SchemaRegistry` fetches index schemas via `FT.INFO` and the translator uses this to generate correct syntax per field type. This adds a Redis round-trip at initialization but ensures correct query generation. + +### Architecture + +``` +SQL String + โ†“ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ SQLParser โ”‚ Parse SQL โ†’ ParsedQuery dataclass +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ†“ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ SchemaRegistry โ”‚ Load field types from Redis +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ†“ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Analyzer โ”‚ Classify conditions by field type +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ†“ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ QueryBuilder โ”‚ Generate RediSearch syntax per type +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ†“ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Translator โ”‚ Orchestrate pipeline, build command +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ†“ +โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” +โ”‚ Executor โ”‚ Execute command, parse results +โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”ฌโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ + โ†“ +QueryResult(rows, count) +``` + +Each layer has focused unit tests; 100% coverage is achievable because responsibilities are clear. Adding a new field type (e.g., GEO) means updating Analyzer and QueryBuilder, not rewriting everything. Early prototypes combined parsing and translation, which led to tests that required Redis connections for simple SQL parsing tests, difficulty testing edge cases in isolation, and tangled code that was hard to modify. The layered approach emerged from TDD โ€” writing tests first revealed natural boundaries. + +## For AI agents + +- **[`AGENTS.md`](AGENTS.md):** how to use sql-redis from an agent, including gotchas and the error model. +- **`llms.txt`:** auto-generated at docs-build time (`make docs-build` โ†’ `site/llms.txt`). A flat index of every doc page with one-line summaries; it will live at `docs.redisvl.com/projects/sql-redis/llms.txt` once published. +- **[`docs/for-ais-only/`](docs/for-ais-only/):** repository map, build and test guide, and intentional failure modes for agents modifying the library. + ## Development ```bash -make install # uv sync +make install # uv sync --all-extras make test # requires Docker for testcontainers make test-cov # with coverage report make lint # format + mypy ``` -The project uses strict TDD with 100% coverage enforced in CI. See [`docs/concepts/testing-philosophy.md`](docs/concepts/testing-philosophy.md). +## Testing philosophy + +This project uses strict TDD with 100% test coverage as a hard requirement: + +1. **Write failing tests first** โ€” Define expected behavior before implementation. +2. **One test at a time** โ€” Implement just enough to pass each test. +3. **No untestable code** โ€” If we can't test it, we don't write it. +4. **Integration tests mirror raw Redis** โ€” `test_sql_queries.py` verifies SQL produces the same results as equivalent `FT.AGGREGATE` commands in `test_redis_queries.py`. + +Coverage is enforced in CI. Pragmas (`# pragma: no cover`) are forbidden โ€” if code can't be tested, it shouldn't exist. See [`docs/concepts/testing-philosophy.md`](docs/concepts/testing-philosophy.md) for the long form. ## License diff --git a/docs/Makefile b/docs/Makefile deleted file mode 100644 index 5c2dc9c..0000000 --- a/docs/Makefile +++ /dev/null @@ -1,14 +0,0 @@ -# Minimal makefile for Sphinx documentation - -SPHINXOPTS ?= -SPHINXBUILD ?= sphinx-build -SOURCEDIR = . -BUILDDIR = _build - -help: - @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) - -.PHONY: help Makefile - -%: Makefile - @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/docs/SPEC.md b/docs/SPEC.md deleted file mode 100644 index dc0c79e..0000000 --- a/docs/SPEC.md +++ /dev/null @@ -1,115 +0,0 @@ -# Documentation Spec - -## Goal - -Replace the single 456-line README with a Diรกtaxis-aligned Sphinx documentation site, modeled on `redis-vl-python`'s `docs/` layout, so that: - -1. Each piece of content lives in the quadrant that matches its purpose (learning, task, reference, understanding). -2. The full public API surface (currently 11 exported symbols) is discoverable. -3. Docstrings in `sql_redis/` are the single source of truth for API reference (via Sphinx `autoclass`). -4. The site can be built locally and published to Read the Docs. - -## Diรกtaxis Recap - -| Quadrant | User need | Voice | Folder | -|---|---|---|---| -| Tutorial | "Teach me" (learning) | Hand-holding, end-to-end | `user_guide/getting-started.md` | -| How-to | "Help me do X" (task) | Recipe, presumes knowledge | `user_guide/how_to_guides/` | -| Reference | "Tell me what" (info) | Dry, complete, accurate | `api/`, `api/sql-syntax.md` | -| Explanation | "Help me understand" (theory) | Discursive, context, history | `concepts/` | - -Tutorials and how-to are both in `user_guide/` (matching the redis-vl-python layout) but kept structurally distinct. - -## Folder Layout - -``` -docs/ - index.md landing page with grid cards - conf.py Sphinx config (myst, autoclass, sphinx_book_theme, sphinx_design) - Makefile standard sphinx makefile - - concepts/ EXPLANATION - index.md grid landing - architecture.md layered pipeline + diagram - why-sql.md SQL vs pandas-like vs builder - why-sqlglot.md sqlglot vs custom parser - schema-aware-translation.md why FT.INFO matters, lazy vs eager - parameter-substitution.md token-based substitution rationale (from PARAMETER_SUBSTITUTION.md) - testing-philosophy.md TDD, 100% coverage - - user_guide/ TUTORIAL + HOW-TO - index.md grid landing - installation.md pip install, Redis setup - getting-started.md first end-to-end query - how_to_guides/ - index.md - use-parameters.md token substitution, vector params - vector-search.md KNN, hybrid filter+vector - text-search.md exact phrase, fuzzy, proximity, BM25 - geo-queries.md POINT, units, operators - date-queries.md ISO literals, YEAR/MONTH/DAY - missing-fields.md IS NULL, exists() - lazy-vs-eager-schemas.md SchemaCacheStrategy - async-usage.md AsyncExecutor, AsyncSchemaRegistry - - api/ REFERENCE - index.md TOC - translator.rst Translator, TranslatedQuery (autoclass) - schema.rst SchemaRegistry, AsyncSchemaRegistry (autoclass) - executor.rst Executor, AsyncExecutor, QueryResult, factories (autoclass) - sql-syntax.md reference tables (TEXT, GEO, dates) extracted from README - - examples/ - index.md placeholder pointing back to user_guide -``` - -## API Reference Generation - -Use `sphinx.ext.autodoc` + `sphinx.ext.napoleon` (Google-style docstrings, which the codebase already uses). - -Each `.rst` file declares the symbols with `autoclass :members:` so the docstrings already in `executor.py`, `schema.py`, `translator.py` become the rendered reference. No duplication, no drift. - -## README - -Trim to about 80 lines: tagline, one-screen quick example, install, link to docs site, status note. The reference tables and design discussions move to docs. - -## Root Files - -- `PR_NOTES.md`: delete (transient PR description, has no place at repo root). -- `PARAMETER_SUBSTITUTION.md`: content migrated to `concepts/parameter-substitution.md`, root file deleted. - -## Build Targets - -Root `Makefile` gains, mirroring redis-vl-python: - -```make -docs-build: uv run make -C docs html -docs-serve: uv run python -m http.server --directory docs/_build/html -``` - -`docs/Makefile` is the standard Sphinx-generated catch-all that delegates to `sphinx-build`. - -## Dependencies - -Add a `docs` dependency group to `pyproject.toml`: - -```toml -[dependency-groups] -docs = [ - "sphinx>=7.3", - "sphinx-book-theme>=1.1", - "sphinx-design>=0.6", - "sphinx-copybutton>=0.5", - "myst-parser>=3.0", -] -``` - -## Read the Docs - -Add `.readthedocs.yaml` so the site can be published. Build uses uv with `--group docs`. - -## Out of Scope - -- No Jupyter notebook tutorials (per user instruction). -- No CONTRIBUTING.md (separate concern). -- No MCP / connector pages (those don't exist for sql-redis). diff --git a/docs/api/executor.md b/docs/api/executor.md new file mode 100644 index 0000000..63a8ce3 --- /dev/null +++ b/docs/api/executor.md @@ -0,0 +1,49 @@ +--- +description: Sync and async executors that run translated SQL against Redis. +--- + +# Executor + +The executor runs a translated SQL query against Redis and parses the response +into a [`QueryResult`][sql_redis.QueryResult]. There are sync and async variants +and factory functions that wire up a schema registry for you. + +| Symbol | Description | +|---|---| +| [`Executor`](#executor) | Sync executor. | +| [`AsyncExecutor`](#asyncexecutor) | Async executor. | +| [`create_executor`](#create_executor) | Factory for the sync executor with a configurable cache strategy. | +| [`create_async_executor`](#create_async_executor) | Factory for the async executor. | +| [`QueryResult`](#queryresult) | Result rows and total count. | +| [`SchemaCacheStrategy`](#schemacachestrategy) | `"lazy"` or `"load_all"` literal. | + +## Executor + +::: sql_redis.Executor + +## AsyncExecutor + +::: sql_redis.AsyncExecutor + +## create_executor + +::: sql_redis.create_executor + +## create_async_executor + +::: sql_redis.create_async_executor + +## QueryResult + +::: sql_redis.QueryResult + +## SchemaCacheStrategy + +::: sql_redis.SchemaCacheStrategy + +## \_\_version\_\_ + +::: sql_redis.__version__ + +The installed package version, as a string. Useful for log lines, bug +reports, and version-gated feature checks. diff --git a/docs/api/executor.rst b/docs/api/executor.rst deleted file mode 100644 index 16ed567..0000000 --- a/docs/api/executor.rst +++ /dev/null @@ -1,98 +0,0 @@ -******** -Executor -******** - -The executor runs a translated SQL query against Redis and parses the response -into a :class:`~sql_redis.QueryResult`. There are sync and async variants and -factory functions that wire up a schema registry for you. - -.. list-table:: - :widths: 30 70 - :header-rows: 1 - - * - Symbol - - Description - * - :ref:`executor_api` - - Sync executor. - * - :ref:`asyncexecutor_api` - - Async executor. - * - :ref:`createexecutor_api` - - Factory for the sync executor with a configurable cache strategy. - * - :ref:`createasyncexecutor_api` - - Factory for the async executor. - * - :ref:`queryresult_api` - - Result rows and total count. - * - :ref:`schemacachestrategy_api` - - ``"lazy"`` or ``"load_all"`` literal. - -.. _executor_api: - -Executor -======== - -.. currentmodule:: sql_redis - -.. autoclass:: Executor - :members: - :inherited-members: - -.. _asyncexecutor_api: - -AsyncExecutor -============= - -.. currentmodule:: sql_redis - -.. autoclass:: AsyncExecutor - :members: - :inherited-members: - -.. _createexecutor_api: - -create_executor -=============== - -.. currentmodule:: sql_redis - -.. autofunction:: create_executor - -.. _createasyncexecutor_api: - -create_async_executor -===================== - -.. currentmodule:: sql_redis - -.. autofunction:: create_async_executor - -.. _queryresult_api: - -QueryResult -=========== - -.. currentmodule:: sql_redis - -.. autoclass:: QueryResult - :members: - -.. _schemacachestrategy_api: - -SchemaCacheStrategy -=================== - -.. currentmodule:: sql_redis - -.. autodata:: SchemaCacheStrategy - -.. _version_api: - -__version__ -=========== - -.. currentmodule:: sql_redis - -.. autodata:: __version__ - :annotation: = "" - -The installed package version, as a string. Useful for log lines, bug -reports, and version-gated feature checks. diff --git a/docs/api/index.md b/docs/api/index.md index cf4dce5..21162f8 100644 --- a/docs/api/index.md +++ b/docs/api/index.md @@ -1,19 +1,35 @@ --- -myst: - html_meta: - "description lang=en": | - sql-redis API reference. Generated from docstrings. +description: sql-redis API reference. Generated from docstrings. --- # API Reference Reference documentation for the public sql-redis API. Each class and function is generated from the docstrings in the source. -```{toctree} -:maxdepth: 2 +
-translator -schema -executor -sql-syntax -``` +- :material-translate:{ .lg .middle } **[Translator](translator.md)** + + --- + + Turn SQL into a Redis `FT.SEARCH` or `FT.AGGREGATE` command without executing it. + +- :material-database-search:{ .lg .middle } **[Schema Registries](schema.md)** + + --- + + Cache index field types from `FT.INFO`. Sync and async variants. + +- :material-play-circle:{ .lg .middle } **[Executor](executor.md)** + + --- + + Run a translated query against Redis. Sync and async, with factory helpers. + +- :material-code-tags:{ .lg .middle } **[SQL Syntax](sql-syntax.md)** + + --- + + The supported SQL surface: clauses, operators, functions, vector search. + +
diff --git a/docs/api/schema.md b/docs/api/schema.md new file mode 100644 index 0000000..c27e059 --- /dev/null +++ b/docs/api/schema.md @@ -0,0 +1,22 @@ +--- +description: Schema registries that cache index field types loaded from Redis. +--- + +# Schema Registries + +The schema registry caches index field types loaded from Redis via `FT.INFO`. +There are sync and async variants. Conceptual background is in +[Schema-aware translation](../concepts/schema-aware-translation.md). + +| Class | Description | +|---|---| +| [`SchemaRegistry`](#schemaregistry) | Sync registry. Lazy and eager loading, polling for index changes. | +| [`AsyncSchemaRegistry`](#asyncschemaregistry) | Async registry. Coalesced concurrent loads, cancellation-safe. | + +## SchemaRegistry + +::: sql_redis.SchemaRegistry + +## AsyncSchemaRegistry + +::: sql_redis.AsyncSchemaRegistry diff --git a/docs/api/schema.rst b/docs/api/schema.rst deleted file mode 100644 index c55a536..0000000 --- a/docs/api/schema.rst +++ /dev/null @@ -1,40 +0,0 @@ -***************** -Schema Registries -***************** - -The schema registry caches index field types loaded from Redis via ``FT.INFO``. -There are sync and async variants. Conceptual background is in -:doc:`/concepts/schema-aware-translation`. - -.. list-table:: - :widths: 25 75 - :header-rows: 1 - - * - Class - - Description - * - :ref:`schemaregistry_api` - - Sync registry. Lazy and eager loading, polling for index changes. - * - :ref:`asyncschemaregistry_api` - - Async registry. Coalesced concurrent loads, cancellation-safe. - -.. _schemaregistry_api: - -SchemaRegistry -============== - -.. currentmodule:: sql_redis - -.. autoclass:: SchemaRegistry - :members: - :inherited-members: - -.. _asyncschemaregistry_api: - -AsyncSchemaRegistry -=================== - -.. currentmodule:: sql_redis - -.. autoclass:: AsyncSchemaRegistry - :members: - :inherited-members: diff --git a/docs/api/sql-syntax.md b/docs/api/sql-syntax.md index 52b4335..f6d7f24 100644 --- a/docs/api/sql-syntax.md +++ b/docs/api/sql-syntax.md @@ -54,7 +54,7 @@ The complete catalog of SQL clauses, operators, and functions sql-redis recognis | Negation | `NOT fulltext(title, 'x')` | `-@title:x` | | BM25 score | `score() AS rel` | `WITHSCORES` | -See {doc}`/user_guide/how_to_guides/text-search` for a task-oriented walkthrough. +See [Text search](../user_guide/how_to_guides/text-search.md) for a task-oriented walkthrough. ## GEO @@ -66,7 +66,7 @@ See {doc}`/user_guide/how_to_guides/text-search` for a task-oriented walkthrough | Operators | `<`, `<=`, `>`, `>=`, `BETWEEN` | | In `SELECT` | `geo_distance(loc, POINT(lon, lat)) AS d` | -See {doc}`/user_guide/how_to_guides/geo-queries`. +See [GEO queries](../user_guide/how_to_guides/geo-queries.md). ## Date functions @@ -83,7 +83,7 @@ See {doc}`/user_guide/how_to_guides/geo-queries`. ISO 8601 date and datetime literals in `WHERE` are converted to Unix timestamps automatically. -See {doc}`/user_guide/how_to_guides/date-queries`. +See [Date queries](../user_guide/how_to_guides/date-queries.md). ## Missing fields @@ -96,4 +96,4 @@ See {doc}`/user_guide/how_to_guides/date-queries`. `IS NULL` requires Redis 7.4+ and `INDEXMISSING` on the field. -See {doc}`/user_guide/how_to_guides/missing-fields`. +See [Missing fields](../user_guide/how_to_guides/missing-fields.md). diff --git a/docs/api/translator.md b/docs/api/translator.md new file mode 100644 index 0000000..769189a --- /dev/null +++ b/docs/api/translator.md @@ -0,0 +1,17 @@ +--- +description: Translator that turns SQL into Redis FT.SEARCH or FT.AGGREGATE commands. +--- + +# Translator + +The translator turns a SQL string into a Redis `FT.SEARCH` or `FT.AGGREGATE` +command. It does not execute anything; use [`Executor`][sql_redis.Executor] +for that. + +## Translator + +::: sql_redis.Translator + +## TranslatedQuery + +::: sql_redis.TranslatedQuery diff --git a/docs/api/translator.rst b/docs/api/translator.rst deleted file mode 100644 index 38ffa35..0000000 --- a/docs/api/translator.rst +++ /dev/null @@ -1,23 +0,0 @@ -********** -Translator -********** - -The translator turns a SQL string into a Redis ``FT.SEARCH`` or ``FT.AGGREGATE`` -command. It does not execute anything; use :class:`~sql_redis.Executor` for that. - -Translator -========== - -.. currentmodule:: sql_redis - -.. autoclass:: Translator - :members: - :inherited-members: - -TranslatedQuery -=============== - -.. currentmodule:: sql_redis - -.. autoclass:: TranslatedQuery - :members: diff --git a/docs/assets/favicon.png b/docs/assets/favicon.png new file mode 100644 index 0000000..55f7886 Binary files /dev/null and b/docs/assets/favicon.png differ diff --git a/docs/assets/redis-logo-script.svg b/docs/assets/redis-logo-script.svg new file mode 100644 index 0000000..151ac35 --- /dev/null +++ b/docs/assets/redis-logo-script.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/docs/concepts/architecture.md b/docs/concepts/architecture.md index fe58cb9..7cd9921 100644 --- a/docs/concepts/architecture.md +++ b/docs/concepts/architecture.md @@ -49,10 +49,10 @@ QueryResult (rows, count) ``` - **`SQLParser`** wraps sqlglot. Pure: no Redis dependency. Output is a `ParsedQuery`, the library's own dataclass. -- **`Analyzer`** decides how each `WHERE` condition will translate, based on the underlying field's type. This is the only place the schema registry is consulted during translation. See {doc}`schema-aware-translation` for why this lookup is necessary. +- **`Analyzer`** decides how each `WHERE` condition will translate, based on the underlying field's type. This is the only place the schema registry is consulted during translation. See [Schema-aware translation](schema-aware-translation.md) for why this lookup is necessary. - **`QueryBuilder`** is stateless. Given a tagged condition, it knows how to emit `@field:term`, `@field:[min max]`, `@field:{value}`, and so on. -- **`Translator`** is the orchestrator. It calls parse, analyze, build in order and packages the result into a `TranslatedQuery`. It also decides whether the final command is `FT.SEARCH` or `FT.AGGREGATE` (see {doc}`search-vs-aggregate`). -- **`Executor`** is the only layer that talks to Redis at query time. It substitutes parameters ({doc}`parameter-substitution`), sends the command, parses the reply into rows ({doc}`result-shape`). +- **`Translator`** is the orchestrator. It calls parse, analyze, build in order and packages the result into a `TranslatedQuery`. It also decides whether the final command is `FT.SEARCH` or `FT.AGGREGATE` (see [FT.SEARCH vs FT.AGGREGATE](search-vs-aggregate.md)). +- **`Executor`** is the only layer that talks to Redis at query time. It substitutes parameters ([Parameter substitution](parameter-substitution.md)), sends the command, parses the reply into rows ([Result shape](result-shape.md)). ## Why this layering exists diff --git a/docs/concepts/async-invariants.md b/docs/concepts/async-invariants.md index a0330a3..b8bad5b 100644 --- a/docs/concepts/async-invariants.md +++ b/docs/concepts/async-invariants.md @@ -45,4 +45,4 @@ The following are explicitly **not** guaranteed: ## Reference -The recipes for using these mechanisms (cancellation-safe queries, post-alteration invalidation, change watching) are in {doc}`/user_guide/how_to_guides/async-usage` and {doc}`/user_guide/how_to_guides/lazy-vs-eager-schemas`. +The recipes for using these mechanisms (cancellation-safe queries, post-alteration invalidation, change watching) are in [Async usage](../user_guide/how_to_guides/async-usage.md) and [Lazy vs eager schemas](../user_guide/how_to_guides/lazy-vs-eager-schemas.md). diff --git a/docs/concepts/index.md b/docs/concepts/index.md index 76f69b9..118a4fd 100644 --- a/docs/concepts/index.md +++ b/docs/concepts/index.md @@ -1,101 +1,71 @@ --- -myst: - html_meta: - "description lang=en": | - Concepts behind sql-redis. Architecture and design decisions. +description: Concepts behind sql-redis. Architecture and design decisions. --- # Concepts Foundational reading for sql-redis. Each page explains a single design choice or sub-system, with enough context to make informed extensions or contributions. -::::{grid} 2 -:gutter: 3 +
-:::{grid-item-card} ๐Ÿ—๏ธ Architecture -:link: architecture -:link-type: doc +- :material-sitemap:{ .lg .middle } **[Architecture](architecture.md)** -The two top-level objects (Executor, SchemaRegistry) and the layered translator they contain. -::: + --- -:::{grid-item-card} ๐Ÿค” Why SQL? -:link: why-sql -:link-type: doc + The two top-level objects (Executor, SchemaRegistry) and the layered translator they contain. -The interface choice. SQL versus a pandas-style DSL versus a builder API. -::: +- :material-help-circle:{ .lg .middle } **[Why SQL?](why-sql.md)** -:::{grid-item-card} ๐Ÿช› Why sqlglot? -:link: why-sqlglot -:link-type: doc + --- -The parser choice. sqlglot versus a hand-rolled recursive-descent parser. -::: + The interface choice. SQL versus a pandas-style DSL versus a builder API. -:::{grid-item-card} ๐Ÿ—‚๏ธ Schema-aware translation -:link: schema-aware-translation -:link-type: doc +- :material-tools:{ .lg .middle } **[Why sqlglot?](why-sqlglot.md)** -Why field types matter, how the schema registry caches them, lazy versus eager loading. -::: + --- -:::{grid-item-card} ๐Ÿ”€ FT.SEARCH vs FT.AGGREGATE -:link: search-vs-aggregate -:link-type: doc + The parser choice. sqlglot versus a hand-rolled recursive-descent parser. -Which Redis command runs for a given SQL, why the choice is forced, and which feature combinations are illegal. -::: +- :material-folder-table:{ .lg .middle } **[Schema-aware translation](schema-aware-translation.md)** -:::{grid-item-card} ๐Ÿ”ฃ Parameter substitution -:link: parameter-substitution -:link-type: doc + --- -The token-based substitution algorithm and the bugs it fixes. -::: + Why field types matter, how the schema registry caches them, lazy versus eager loading. -:::{grid-item-card} ๐Ÿงฌ Vector substitution -:link: vector-substitution -:link-type: doc +- :material-source-branch:{ .lg .middle } **[FT.SEARCH vs FT.AGGREGATE](search-vs-aggregate.md)** -Why bytes parameters take a different path: two-stage substitution that keeps vectors out of the SQL string. -::: + --- -:::{grid-item-card} ๐Ÿ” Async invariants -:link: async-invariants -:link-type: doc + Which Redis command runs for a given SQL, why the choice is forced, and which feature combinations are illegal. -Coalesced FT.INFO loads, shielded reads, invalidate-cancels-in-flight. The three guarantees the async path provides. -::: +- :material-variable:{ .lg .middle } **[Parameter substitution](parameter-substitution.md)** -:::{grid-item-card} ๐Ÿ“‹ Result shape -:link: result-shape -:link-type: doc + --- -What QueryResult.rows actually contains, why it varies with the command, scoring, and client decoding. -::: + The token-based substitution algorithm and the bugs it fixes. -:::{grid-item-card} ๐Ÿงช Testing philosophy -:link: testing-philosophy -:link-type: doc +- :material-dna:{ .lg .middle } **[Vector substitution](vector-substitution.md)** -TDD, 100% coverage, and why integration tests do not mock Redis. -::: + --- -:::: + Why bytes parameters take a different path: two-stage substitution that keeps vectors out of the SQL string. -```{toctree} -:maxdepth: 2 -:hidden: +- :material-sync:{ .lg .middle } **[Async invariants](async-invariants.md)** -architecture -why-sql -why-sqlglot -schema-aware-translation -search-vs-aggregate -parameter-substitution -vector-substitution -async-invariants -result-shape -testing-philosophy -``` + --- + + Coalesced FT.INFO loads, shielded reads, invalidate-cancels-in-flight. The three guarantees the async path provides. + +- :material-format-list-bulleted:{ .lg .middle } **[Result shape](result-shape.md)** + + --- + + What `QueryResult.rows` actually contains, why it varies with the command, scoring, and client decoding. + +- :material-test-tube:{ .lg .middle } **[Testing philosophy](testing-philosophy.md)** + + --- + + TDD, 100% coverage, and why integration tests do not mock Redis. + +
diff --git a/docs/concepts/parameter-substitution.md b/docs/concepts/parameter-substitution.md index 4c89e2c..072c282 100644 --- a/docs/concepts/parameter-substitution.md +++ b/docs/concepts/parameter-substitution.md @@ -38,8 +38,8 @@ The sqlglot route would be more general, in particular for the theoretical case A substitution function that handled string and numeric types but not bytes would force vector queries to either pre-encode their vectors as base64 strings (and have RediSearch reject them) or use a side-channel API. Neither is good. -The library's answer is the **two-stage substitution** described in {doc}`vector-substitution`. Briefly: bytes parameters are intentionally *skipped* at the string-substitution stage. The translator emits a `$vector` placeholder, and the executor injects the raw bytes into the Redis command list after translation, where Redis accepts them natively. From the caller's perspective, vector params look identical to other params. +The library's answer is the **two-stage substitution** described in [Vector substitution](vector-substitution.md). Briefly: bytes parameters are intentionally *skipped* at the string-substitution stage. The translator emits a `$vector` placeholder, and the executor injects the raw bytes into the Redis command list after translation, where Redis accepts them natively. From the caller's perspective, vector params look identical to other params. ## What this concept does not cover -The full table of which Python types substitute into what SQL form lives in the how-to ({doc}`/user_guide/how_to_guides/use-parameters`). The reference for the regex pattern itself lives in `sql_redis/executor.py::_substitute_params`. This page is the *why*, not the *how* or *what*. +The full table of which Python types substitute into what SQL form lives in the how-to ([Use parameters](../user_guide/how_to_guides/use-parameters.md)). The reference for the regex pattern itself lives in `sql_redis/executor.py::_substitute_params`. This page is the *why*, not the *how* or *what*. diff --git a/docs/concepts/result-shape.md b/docs/concepts/result-shape.md index 9890aa9..246fb81 100644 --- a/docs/concepts/result-shape.md +++ b/docs/concepts/result-shape.md @@ -4,7 +4,7 @@ Every `Executor.execute()` call returns a `QueryResult` with two attributes: `ro ## What `count` means -The integer in `count` is what Redis returned at position 0 of the reply. Its meaning depends on which command ran (see {doc}`search-vs-aggregate`): +The integer in `count` is what Redis returned at position 0 of the reply. Its meaning depends on which command ran (see [FT.SEARCH vs FT.AGGREGATE](search-vs-aggregate.md)): - **`FT.SEARCH`** (no aggregation): `count` is the **total number of matching documents** in the index, regardless of `LIMIT`. So `count` can be much larger than `len(rows)`. This is useful for pagination. - **`FT.AGGREGATE`** (any aggregation, GROUP BY, computed field, date function): `count` is the **number of rows in the reply**, which is what you got back. After `LIMIT`, the two are equal. diff --git a/docs/concepts/schema-aware-translation.md b/docs/concepts/schema-aware-translation.md index 4cd0137..ef8c2c6 100644 --- a/docs/concepts/schema-aware-translation.md +++ b/docs/concepts/schema-aware-translation.md @@ -32,16 +32,16 @@ The registry can fill its cache in two ways. Both end at the same place; they di **Eager** loads everything at construction: one `FT._LIST` followed by one `FT.INFO` per index. Construction blocks until they all return. Subsequent queries do no schema I/O. The cost moves to startup, but a missing or misspelled index name fails immediately rather than at first use. -The right choice depends on whether startup latency or first-query latency matters more for your workload. Recipes for both modes live in {doc}`/user_guide/how_to_guides/lazy-vs-eager-schemas`. +The right choice depends on whether startup latency or first-query latency matters more for your workload. Recipes for both modes live in [Lazy vs eager schemas](../user_guide/how_to_guides/lazy-vs-eager-schemas.md). ## Cache coherence A cached schema can drift from reality. If you alter or drop an index after the schema has been read, the next translation will be based on the old layout. The library cannot detect this on its own; RediSearch does not emit keyspace notifications for `FT.*` commands. -The user is therefore responsible for invalidating the cache when their index changes. The mechanism is provided as an explicit call rather than automatic, because automatic invalidation would either require polling (expensive) or a hook the application has to wire anyway. The recipe is in {doc}`/user_guide/how_to_guides/lazy-vs-eager-schemas`. +The user is therefore responsible for invalidating the cache when their index changes. The mechanism is provided as an explicit call rather than automatic, because automatic invalidation would either require polling (expensive) or a hook the application has to wire anyway. The recipe is in [Lazy vs eager schemas](../user_guide/how_to_guides/lazy-vs-eager-schemas.md). There is also a polling mode for processes that want to detect index creation and deletion in the background; see the same how-to. ## Async coalescing -In an async process, a burst of requests for a freshly-seen index can produce a thundering herd of `FT.INFO` calls. The async registry deduplicates these: concurrent calls for the same index share a single in-flight request, and only the first caller pays for the round-trip. The concept of in-flight coalescing is part of the broader async story; see {doc}`async-invariants`. +In an async process, a burst of requests for a freshly-seen index can produce a thundering herd of `FT.INFO` calls. The async registry deduplicates these: concurrent calls for the same index share a single in-flight request, and only the first caller pays for the round-trip. The concept of in-flight coalescing is part of the broader async story; see [Async invariants](async-invariants.md). diff --git a/docs/concepts/search-vs-aggregate.md b/docs/concepts/search-vs-aggregate.md index a92cb7e..eb26477 100644 --- a/docs/concepts/search-vs-aggregate.md +++ b/docs/concepts/search-vs-aggregate.md @@ -38,7 +38,7 @@ These constraints are not bugs to fix; they are the cost of the abstraction. The Two practical consequences: -1. **The result-row shape changes.** `FT.SEARCH` returns rows with field-value pairs straight from the indexed documents, possibly with a score column. `FT.AGGREGATE` returns rows of computed fields, group keys, and reduced values; the original document fields are present only if the SQL asks for them. See {doc}`result-shape`. +1. **The result-row shape changes.** `FT.SEARCH` returns rows with field-value pairs straight from the indexed documents, possibly with a score column. `FT.AGGREGATE` returns rows of computed fields, group keys, and reduced values; the original document fields are present only if the SQL asks for them. See [Result shape](result-shape.md). 2. **`LIMIT` semantics differ subtly.** Both commands honour `LIMIT`, but the `count` returned by `FT.AGGREGATE` reflects the post-pipeline row count, while `FT.SEARCH`'s `count` is the total match count regardless of the limit. The library exposes both as `QueryResult.count`; the meaning depends on which path ran. If you need to know which command was issued for a given SQL, call `Translator.translate(sql)` directly and inspect `TranslatedQuery.command`. diff --git a/docs/concepts/testing-philosophy.md b/docs/concepts/testing-philosophy.md index aa8c20f..1e92a4b 100644 --- a/docs/concepts/testing-philosophy.md +++ b/docs/concepts/testing-philosophy.md @@ -17,6 +17,6 @@ Integration tests use `testcontainers[redis]` to start a real Redis with the sea ## Why 100% coverage is achievable here -Because the layers are decoupled (see {doc}`architecture`), each component has a clear contract and a small surface. There are no untestable branches because there are no hidden dependencies. +Because the layers are decoupled (see [Architecture](architecture.md)), each component has a clear contract and a small surface. There are no untestable branches because there are no hidden dependencies. Coverage is enforced in CI. diff --git a/docs/concepts/vector-substitution.md b/docs/concepts/vector-substitution.md index 69079cc..23db3d5 100644 --- a/docs/concepts/vector-substitution.md +++ b/docs/concepts/vector-substitution.md @@ -54,4 +54,4 @@ If multi-vector queries become a thing later, the same scheme generalises: emit - A `bytes` value never appears in the SQL string, so debugging tools that print the substituted SQL will still show `:vec`. - A `bytes` value does *not* go through `_substitute_params` quoting, so it cannot accidentally produce malformed SQL. The downside is that a misnamed `:vec` placeholder is not detected at substitution time; it produces a Redis error at execution. -The user-facing recipe is in {doc}`/user_guide/how_to_guides/use-parameters` ("Vectors") and {doc}`/user_guide/how_to_guides/vector-search`. +The user-facing recipe is in [Use parameters](../user_guide/how_to_guides/use-parameters.md) ("Vectors") and [Vector search](../user_guide/how_to_guides/vector-search.md). diff --git a/docs/conf.py b/docs/conf.py deleted file mode 100644 index 9986781..0000000 --- a/docs/conf.py +++ /dev/null @@ -1,67 +0,0 @@ -"""Sphinx configuration for sql-redis documentation.""" - -import os -import sys - -sys.path.insert(0, os.path.abspath("..")) - -from sql_redis import __version__ - -project = "sql-redis" -copyright = "2026, Redis Inc." -author = "Redis Applied AI" -version = __version__ -release = version - -extensions = [ - "sphinx.ext.autodoc", - "sphinx.ext.napoleon", - "sphinx.ext.viewcode", - "sphinx.ext.intersphinx", - "sphinx_design", - "sphinx_copybutton", - "myst_parser", -] - -source_suffix = { - ".rst": "restructuredtext", - ".md": "markdown", -} - -templates_path = ["_templates"] -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", "SPEC.md"] - -html_theme = "sphinx_book_theme" -html_title = "sql-redis" -html_static_path = ["_static"] if os.path.isdir("_static") else [] - -html_theme_options = { - "repository_url": "https://github.com/redis-developer/sql-redis", - "use_repository_button": True, - "use_edit_page_button": True, - "use_issues_button": True, - "repository_branch": "main", - "path_to_docs": "docs", - "show_navbar_depth": 2, - "navigation_depth": 4, - "show_toc_level": 3, - "home_page_in_toc": True, -} - -myst_enable_extensions = ["colon_fence", "deflist"] -myst_heading_anchors = 3 - -autoclass_content = "both" -autodoc_member_order = "groupwise" -autodoc_typehints = "description" -add_module_names = False - -napoleon_google_docstring = True -napoleon_numpy_docstring = False -napoleon_include_init_with_doc = True - -intersphinx_mapping = { - "python": ("https://docs.python.org/3", None), - "redis": ("https://redis-py.readthedocs.io/en/stable/", None), - "redisvl": ("https://docs.redisvl.com/", None), -} diff --git a/docs/examples/index.md b/docs/examples/index.md index 58a0ab5..602f003 100644 --- a/docs/examples/index.md +++ b/docs/examples/index.md @@ -1,36 +1,31 @@ # Examples -Worked examples and applied patterns built on the sql-redis primitives. The how-to guides under {doc}`/user_guide/how_to_guides/index` are the current source of runnable examples; this section will grow as more end-to-end scenarios are added. +Worked examples and applied patterns built on the sql-redis primitives. The how-to guides under [How-To Guides](../user_guide/how_to_guides/index.md) are the current source of runnable examples; this section will grow as more end-to-end scenarios are added. -::::{grid} 2 -:gutter: 3 +
-:::{grid-item-card} ๐Ÿ” Full-text search -:link: /user_guide/how_to_guides/text-search -:link-type: doc +- :material-magnify:{ .lg .middle } **[Full-text search](../user_guide/how_to_guides/text-search.md)** -Phrase, fuzzy, proximity, BM25. -::: + --- -:::{grid-item-card} ๐Ÿงฎ Vector and hybrid search -:link: /user_guide/how_to_guides/vector-search -:link-type: doc + Phrase, fuzzy, proximity, BM25. -KNN, filter-then-vector. -::: +- :material-vector-square:{ .lg .middle } **[Vector and hybrid search](../user_guide/how_to_guides/vector-search.md)** -:::{grid-item-card} ๐ŸŒ GEO queries -:link: /user_guide/how_to_guides/geo-queries -:link-type: doc + --- -POINT, units, operators. -::: + KNN, filter-then-vector. -:::{grid-item-card} ๐Ÿ“… Date queries -:link: /user_guide/how_to_guides/date-queries -:link-type: doc +- :material-earth:{ .lg .middle } **[GEO queries](../user_guide/how_to_guides/geo-queries.md)** -ISO literals and date functions. -::: + --- -:::: + POINT, units, operators. + +- :material-calendar:{ .lg .middle } **[Date queries](../user_guide/how_to_guides/date-queries.md)** + + --- + + ISO literals and date functions. + +
diff --git a/docs/for-ais-only/BUILD_AND_TEST.md b/docs/for-ais-only/BUILD_AND_TEST.md index fc8a3cd..638eded 100644 --- a/docs/for-ais-only/BUILD_AND_TEST.md +++ b/docs/for-ais-only/BUILD_AND_TEST.md @@ -21,8 +21,8 @@ make test-verbose pytest -vv -s make test-cov pytest with coverage report (terminal + htmlcov/) make check lint + test make build uv build (wheel + sdist) -make docs-build Build Sphinx HTML to docs/_build/html -make docs-serve Serve docs/_build/html on http://localhost:8000 +make docs-build Build MkDocs HTML to ./site (strict mode) +make docs-serve Run mkdocs serve on http://127.0.0.1:8000 make clean Remove caches and build output ``` @@ -49,15 +49,15 @@ open htmlcov/index.html ``` uv sync --group docs -make docs-build # writes docs/_build/html -make docs-serve # http://localhost:8000 +make docs-build # writes ./site +make docs-serve # http://127.0.0.1:8000 ``` -The Sphinx build should complete with zero warnings. Treat any warning as a -breaking change. To enforce this in CI, run with `-W`: +The MkDocs build runs with `--strict`, so any warning is fatal. To run the +strict build directly: ``` -uv run --group docs sphinx-build -W -b html docs docs/_build/html +uv run --group docs mkdocs build --strict ``` ## CI gates (target state) diff --git a/docs/for-ais-only/FAILURE_MODES.md b/docs/for-ais-only/FAILURE_MODES.md index 83f907a..3cac618 100644 --- a/docs/for-ais-only/FAILURE_MODES.md +++ b/docs/for-ais-only/FAILURE_MODES.md @@ -11,7 +11,7 @@ of them. The fix at the call site is to construct `Redis(decode_responses=True)`. Do not "fix" this in `executor.py` by force-decoding; that breaks users who deliberately want bytes (binary fields, vectors). See -{doc}`/concepts/result-shape` for the full story. +[Result shape](../concepts/result-shape.md) for the full story. ## Stopwords are silently stripped @@ -59,7 +59,7 @@ post-invalidate stale write into the cache. The shielded `await` in `ensure_schema()` returns the current cache state when the underlying task is cancelled, so other awaiters do not propagate `CancelledError`. If you "simplify" by removing the shield, you reintroduce a race. See -{doc}`/concepts/async-invariants`. +[Async invariants](../concepts/async-invariants.md). ## Lazy schema-load failures are deferred @@ -74,7 +74,7 @@ This is not a bug. `score()` requires `WITHSCORES`, which is `FT.SEARCH` only. Anything that forces `FT.AGGREGATE` (aggregations, GROUP BY, computed fields, date functions, geo > / >= / BETWEEN, HAVING) cannot coexist with `score()`. The translator surfaces the conflict explicitly rather than -silently dropping one side. See {doc}`/concepts/search-vs-aggregate`. +silently dropping one side. See [FT.SEARCH vs FT.AGGREGATE](../concepts/search-vs-aggregate.md). ## `OR` plus geo > / >= / BETWEEN raises ValueError @@ -87,4 +87,4 @@ date-function predicates combined with `OR`. If CI reports coverage below 100%, do not retry. The failure is real. Either add a test or delete the unreachable branch. The project explicitly forbids -`# pragma: no cover` (see {doc}`/concepts/testing-philosophy`). +`# pragma: no cover` (see [Testing philosophy](../concepts/testing-philosophy.md)). diff --git a/docs/for-ais-only/index.md b/docs/for-ais-only/index.md index 8c0861e..c25fc29 100644 --- a/docs/for-ais-only/index.md +++ b/docs/for-ais-only/index.md @@ -1,24 +1,17 @@ --- -myst: - html_meta: - "description lang=en": | - Internal AI-agent guide to the sql-redis source tree. Repo map, build, test, and failure modes. +description: Internal AI-agent guide to the sql-redis source tree. Repo map, build, test, and failure modes. --- # For AI Agents Modifying sql-redis This section is the internal counterpart to the user-facing -[AGENTS.md](https://github.com/redis-applied-ai/sql-redis/blob/main/AGENTS.md). +[AGENTS.md](https://github.com/redis-developer/sql-redis/blob/main/AGENTS.md). It exists for an agent that has been asked to *change* the library: add a new SQL feature, fix a parser bug, extend the schema registry. -```{toctree} -:maxdepth: 1 - -REPOSITORY_MAP -BUILD_AND_TEST -FAILURE_MODES -``` +- [Repository map](REPOSITORY_MAP.md) +- [Build and test](BUILD_AND_TEST.md) +- [Failure modes](FAILURE_MODES.md) ## Decision tree @@ -29,25 +22,25 @@ Use this to find the right starting point fast. | Add a new SQL clause or operator | [Repository map](REPOSITORY_MAP.md), then `parser.py`, then `analyzer.py`, then `query_builder.py` | | Add a new field type (e.g., a future GEO variant) | [Repository map](REPOSITORY_MAP.md), then `analyzer.py` and `query_builder.py` | | Fix a translation bug | [Repository map](REPOSITORY_MAP.md), then `translator.py`. Run integration tests under `tests/test_sql_queries.py`. | -| Change parameter substitution | {doc}`/concepts/parameter-substitution`, then `executor.py::_substitute_params` | -| Change schema loading semantics | {doc}`/concepts/schema-aware-translation`, then `schema.py` | -| Change async behavior | {doc}`/concepts/async-invariants`, then `executor.py::AsyncExecutor` and `schema.py::AsyncSchemaRegistry` | -| Change the FT.SEARCH/FT.AGGREGATE branching | {doc}`/concepts/search-vs-aggregate`, then `translator.py::translate_parsed` | -| Change the result-row shape | {doc}`/concepts/result-shape`, then `executor.py` (`Executor.execute` parsing branches) | +| Change parameter substitution | [Parameter substitution](../concepts/parameter-substitution.md), then `executor.py::_substitute_params` | +| Change schema loading semantics | [Schema-aware translation](../concepts/schema-aware-translation.md), then `schema.py` | +| Change async behavior | [Async invariants](../concepts/async-invariants.md), then `executor.py::AsyncExecutor` and `schema.py::AsyncSchemaRegistry` | +| Change the FT.SEARCH/FT.AGGREGATE branching | [FT.SEARCH vs FT.AGGREGATE](../concepts/search-vs-aggregate.md), then `translator.py::translate_parsed` | +| Change the result-row shape | [Result shape](../concepts/result-shape.md), then `executor.py` (`Executor.execute` parsing branches) | | Run tests | [Build and test](BUILD_AND_TEST.md) | | Diagnose "this looks broken" | [Failure modes](FAILURE_MODES.md) before assuming a bug | ## Project invariants the agent should preserve 1. **No mocks for Redis in integration tests.** Real `testcontainers[redis]` - only. See {doc}`/concepts/testing-philosophy`. + only. See [Testing philosophy](../concepts/testing-philosophy.md). 2. **100% line coverage is enforced in CI.** No `# pragma: no cover`. If a branch can't be tested, it shouldn't exist. 3. **Public API is what `sql_redis/__init__.py` exports.** Anything else is - internal and can change without notice. The autoclass-driven reference at - `docs/api/` is the contract. + internal and can change without notice. The mkdocstrings-driven reference + at `docs/api/` is the contract. 4. **Docstrings are the single source of truth for the API reference.** - Sphinx `autoclass` reads them directly. If you change a method signature, - update the docstring in the same change. + mkdocstrings reads them directly. If you change a method signature, update + the docstring in the same change. 5. **No emdashes or `--` in prose.** Stylistic rule from the project's - [`CLAUDE.md`](https://github.com/redis-applied-ai/sql-redis/blob/main/CLAUDE.md). + [`CLAUDE.md`](https://github.com/redis-developer/sql-redis/blob/main/CLAUDE.md). diff --git a/docs/index.md b/docs/index.md index e1923ed..4556576 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1,20 +1,15 @@ --- -myst: - html_meta: - "description lang=en": | - sql-redis documentation. SQL to Redis FT.SEARCH and FT.AGGREGATE translator. +description: sql-redis documentation. SQL to Redis FT.SEARCH and FT.AGGREGATE translator. --- # sql-redis -```{admonition} Status: Experimental -:class: warning +!!! warning "Status: Experimental" -sql-redis is part of the [Redis AI Hub](https://redis.io/ai-hub/) under the -**Experimental** tier. The Python API can change between minor releases. The -project is validating its design and SQL surface in real use; we welcome bug -reports and feedback at the [issue tracker](https://github.com/redis-applied-ai/sql-redis/issues). -``` + sql-redis is part of the [Redis AI Hub](https://redis.io/ai-hub/) under the + **Experimental** tier. The Python API can change between minor releases. The + project is validating its design and SQL surface in real use; we welcome bug + reports and feedback at the [issue tracker](https://github.com/redis-developer/sql-redis/issues). Query Redis collections with familiar SQL on top of RediSearch and RedisVL indexes. sql-redis converts SQL `SELECT` statements into Redis `FT.SEARCH` and `FT.AGGREGATE` commands, looking up index schemas via `FT.INFO` so the translation respects the underlying field types. @@ -28,66 +23,45 @@ pip install sql-redis docker run -d --name redis -p 6379:6379 redis:8.4 ``` -โ†’ *{doc}`user_guide/getting-started`* +โ†’ *[Getting Started](user_guide/getting-started.md)* --- ## Explore the Docs -::::{grid} 2 -:gutter: 4 +
+ +- :material-book-open-variant:{ .lg .middle } **[Concepts](concepts/index.md)** + + --- -:::{grid-item-card} ๐Ÿ“– Concepts -:link: concepts/index -:link-type: doc -:class-card: sd-shadow-sm + Understand how sql-redis works. Architecture, design decisions, and the why behind every layer. -Understand how sql-redis works. Architecture, design decisions, and the why behind every layer. -::: +- :material-rocket-launch:{ .lg .middle } **[User Guide](user_guide/index.md)** -:::{grid-item-card} ๐Ÿš€ User Guide -:link: user_guide/index -:link-type: doc -:class-card: sd-shadow-sm + --- -Step by step. Installation, first query, and task-oriented recipes for every feature. -::: + Step by step. Installation, first query, and task-oriented recipes for every feature. -:::{grid-item-card} ๐Ÿ’ก Examples -:link: examples/index -:link-type: doc -:class-card: sd-shadow-sm +- :material-lightbulb-on:{ .lg .middle } **[Examples](examples/index.md)** -Worked examples and patterns built on the sql-redis primitives. -::: + --- -:::{grid-item-card} ๐Ÿ“š API Reference -:link: api/index -:link-type: doc -:class-card: sd-shadow-sm + Worked examples and patterns built on the sql-redis primitives. -Every public class, method, and parameter, generated from docstrings. -::: +- :material-api:{ .lg .middle } **[API Reference](api/index.md)** -:::: + --- + + Every public class, method, and parameter, generated from docstrings. + +
## For AI agents If you are an AI agent reading these docs, start with -[`AGENTS.md`](https://github.com/redis-applied-ai/sql-redis/blob/main/AGENTS.md) +[`AGENTS.md`](https://github.com/redis-developer/sql-redis/blob/main/AGENTS.md) at the repo root for a usage-oriented quick reference, or -{doc}`for-ais-only/index` for an internal map of the source tree. A flat -[`llms.txt`](https://github.com/redis-applied-ai/sql-redis/blob/main/docs/llms.txt) -index of every doc page is also available. - -```{toctree} -:maxdepth: 2 -:hidden: - -Concepts -User Guide -Examples -API -For AI Agents -Changelog -``` +[For AI Agents](for-ais-only/index.md) for an internal map of the source tree. A +flat [`llms.txt`](https://docs.redisvl.com/projects/sql-redis/llms.txt) index of +every doc page is also auto-generated at build time. diff --git a/docs/llms.txt b/docs/llms.txt deleted file mode 100644 index 4345be5..0000000 --- a/docs/llms.txt +++ /dev/null @@ -1,51 +0,0 @@ -# sql-redis - -> Experimental SQL to Redis FT.SEARCH and FT.AGGREGATE translator. Accepts a SQL SELECT string, looks up index schemas via FT.INFO, emits the matching Redis search command, parses the reply into rows. - -For agents using the library, read [AGENTS.md](../AGENTS.md) first. For the full API surface, read [docs/api/](api/). For task-oriented recipes, read [docs/user_guide/how_to_guides/](user_guide/how_to_guides/). - -## Concepts - -- [Architecture](concepts/architecture.md): The two top-level objects (Executor, SchemaRegistry) and the layered translator they contain. -- [Why SQL?](concepts/why-sql.md): Interface choice. SQL versus pandas-style DSL versus a builder API. -- [Why sqlglot?](concepts/why-sqlglot.md): Parser choice. sqlglot versus a hand-rolled recursive-descent parser. -- [Schema-aware translation](concepts/schema-aware-translation.md): Why field types matter, how the registry caches them, lazy versus eager loading, async coalescing. -- [FT.SEARCH versus FT.AGGREGATE](concepts/search-vs-aggregate.md): Which Redis command runs for a given SQL, which feature combinations are illegal, and why. -- [Parameter substitution](concepts/parameter-substitution.md): The token-based substitution algorithm, the two bugs it fixes, and why it is not parser-based. -- [Vector substitution](concepts/vector-substitution.md): Why bytes parameters take a different path. Two-stage substitution that keeps vectors out of the SQL string. -- [Async invariants](concepts/async-invariants.md): The three guarantees the async path provides: coalesced loads, shielded reads, invalidate cancels in-flight. -- [Result shape](concepts/result-shape.md): What QueryResult.rows actually contains, why it varies with the command, scoring, and client decoding. -- [Testing philosophy](concepts/testing-philosophy.md): TDD, 100% coverage, why integration tests use real Redis instead of mocks. - -## User guide - -- [Installation](user_guide/installation.md): pip install, Redis container setup, optional development setup. -- [Getting started](user_guide/getting-started.md): End-to-end first query in five minutes. Index creation through result iteration with expected output. - -## How-to guides - -- [Use parameters](user_guide/how_to_guides/use-parameters.md): Token-based substitution rules, type handling, vector bytes. -- [Vector and hybrid search](user_guide/how_to_guides/vector-search.md): KNN, filter-then-vector, scoring. -- [Full-text search](user_guide/how_to_guides/text-search.md): Phrase, tokenized AND, OR, fuzzy, prefix/suffix/contains, proximity, optional terms, scoring, stopwords. -- [GEO queries](user_guide/how_to_guides/geo-queries.md): POINT(lon, lat), units, all operators, distance in SELECT. -- [Date queries](user_guide/how_to_guides/date-queries.md): ISO 8601 literal conversion, YEAR/MONTH/DAY functions, limitations. -- [Missing fields](user_guide/how_to_guides/missing-fields.md): IS NULL / IS NOT NULL (Redis 7.4+, INDEXMISSING) and the exists() function. -- [Lazy versus eager schemas](user_guide/how_to_guides/lazy-vs-eager-schemas.md): SchemaCacheStrategy choices, invalidation, polling for index changes. -- [Async usage](user_guide/how_to_guides/async-usage.md): AsyncExecutor, AsyncSchemaRegistry.ensure_schema, cancellation safety. - -## API reference - -- [Translator](api/translator.rst): Translator and TranslatedQuery. Turns SQL strings into Redis command lists without executing. -- [Schema registries](api/schema.rst): SchemaRegistry (sync) and AsyncSchemaRegistry. Caches FT.INFO output; supports lazy load, invalidation, change polling. -- [Executor](api/executor.rst): Executor, AsyncExecutor, create_executor, create_async_executor, QueryResult, SchemaCacheStrategy, __version__. -- [SQL syntax catalog](api/sql-syntax.md): Every supported clause, operator, and function with its RediSearch translation. - -## Examples - -- [Examples index](examples/index.md): Pointers to applied scenarios in the how-to guides. - -## For agents modifying sql-redis - -- [Repository map](for-ais-only/REPOSITORY_MAP.md): Module-by-module description of the source tree. -- [Build and test](for-ais-only/BUILD_AND_TEST.md): Make targets, test layout, coverage policy. -- [Failure modes](for-ais-only/FAILURE_MODES.md): Things that look like bugs but are intentional design choices. diff --git a/docs/stylesheets/redis-brand.css b/docs/stylesheets/redis-brand.css new file mode 100644 index 0000000..f790a62 --- /dev/null +++ b/docs/stylesheets/redis-brand.css @@ -0,0 +1,39 @@ +/* Redis brand tokens and Material theme overrides for sql-redis docs. + * Mirrors the redis-docs tailwind palette (Redis red #FF4438, Redis ink + * #091A23) and self-hosts the Space Grotesk + Space Mono pairing used on + * redis.io. + */ +@import url("https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&family=Space+Mono:wght@400;700&display=swap"); + +:root { + /* Mirrors redis-docs tailwind.config.js. */ + --rds-red: #FF4438; /* redis-red-500: primary brand red */ + --rds-red-hover: #D52D1F; /* redis-red-600: hover/pressed */ + --rds-red-bright: #E4291E; /* redis-red-700 */ + --rds-midnight: #091A23; /* redis-ink-900 */ + --rds-ink: #161F31; /* midnight-700 */ + --rds-slate: #2D4754; /* redis-pen-700 */ + --rds-mute: #B9C2C6; /* redis-pen-300 */ + --rds-line: #E8EBEC; /* redis-pen-200 */ + --rds-bg: #FFFFFF; +} + +/* Wire the Redis red into Material's primary/accent slots so the top bar, + * search bar, links, and active nav items pick it up. */ +:root, +[data-md-color-scheme="default"], +[data-md-color-scheme="slate"] { + --md-primary-fg-color: var(--rds-red); + --md-primary-fg-color--light: #FF6B5F; + --md-primary-fg-color--dark: var(--rds-red-hover); + --md-accent-fg-color: var(--rds-red); + --md-accent-fg-color--transparent: rgba(255, 68, 56, 0.1); +} + +/* Bold every top-level entry in the left sidebar. With navigation.sections, + * group labels (Concepts, User Guide, ...) get the section-title treatment, + * but single-page top-level items like Home stay regular weight. This rule + * gives them the same visual weight as the section headers. */ +.md-nav--primary > .md-nav__list > .md-nav__item > .md-nav__link { + font-weight: 700; +} diff --git a/docs/user_guide/getting-started.md b/docs/user_guide/getting-started.md index fc0010d..ebafb12 100644 --- a/docs/user_guide/getting-started.md +++ b/docs/user_guide/getting-started.md @@ -6,7 +6,7 @@ By the end of this walkthrough you will have run a SQL `SELECT` against a Redis Before you start, confirm both: -- Redis 8.x is running locally on port 6379. See {doc}`installation`. +- Redis 8.x is running locally on port 6379. See [Installation](installation.md). - `sql-redis` is installed in the current Python environment (`pip install sql-redis`). If both are true, the following Python snippet should print a version string: @@ -90,9 +90,9 @@ You created an index, loaded data, ran a SQL query, and got rows back. Everythin ## Where next -- Inject runtime values into a query: {doc}`how_to_guides/use-parameters`. -- Search a `TEXT` field beyond simple equality: {doc}`how_to_guides/text-search`. -- Find the K most similar items to a query embedding: {doc}`how_to_guides/vector-search`. -- Filter by geographic distance: {doc}`how_to_guides/geo-queries`. -- Use the async API: {doc}`how_to_guides/async-usage`. -- The full SQL surface: {doc}`/api/sql-syntax`. +- Inject runtime values into a query: [Use parameters](how_to_guides/use-parameters.md). +- Search a `TEXT` field beyond simple equality: [Text search](how_to_guides/text-search.md). +- Find the K most similar items to a query embedding: [Vector search](how_to_guides/vector-search.md). +- Filter by geographic distance: [GEO queries](how_to_guides/geo-queries.md). +- Use the async API: [Async usage](how_to_guides/async-usage.md). +- The full SQL surface: [SQL Syntax](../api/sql-syntax.md). diff --git a/docs/user_guide/how_to_guides/index.md b/docs/user_guide/how_to_guides/index.md index d7e5bda..041cf6c 100644 --- a/docs/user_guide/how_to_guides/index.md +++ b/docs/user_guide/how_to_guides/index.md @@ -1,16 +1,12 @@ # How-To Guides -Task-oriented recipes. Each page assumes you have completed {doc}`/user_guide/getting-started` and answers a single "how do I" question. +Task-oriented recipes. Each page assumes you have completed [Getting Started](../getting-started.md) and answers a single "how do I" question. -```{toctree} -:maxdepth: 1 - -use-parameters -vector-search -text-search -geo-queries -date-queries -missing-fields -lazy-vs-eager-schemas -async-usage -``` +- [Use parameters](use-parameters.md) +- [Vector search](vector-search.md) +- [Text search](text-search.md) +- [GEO queries](geo-queries.md) +- [Date queries](date-queries.md) +- [Missing fields](missing-fields.md) +- [Lazy vs eager schemas](lazy-vs-eager-schemas.md) +- [Async usage](async-usage.md) diff --git a/docs/user_guide/how_to_guides/text-search.md b/docs/user_guide/how_to_guides/text-search.md index 8907959..f6320ec 100644 --- a/docs/user_guide/how_to_guides/text-search.md +++ b/docs/user_guide/how_to_guides/text-search.md @@ -2,7 +2,7 @@ You want to search a `TEXT` field beyond simple equality. Each section below answers one task. -For the complete catalog of supported modes and their RediSearch translation, see {doc}`/api/sql-syntax`. +For the complete catalog of supported modes and their RediSearch translation, see the [SQL Syntax reference](../../api/sql-syntax.md). ## Match an exact phrase @@ -132,7 +132,7 @@ for row in result.rows: print(row[b"title"], row[b"relevance"]) ``` -`score()` triggers `WITHSCORES` in the underlying `FT.SEARCH`. The score is BM25 by default. The result-row shape changes when scoring is enabled; see {doc}`/concepts/result-shape`. +`score()` triggers `WITHSCORES` in the underlying `FT.SEARCH`. The score is BM25 by default. The result-row shape changes when scoring is enabled; see [Result shape](../../concepts/result-shape.md). ## Keep stopwords in matches diff --git a/docs/user_guide/how_to_guides/use-parameters.md b/docs/user_guide/how_to_guides/use-parameters.md index 0947db7..9950b7c 100644 --- a/docs/user_guide/how_to_guides/use-parameters.md +++ b/docs/user_guide/how_to_guides/use-parameters.md @@ -62,4 +62,4 @@ The `bytes` value is intentionally not stringified into the SQL. The executor in ## See also -- {doc}`/concepts/parameter-substitution` for why the substitution is token-based. +- [Parameter substitution](../../concepts/parameter-substitution.md) for why the substitution is token-based. diff --git a/docs/user_guide/index.md b/docs/user_guide/index.md index d9efae8..efc6f50 100644 --- a/docs/user_guide/index.md +++ b/docs/user_guide/index.md @@ -1,43 +1,27 @@ --- -myst: - html_meta: - "description lang=en": | - sql-redis user guide. Installation, getting started, and task-oriented recipes. +description: sql-redis user guide. Installation, getting started, and task-oriented recipes. --- # User Guide -::::{grid} 2 -:gutter: 3 +
-:::{grid-item-card} ๐Ÿ“ฆ Installation -:link: installation -:link-type: doc +- :material-package-variant:{ .lg .middle } **[Installation](installation.md)** -**Set up sql-redis.** pip install, Redis container, optional extras. -::: + --- -:::{grid-item-card} ๐Ÿš€ Getting Started -:link: getting-started -:link-type: doc + Set up sql-redis. pip install, Redis container, optional extras. -**Your first query.** Schema setup, executor construction, end-to-end SELECT. -::: +- :material-rocket-launch:{ .lg .middle } **[Getting Started](getting-started.md)** -:::{grid-item-card} ๐Ÿ› ๏ธ How-To Guides -:link: how_to_guides/index -:link-type: doc + --- -**Solve specific problems.** Recipes for parameters, vectors, text search, GEO, dates, async, and schema strategy. -::: + Your first query. Schema setup, executor construction, end-to-end SELECT. -:::: +- :material-tools:{ .lg .middle } **[How-To Guides](how_to_guides/index.md)** -```{toctree} -:maxdepth: 2 -:hidden: + --- -installation -getting-started -how_to_guides/index -``` + Solve specific problems. Recipes for parameters, vectors, text search, GEO, dates, async, and schema strategy. + +
diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..232e8c4 --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,199 @@ +site_name: sql-redis +site_description: SQL to Redis FT.SEARCH and FT.AGGREGATE translator. +site_url: https://docs.redisvl.com/projects/sql-redis/ +repo_url: https://github.com/redis-developer/sql-redis +repo_name: redis-developer/sql-redis +edit_uri: edit/main/docs/ +copyright: Copyright © 2026 Redis Inc. + +docs_dir: docs + +theme: + name: material + language: en + logo: assets/redis-logo-script.svg + favicon: assets/favicon.png + font: + text: Space Grotesk + code: Space Mono + palette: + - media: "(prefers-color-scheme)" + toggle: + icon: material/brightness-auto + name: Switch to light mode + - media: "(prefers-color-scheme: light)" + scheme: default + primary: custom + accent: custom + toggle: + icon: material/brightness-7 + name: Switch to dark mode + - media: "(prefers-color-scheme: dark)" + scheme: slate + primary: custom + accent: custom + toggle: + icon: material/brightness-4 + name: Switch to system preference + features: + - navigation.instant + - navigation.instant.progress + - navigation.tracking + - navigation.sections + - navigation.indexes + - navigation.top + - navigation.footer + - toc.follow + - search.suggest + - search.highlight + - search.share + - content.code.copy + - content.code.annotate + - content.tabs.link + - content.action.edit + - content.action.view + - content.tooltips + icon: + repo: fontawesome/brands/github + edit: material/pencil + view: material/eye + +extra: + status: + experimental: Experimental + social: + - icon: fontawesome/brands/github + link: https://github.com/redis-developer/sql-redis + - icon: fontawesome/brands/python + link: https://pypi.org/project/sql-redis/ + +extra_css: + - stylesheets/redis-brand.css + +plugins: + - search + - autorefs + - section-index + - git-revision-date-localized: + enable_creation_date: false + type: timeago + fallback_to_build_date: true + - mkdocstrings: + default_handler: python + handlers: + python: + paths: [.] + options: + docstring_style: google + show_source: true + show_root_heading: true + show_root_full_path: false + show_symbol_type_heading: true + show_symbol_type_toc: true + members_order: source + separate_signature: true + show_signature_annotations: true + signature_crossrefs: true + merge_init_into_class: true + docstring_section_style: table + inherited_members: true + filters: + - "!^_" + - llmstxt: + full_output: llms-full.txt + sections: + Concepts: + - concepts/*.md + User Guide: + - user_guide/*.md + - user_guide/how_to_guides/*.md + API Reference: + - api/*.md + For AI Agents: + - for-ais-only/*.md + - redirects: + redirect_maps: {} + +markdown_extensions: + - abbr + - admonition + - attr_list + - def_list + - footnotes + - md_in_html + - tables + - toc: + permalink: true + toc_depth: 3 + - pymdownx.betterem + - pymdownx.caret + - pymdownx.details + - pymdownx.emoji: + emoji_index: !!python/name:material.extensions.emoji.twemoji + emoji_generator: !!python/name:material.extensions.emoji.to_svg + - pymdownx.highlight: + anchor_linenums: true + line_spans: __span + pygments_lang_class: true + - pymdownx.inlinehilite + - pymdownx.keys + - pymdownx.mark + - pymdownx.smartsymbols + - pymdownx.snippets + - pymdownx.superfences: + custom_fences: + - name: mermaid + class: mermaid + format: !!python/name:pymdownx.superfences.fence_code_format + - pymdownx.tabbed: + alternate_style: true + slugify: !!python/object/apply:pymdownx.slugs.slugify {kwds: {case: lower}} + - pymdownx.tasklist: + custom_checkbox: true + - pymdownx.tilde + +watch: + - sql_redis + - AGENTS.md + +nav: + - Home: index.md + - Concepts: + - concepts/index.md + - Architecture: concepts/architecture.md + - Why SQL?: concepts/why-sql.md + - Why sqlglot?: concepts/why-sqlglot.md + - Schema-aware translation: concepts/schema-aware-translation.md + - FT.SEARCH vs FT.AGGREGATE: concepts/search-vs-aggregate.md + - Parameter substitution: concepts/parameter-substitution.md + - Vector substitution: concepts/vector-substitution.md + - Async invariants: concepts/async-invariants.md + - Result shape: concepts/result-shape.md + - Testing philosophy: concepts/testing-philosophy.md + - User Guide: + - user_guide/index.md + - Installation: user_guide/installation.md + - Getting Started: user_guide/getting-started.md + - How-To Guides: + - user_guide/how_to_guides/index.md + - Use parameters: user_guide/how_to_guides/use-parameters.md + - Vector search: user_guide/how_to_guides/vector-search.md + - Text search: user_guide/how_to_guides/text-search.md + - GEO queries: user_guide/how_to_guides/geo-queries.md + - Date queries: user_guide/how_to_guides/date-queries.md + - Missing fields: user_guide/how_to_guides/missing-fields.md + - Lazy vs eager schemas: user_guide/how_to_guides/lazy-vs-eager-schemas.md + - Async usage: user_guide/how_to_guides/async-usage.md + - Examples: examples/index.md + - API Reference: + - api/index.md + - Translator: api/translator.md + - Schema: api/schema.md + - Executor: api/executor.md + - SQL Syntax: api/sql-syntax.md + - For AI Agents: + - for-ais-only/index.md + - Repository map: for-ais-only/REPOSITORY_MAP.md + - Build and test: for-ais-only/BUILD_AND_TEST.md + - Failure modes: for-ais-only/FAILURE_MODES.md + - Changelog: https://github.com/redis-developer/sql-redis/releases diff --git a/pyproject.toml b/pyproject.toml index 99291a2..05ca215 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -48,11 +48,14 @@ dev = [ "testcontainers[redis]>=4.0.0,<5", ] docs = [ - "sphinx>=7.3,<9", - "sphinx-book-theme>=1.1", - "sphinx-design>=0.6", - "sphinx-copybutton>=0.5", - "myst-parser>=3.0", + "mkdocs>=1.6", + "mkdocs-material>=9.5", + "mkdocstrings[python]>=0.26", + "mkdocs-autorefs>=1.2", + "mkdocs-section-index>=0.3", + "mkdocs-git-revision-date-localized-plugin>=1.2", + "mkdocs-llmstxt>=0.2", + "mkdocs-redirects>=1.2", ] [tool.uv] diff --git a/uv.lock b/uv.lock index 1f12e6d..8a0c397 100644 --- a/uv.lock +++ b/uv.lock @@ -1,5 +1,5 @@ version = 1 -revision = 2 +revision = 3 requires-python = ">=3.9, <3.14" resolution-markers = [ "python_full_version >= '3.11'", @@ -9,59 +9,54 @@ resolution-markers = [ ] [[package]] -name = "accessible-pygments" -version = "0.0.5" +name = "async-timeout" +version = "5.0.1" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "pygments" }, +sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/bc/c1/bbac6a50d02774f91572938964c582fff4270eee73ab822a4aeea4d8b11b/accessible_pygments-0.0.5.tar.gz", hash = "sha256:40918d3e6a2b619ad424cb91e556bd3bd8865443d9f22f1dcdf79e33c8046872", size = 1377899, upload-time = "2024-05-10T11:23:10.216Z" } + +[[package]] +name = "babel" +version = "2.18.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/8d/3f/95338030883d8c8b91223b4e21744b04d11b161a3ef117295d8241f50ab4/accessible_pygments-0.0.5-py3-none-any.whl", hash = "sha256:88ae3211e68a1d0b011504b2ffc1691feafce124b845bd072ab6f9f66f34d4b7", size = 1395903, upload-time = "2024-05-10T11:23:08.421Z" }, + { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, ] [[package]] -name = "alabaster" -version = "0.7.16" +name = "backrefs" +version = "6.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.9.2' and python_full_version < '3.10'", "python_full_version < '3.9.2'", ] -sdist = { url = "https://files.pythonhosted.org/packages/c9/3e/13dd8e5ed9094e734ac430b5d0eb4f2bb001708a8b7856cbf8e084e001ba/alabaster-0.7.16.tar.gz", hash = "sha256:75a8b99c28a5dad50dd7f8ccdd447a121ddb3892da9e53d1ca5cca3106d58d65", size = 23776, upload-time = "2024-01-10T00:56:10.189Z" } +sdist = { url = "https://files.pythonhosted.org/packages/4e/a6/e325ec73b638d3ede4421b5445d4a0b8b219481826cc079d510100af356c/backrefs-6.2.tar.gz", hash = "sha256:f44ff4d48808b243b6c0cdc6231e22195c32f77046018141556c66f8bab72a49", size = 7012303, upload-time = "2026-02-16T19:10:15.828Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/34/d4e1c02d3bee589efb5dfa17f88ea08bdb3e3eac12bc475462aec52ed223/alabaster-0.7.16-py3-none-any.whl", hash = "sha256:b46733c07dce03ae4e150330b975c75737fa60f0a7c591b6c8bf4928a28e2c92", size = 13511, upload-time = "2024-01-10T00:56:08.388Z" }, + { url = "https://files.pythonhosted.org/packages/1b/39/3765df263e08a4df37f4f43cb5aa3c6c17a4bdd42ecfe841e04c26037171/backrefs-6.2-py310-none-any.whl", hash = "sha256:0fdc7b012420b6b144410342caeb8adc54c6866cf12064abc9bb211302e496f8", size = 381075, upload-time = "2026-02-16T19:10:04.322Z" }, + { url = "https://files.pythonhosted.org/packages/0f/f0/35240571e1b67ffb19dafb29ab34150b6f59f93f717b041082cdb1bfceb1/backrefs-6.2-py311-none-any.whl", hash = "sha256:08aa7fae530c6b2361d7bdcbda1a7c454e330cc9dbcd03f5c23205e430e5c3be", size = 392874, upload-time = "2026-02-16T19:10:06.314Z" }, + { url = "https://files.pythonhosted.org/packages/e3/63/77e8c9745b4d227cce9f5e0a6f68041278c5f9b18588b35905f5f19c1beb/backrefs-6.2-py312-none-any.whl", hash = "sha256:c3f4b9cb2af8cda0d87ab4f57800b57b95428488477be164dd2b47be54db0c90", size = 398787, upload-time = "2026-02-16T19:10:08.274Z" }, + { url = "https://files.pythonhosted.org/packages/c5/71/c754b1737ad99102e03fa3235acb6cb6d3ac9d6f596cbc3e5f236705abd8/backrefs-6.2-py313-none-any.whl", hash = "sha256:12df81596ab511f783b7d87c043ce26bc5b0288cf3bb03610fe76b8189282b2b", size = 400747, upload-time = "2026-02-16T19:10:09.791Z" }, + { url = "https://files.pythonhosted.org/packages/21/f8/d02f650c47d05034dcd6f9c8cf94f39598b7a89c00ecda0ecb2911bc27e9/backrefs-6.2-py39-none-any.whl", hash = "sha256:664e33cd88c6840b7625b826ecf2555f32d491800900f5a541f772c485f7cda7", size = 381077, upload-time = "2026-02-16T19:10:13.74Z" }, ] [[package]] -name = "alabaster" -version = "1.0.0" +name = "backrefs" +version = "7.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] -sdist = { url = "https://files.pythonhosted.org/packages/a6/f8/d9c74d0daf3f742840fd818d69cfae176fa332022fd44e3469487d5a9420/alabaster-1.0.0.tar.gz", hash = "sha256:c00dca57bca26fa62a6d7d0a9fcce65f3e026e9bfe33e9c538fd3fbb2144fd9e", size = 24210, upload-time = "2024-07-26T18:15:03.762Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/7e/b3/6b4067be973ae96ba0d615946e314c5ae35f9f993eca561b356540bb0c2b/alabaster-1.0.0-py3-none-any.whl", hash = "sha256:fc6786402dc3fcb2de3cabd5fe455a2db534b371124f1f21de8731783dec828b", size = 13929, upload-time = "2024-07-26T18:15:02.05Z" }, -] - -[[package]] -name = "async-timeout" -version = "5.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a5/ae/136395dfbfe00dfc94da3f3e136d0b13f394cba8f4841120e34226265780/async_timeout-5.0.1.tar.gz", hash = "sha256:d9321a7a3d5a6a5e187e824d2fa0793ce379a202935782d555d6e9d2735677d3", size = 9274, upload-time = "2024-11-06T16:41:39.6Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/fe/ba/e2081de779ca30d473f21f5b30e0e737c438205440784c7dfc81efc2b029/async_timeout-5.0.1-py3-none-any.whl", hash = "sha256:39e3809566ff85354557ec2398b55e096c8364bacac9405a7a1fa429e77fe76c", size = 6233, upload-time = "2024-11-06T16:41:37.9Z" }, -] - -[[package]] -name = "babel" -version = "2.18.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/7d/b2/51899539b6ceeeb420d40ed3cd4b7a40519404f9baf3d4ac99dc413a834b/babel-2.18.0.tar.gz", hash = "sha256:b80b99a14bd085fcacfa15c9165f651fbb3406e66cc603abf11c5750937c992d", size = 9959554, upload-time = "2026-02-01T12:30:56.078Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5e/a7/a7dd63622beef68cc0d3c3c36d472e143dd95443d5ebf14cd1a5b4dfbf11/backrefs-7.0.tar.gz", hash = "sha256:4989bb9e1e99eb23647c7160ed51fb21d0b41b5d200f2d3017da41e023097e82", size = 7012453, upload-time = "2026-04-28T16:28:04.215Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/77/f5/21d2de20e8b8b0408f0681956ca2c69f1320a3848ac50e6e7f39c6159675/babel-2.18.0-py3-none-any.whl", hash = "sha256:e2b422b277c2b9a9630c1d7903c2a00d0830c409c59ac8cae9081c92f1aeba35", size = 10196845, upload-time = "2026-02-01T12:30:53.445Z" }, + { url = "https://files.pythonhosted.org/packages/d4/39/39a31d7eae729ea14ed10c3ccef79371197177b9355a86cb3525709e8502/backrefs-7.0-py310-none-any.whl", hash = "sha256:b57cd227ea556b0aed3dc9b8da4628db4eabc0402c6d7fcfc69283a93955f7e9", size = 380824, upload-time = "2026-04-28T16:27:55.647Z" }, + { url = "https://files.pythonhosted.org/packages/c9/b5/9302644225ba7dfa934a2ff2b9c7bb85701313a90dddb3dfaf693fa5bae2/backrefs-7.0-py311-none-any.whl", hash = "sha256:a0fa7360c63509e9e077e174ef4e6d3c21c8db94189b9d957289ae6d794b9475", size = 392626, upload-time = "2026-04-28T16:27:57.42Z" }, + { url = "https://files.pythonhosted.org/packages/36/da/87912ddec6e06feffbaa3d7aa18fc6352bee2e8f1fee185d7d1690f8f4e8/backrefs-7.0-py312-none-any.whl", hash = "sha256:ca42ce6a49ace3d75684dfa9937f3373902a63284ecb385ce36d15e5dcb41c12", size = 398537, upload-time = "2026-04-28T16:27:58.913Z" }, + { url = "https://files.pythonhosted.org/packages/00/bb/90ba423612b6aa0adccc6b1874bcd4a9b44b660c0c16f346611e00f64ac3/backrefs-7.0-py313-none-any.whl", hash = "sha256:f2c52955d631b9e1ac4cd56209f0a3a946d592b98e7790e77699339ae01c102a", size = 400491, upload-time = "2026-04-28T16:28:00.928Z" }, ] [[package]] @@ -536,21 +531,12 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/e3/26/57c6fb270950d476074c087527a558ccb6f4436657314bfb6cdf484114c4/docker-7.1.0-py3-none-any.whl", hash = "sha256:c96b93b7f0a746f9e77d325bcfb87422a3d8bd4f03136ae8a85b37f1898d5fc0", size = 147774, upload-time = "2024-05-23T11:13:55.01Z" }, ] -[[package]] -name = "docutils" -version = "0.21.2" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/ed/aefcc8cd0ba62a0560c3c18c33925362d46c6075480bfa4df87b28e169a9/docutils-0.21.2.tar.gz", hash = "sha256:3a6b18732edf182daa3cd12775bbb338cf5691468f91eeeb109deff6ebfa986f", size = 2204444, upload-time = "2024-04-23T18:57:18.24Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/8f/d7/9322c609343d929e75e7e5e6255e614fcc67572cfd083959cdef3b7aad79/docutils-0.21.2-py3-none-any.whl", hash = "sha256:dafca5b9e384f0e419294eb4d2ff9fa826435bf15f15b7bd45723e8ad76811b2", size = 587408, upload-time = "2024-04-23T18:57:14.835Z" }, -] - [[package]] name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions", marker = "python_full_version < '3.13'" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -584,64 +570,96 @@ wheels = [ ] [[package]] -name = "identify" -version = "2.6.15" +name = "ghp-import" +version = "2.1.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9.2' and python_full_version < '3.10'", - "python_full_version < '3.9.2'", +dependencies = [ + { name = "python-dateutil" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } +sdist = { url = "https://files.pythonhosted.org/packages/d9/29/d40217cbe2f6b1359e00c6c307bb3fc876ba74068cbab3dde77f03ca0dc4/ghp-import-2.1.0.tar.gz", hash = "sha256:9c535c4c61193c2df8871222567d7fd7e5014d835f97dc7b7439069e2413d343", size = 10943, upload-time = "2022-05-02T15:47:16.11Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, + { url = "https://files.pythonhosted.org/packages/f7/ec/67fbef5d497f86283db54c22eec6f6140243aae73265799baaaa19cd17fb/ghp_import-2.1.0-py3-none-any.whl", hash = "sha256:8337dd7b50877f163d4c0289bc1f1c7f127550241988d568c1db512c4324a619", size = 11034, upload-time = "2022-05-02T15:47:14.552Z" }, ] [[package]] -name = "identify" -version = "2.6.16" +name = "gitdb" +version = "4.0.12" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", - "python_full_version == '3.10.*'", +dependencies = [ + { name = "smmap" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360, upload-time = "2026-01-12T18:58:58.201Z" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/63b0fc47eb32792c7ba1fe1b694daec9a63620db1e313033d18140c2320a/gitdb-4.0.12.tar.gz", hash = "sha256:5ef71f855d191a3326fcfbc0d5da835f26b13fbcba60c32c21091c349ffdb571", size = 394684, upload-time = "2025-01-02T07:20:46.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" }, + { url = "https://files.pythonhosted.org/packages/a0/61/5c78b91c3143ed5c14207f463aecfc8f9dbb5092fb2869baf37c273b2705/gitdb-4.0.12-py3-none-any.whl", hash = "sha256:67073e15955400952c6565cc3e707c554a4eea2e428946f7a4c162fab9bd9bcf", size = 62794, upload-time = "2025-01-02T07:20:43.624Z" }, ] [[package]] -name = "idna" -version = "3.11" +name = "gitpython" +version = "3.1.50" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +dependencies = [ + { name = "gitdb" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/f6/354ae6491228b5eb40e10d89c4d13c651fe1cf7556e35ebdded50cff57ce/gitpython-3.1.50.tar.gz", hash = "sha256:80da2d12504d52e1f998772dc5baf6e553f8d2fcfe1fcc226c9d9a2ee3372dcc", size = 219798, upload-time = "2026-05-06T04:01:26.571Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, + { url = "https://files.pythonhosted.org/packages/20/7a/1c6e3562dfd8950adbb11ffbc65d21e7c89d01a6e4f137fa981056de25c5/gitpython-3.1.50-py3-none-any.whl", hash = "sha256:d352abe2908d07355014abdd21ddf798c2a961469239afec4962e9da884858f9", size = 212507, upload-time = "2026-05-06T04:01:23.799Z" }, ] [[package]] -name = "imagesize" -version = "1.5.0" +name = "griffe" +version = "1.14.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ec/d7/6c09dd7ce4c7837e4cdb11dce980cb45ae3cd87677298dc3b781b6bce7d3/griffe-1.14.0.tar.gz", hash = "sha256:9d2a15c1eca966d68e00517de5d69dd1bc5c9f2335ef6c1775362ba5b8651a13", size = 424684, upload-time = "2025-09-05T15:02:29.167Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2a/b1/9ff6578d789a89812ff21e4e0f80ffae20a65d5dd84e7a17873fe3b365be/griffe-1.14.0-py3-none-any.whl", hash = "sha256:0e9d52832cccf0f7188cfe585ba962d2674b241c01916d780925df34873bceb0", size = 144439, upload-time = "2025-09-05T15:02:27.511Z" }, +] + +[[package]] +name = "griffelib" +version = "2.0.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/9d/82/74f4a3310cdabfbb10da554c3a672847f1ed33c6f61dd472681ce7f1fe67/griffelib-2.0.2.tar.gz", hash = "sha256:3cf20b3bc470e83763ffbf236e0076b1211bac1bc67de13daf494640f2de707e", size = 166461, upload-time = "2026-03-27T11:34:51.091Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/11/8c/c9138d881c79aa0ea9ed83cbd58d5ca75624378b38cee225dcf5c42cc91f/griffelib-2.0.2-py3-none-any.whl", hash = "sha256:925c857658fb1ba40c0772c37acbc2ab650bd794d9c1b9726922e36ea4117ea1", size = 142357, upload-time = "2026-03-27T11:34:46.275Z" }, +] + +[[package]] +name = "identify" +version = "2.6.15" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.9.2' and python_full_version < '3.10'", "python_full_version < '3.9.2'", ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/59/4b0dd64676aa6fb4986a755790cb6fc558559cf0084effad516820208ec3/imagesize-1.5.0.tar.gz", hash = "sha256:8bfc5363a7f2133a89f0098451e0bcb1cd71aba4dc02bbcecb39d99d40e1b94f", size = 1281127, upload-time = "2026-03-03T01:59:54.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/e7/685de97986c916a6d93b3876139e00eef26ad5bbbd61925d670ae8013449/identify-2.6.15.tar.gz", hash = "sha256:e4f4864b96c6557ef2a1e1c951771838f4edc9df3a72ec7118b338801b11c7bf", size = 99311, upload-time = "2025-10-02T17:43:40.631Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/1e/b1/a0662b03103c66cf77101a187f396ea91167cd9b7d5d3a2e465ad2c7ee9b/imagesize-1.5.0-py2.py3-none-any.whl", hash = "sha256:32677681b3f434c2cb496f00e89c5a291247b35b1f527589909e008057da5899", size = 5763, upload-time = "2026-03-03T01:59:52.343Z" }, + { url = "https://files.pythonhosted.org/packages/0f/1c/e5fd8f973d4f375adb21565739498e2e9a1e54c858a97b9a8ccfdc81da9b/identify-2.6.15-py2.py3-none-any.whl", hash = "sha256:1181ef7608e00704db228516541eb83a88a9f94433a8c80bb9b5bd54b1d81757", size = 99183, upload-time = "2025-10-02T17:43:39.137Z" }, ] [[package]] -name = "imagesize" -version = "2.0.0" +name = "identify" +version = "2.6.16" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] -sdist = { url = "https://files.pythonhosted.org/packages/6c/e6/7bf14eeb8f8b7251141944835abd42eb20a658d89084b7e1f3e5fe394090/imagesize-2.0.0.tar.gz", hash = "sha256:8e8358c4a05c304f1fccf7ff96f036e7243a189e9e42e90851993c558cfe9ee3", size = 1773045, upload-time = "2026-03-03T14:18:29.941Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/8d/e8b97e6bd3fb6fb271346f7981362f1e04d6a7463abd0de79e1fda17c067/identify-2.6.16.tar.gz", hash = "sha256:846857203b5511bbe94d5a352a48ef2359532bc8f6727b5544077a0dcfb24980", size = 99360, upload-time = "2026-01-12T18:58:58.201Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/58/40fbbcefeda82364720eba5cf2270f98496bdfa19ea75b4cccae79c698e6/identify-2.6.16-py2.py3-none-any.whl", hash = "sha256:391ee4d77741d994189522896270b787aed8670389bfd60f326d677d64a6dfb0", size = 99202, upload-time = "2026-01-12T18:58:56.627Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/53/fb7122b71361a0d121b669dcf3d31244ef75badbbb724af388948de543e2/imagesize-2.0.0-py2.py3-none-any.whl", hash = "sha256:5667c5bbb57ab3f1fa4bc366f4fbc971db3d5ed011fd2715fd8001f782718d96", size = 9441, upload-time = "2026-03-03T14:18:27.892Z" }, + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, ] [[package]] @@ -765,35 +783,57 @@ wheels = [ ] [[package]] -name = "markdown-it-py" -version = "3.0.0" +name = "markdown" +version = "3.9" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version == '3.10.*'", "python_full_version >= '3.9.2' and python_full_version < '3.10'", "python_full_version < '3.9.2'", ] dependencies = [ - { name = "mdurl", marker = "python_full_version < '3.11'" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +sdist = { url = "https://files.pythonhosted.org/packages/8d/37/02347f6d6d8279247a5837082ebc26fc0d5aaeaf75aa013fcbb433c777ab/markdown-3.9.tar.gz", hash = "sha256:d2900fe1782bd33bdbbd56859defef70c2e78fc46668f8eb9df3128138f2cb6a", size = 364585, upload-time = "2025-09-04T20:25:22.885Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, + { url = "https://files.pythonhosted.org/packages/70/ae/44c4a6a4cbb496d93c6257954260fe3a6e91b7bed2240e5dad2a717f5111/markdown-3.9-py3-none-any.whl", hash = "sha256:9f4d91ed810864ea88a6f32c07ba8bee1346c0cc1f6b1f9f6c822f2a9667d280", size = 107441, upload-time = "2025-09-04T20:25:21.784Z" }, ] [[package]] -name = "markdown-it-py" -version = "4.0.0" +name = "markdown" +version = "3.10.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "3.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/38/71/3b932df36c1a044d397a1f92d1cf91ee0a503d91e470cbd670aa66b07ed0/markdown-it-py-3.0.0.tar.gz", hash = "sha256:e3f60a94fa066dc52ec76661e37c851cb232d92f9886b15cb560aaada2df8feb", size = 74596, upload-time = "2023-06-03T06:41:14.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/42/d7/1ec15b46af6af88f19b8e5ffea08fa375d433c998b8a7639e76935c14f1f/markdown_it_py-3.0.0-py3-none-any.whl", hash = "sha256:355216845c60bd96232cd8d8c40e8f9765cc86f46880e43a8fd22dc1a1a8cab1", size = 87528, upload-time = "2023-06-03T06:41:11.019Z" }, ] + +[[package]] +name = "markdownify" +version = "1.2.2" +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "mdurl", marker = "python_full_version >= '3.11'" }, + { name = "beautifulsoup4" }, + { name = "six" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +sdist = { url = "https://files.pythonhosted.org/packages/3f/bc/c8c8eea5335341306b0fa7e1cb33c5e1c8d24ef70ddd684da65f41c49c92/markdownify-1.2.2.tar.gz", hash = "sha256:b274f1b5943180b031b699b199cbaeb1e2ac938b75851849a31fd0c3d6603d09", size = 18816, upload-time = "2025-11-16T19:21:18.565Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, + { url = "https://files.pythonhosted.org/packages/43/ce/f1e3e9d959db134cedf06825fae8d5b294bd368aacdd0831a3975b7c4d55/markdownify-1.2.2-py3-none-any.whl", hash = "sha256:3f02d3cc52714084d6e589f70397b6fc9f2f3a8531481bf35e8cc39f975e186a", size = 15724, upload-time = "2025-11-16T19:21:17.622Z" }, ] [[package]] @@ -871,36 +911,30 @@ wheels = [ ] [[package]] -name = "mdit-py-plugins" -version = "0.4.2" +name = "mdformat" +version = "0.7.22" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9.2' and python_full_version < '3.10'", - "python_full_version < '3.9.2'", -] dependencies = [ - { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "markdown-it-py" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/19/03/a2ecab526543b152300717cf232bb4bb8605b6edb946c845016fa9c9c9fd/mdit_py_plugins-0.4.2.tar.gz", hash = "sha256:5f2cd1fdb606ddf152d37ec30e46101a60512bc0e5fa1a7002c36647b09e26b5", size = 43542, upload-time = "2024-09-09T20:27:49.564Z" } +sdist = { url = "https://files.pythonhosted.org/packages/fc/eb/b5cbf2484411af039a3d4aeb53a5160fae25dd8c84af6a4243bc2f3fedb3/mdformat-0.7.22.tar.gz", hash = "sha256:eef84fa8f233d3162734683c2a8a6222227a229b9206872e6139658d99acb1ea", size = 34610, upload-time = "2025-01-30T18:00:51.418Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/a7/f7/7782a043553ee469c1ff49cfa1cdace2d6bf99a1f333cf38676b3ddf30da/mdit_py_plugins-0.4.2-py3-none-any.whl", hash = "sha256:0c673c3f889399a33b95e88d2f0d111b4447bdfea7f237dab2d488f459835636", size = 55316, upload-time = "2024-09-09T20:27:48.397Z" }, + { url = "https://files.pythonhosted.org/packages/f2/6f/94a7344f6d634fe3563bea8b33bccedee37f2726f7807e9a58440dc91627/mdformat-0.7.22-py3-none-any.whl", hash = "sha256:61122637c9e1d9be1329054f3fa216559f0d1f722b7919b060a8c2a4ae1850e5", size = 34447, upload-time = "2025-01-30T18:00:48.708Z" }, ] [[package]] -name = "mdit-py-plugins" -version = "0.5.0" +name = "mdformat-tables" +version = "1.0.0" source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", - "python_full_version == '3.10.*'", -] dependencies = [ - { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "mdformat" }, + { name = "wcwidth" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/b2/fd/a756d36c0bfba5f6e39a1cdbdbfdd448dc02692467d83816dff4592a1ebc/mdit_py_plugins-0.5.0.tar.gz", hash = "sha256:f4918cb50119f50446560513a8e311d574ff6aaed72606ddae6d35716fe809c6", size = 44655, upload-time = "2025-08-11T07:25:49.083Z" } +sdist = { url = "https://files.pythonhosted.org/packages/64/fc/995ba209096bdebdeb8893d507c7b32b7e07d9a9f2cdc2ec07529947794b/mdformat_tables-1.0.0.tar.gz", hash = "sha256:a57db1ac17c4a125da794ef45539904bb8a9592e80557d525e1f169c96daa2c8", size = 6106, upload-time = "2024-08-23T23:41:33.413Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/fb/86/dd6e5db36df29e76c7a7699123569a4a18c1623ce68d826ed96c62643cae/mdit_py_plugins-0.5.0-py3-none-any.whl", hash = "sha256:07a08422fc1936a5d26d146759e9155ea466e842f5ab2f7d2266dd084c8dab1f", size = 57205, upload-time = "2025-08-11T07:25:47.597Z" }, + { url = "https://files.pythonhosted.org/packages/2a/37/d78e37d14323da3f607cd1af7daf262cb87fe614a245c15ad03bb03a2706/mdformat_tables-1.0.0-py3-none-any.whl", hash = "sha256:94cd86126141b2adc3b04c08d1441eb1272b36c39146bab078249a41c7240a9a", size = 5104, upload-time = "2024-08-23T23:41:31.863Z" }, ] [[package]] @@ -913,267 +947,530 @@ wheels = [ ] [[package]] -name = "mypy" -version = "1.19.1" +name = "mergedeep" +version = "1.3.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3a/41/580bb4006e3ed0361b8151a01d324fb03f420815446c7def45d02f74c270/mergedeep-1.3.4.tar.gz", hash = "sha256:0096d52e9dad9939c3d975a774666af186eda617e6ca84df4c94dec30004f2a8", size = 4661, upload-time = "2021-02-05T18:55:30.623Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2c/19/04f9b178c2d8a15b076c8b5140708fa6ffc5601fb6f1e975537072df5b2a/mergedeep-1.3.4-py3-none-any.whl", hash = "sha256:70775750742b25c0d8f36c55aed03d24c3384d17c951b3175d898bd778ef0307", size = 6354, upload-time = "2021-02-05T18:55:29.583Z" }, +] + +[[package]] +name = "mkdocs" +version = "1.6.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, - { name = "mypy-extensions" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markupsafe" }, + { name = "mergedeep" }, + { name = "mkdocs-get-deps" }, + { name = "packaging" }, { name = "pathspec" }, - { name = "tomli", marker = "python_full_version < '3.11'" }, - { name = "typing-extensions" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/c6/bbd4f061bd16b378247f12953ffcb04786a618ce5e904b8c5a01a0309061/mkdocs-1.6.1.tar.gz", hash = "sha256:7b432f01d928c084353ab39c57282f29f92136665bdd6abf7c1ec8d822ef86f2", size = 3889159, upload-time = "2024-08-30T12:24:06.899Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, - { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, - { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, - { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, - { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, - { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, - { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, - { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, - { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, - { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, - { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, - { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, - { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, - { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, - { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, - { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, - { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, - { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, - { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, - { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, - { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, - { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, - { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, - { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, - { url = "https://files.pythonhosted.org/packages/b5/f7/88436084550ca9af5e610fa45286be04c3b63374df3e021c762fe8c4369f/mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3", size = 13102606, upload-time = "2025-12-15T05:02:46.833Z" }, - { url = "https://files.pythonhosted.org/packages/ca/a5/43dfad311a734b48a752790571fd9e12d61893849a01bff346a54011957f/mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a", size = 12164496, upload-time = "2025-12-15T05:03:41.947Z" }, - { url = "https://files.pythonhosted.org/packages/88/f0/efbfa391395cce2f2771f937e0620cfd185ec88f2b9cd88711028a768e96/mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67", size = 12772068, upload-time = "2025-12-15T05:02:53.689Z" }, - { url = "https://files.pythonhosted.org/packages/25/05/58b3ba28f5aed10479e899a12d2120d582ba9fa6288851b20bf1c32cbb4f/mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e", size = 13520385, upload-time = "2025-12-15T05:02:38.328Z" }, - { url = "https://files.pythonhosted.org/packages/c5/a0/c006ccaff50b31e542ae69b92fe7e2f55d99fba3a55e01067dd564325f85/mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376", size = 13796221, upload-time = "2025-12-15T05:03:22.147Z" }, - { url = "https://files.pythonhosted.org/packages/b2/ff/8bdb051cd710f01b880472241bd36b3f817a8e1c5d5540d0b761675b6de2/mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24", size = 10055456, upload-time = "2025-12-15T05:03:35.169Z" }, - { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, + { url = "https://files.pythonhosted.org/packages/22/5b/dbc6a8cddc9cfa9c4971d59fb12bb8d42e161b7e7f8cc89e49137c5b279c/mkdocs-1.6.1-py3-none-any.whl", hash = "sha256:db91759624d1647f3f34aa0c3f327dd2601beae39a366d6e064c03468d35c20e", size = 3864451, upload-time = "2024-08-30T12:24:05.054Z" }, ] [[package]] -name = "mypy-extensions" -version = "1.1.0" +name = "mkdocs-autorefs" +version = "1.4.4" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +dependencies = [ + { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markupsafe" }, + { name = "mkdocs" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/52/c0/f641843de3f612a6b48253f39244165acff36657a91cc903633d456ae1ac/mkdocs_autorefs-1.4.4.tar.gz", hash = "sha256:d54a284f27a7346b9c38f1f852177940c222da508e66edc816a0fa55fc6da197", size = 56588, upload-time = "2026-02-10T15:23:55.105Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, + { url = "https://files.pythonhosted.org/packages/28/de/a3e710469772c6a89595fc52816da05c1e164b4c866a89e3cb82fb1b67c5/mkdocs_autorefs-1.4.4-py3-none-any.whl", hash = "sha256:834ef5408d827071ad1bc69e0f39704fa34c7fc05bc8e1c72b227dfdc5c76089", size = 25530, upload-time = "2026-02-10T15:23:53.817Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "mergedeep" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/ce/25/b3cccb187655b9393572bde9b09261d267c3bf2f2cdabe347673be5976a6/mkdocs_get_deps-0.2.2.tar.gz", hash = "sha256:8ee8d5f316cdbbb2834bc1df6e69c08fe769a83e040060de26d3c19fad3599a1", size = 11047, upload-time = "2026-03-10T02:46:33.632Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/29/744136411e785c4b0b744d5413e56555265939ab3a104c6a4b719dad33fd/mkdocs_get_deps-0.2.2-py3-none-any.whl", hash = "sha256:e7878cbeac04860b8b5e0ca31d3abad3df9411a75a32cde82f8e44b6c16ff650", size = 9555, upload-time = "2026-03-10T02:46:32.256Z" }, ] [[package]] -name = "myst-parser" -version = "3.0.1" +name = "mkdocs-git-revision-date-localized-plugin" +version = "1.5.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.9.2' and python_full_version < '3.10'", "python_full_version < '3.9.2'", ] dependencies = [ - { name = "docutils", marker = "python_full_version < '3.10'" }, - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "mdit-py-plugins", version = "0.4.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "pyyaml", marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "babel", marker = "python_full_version < '3.10'" }, + { name = "gitpython", marker = "python_full_version < '3.10'" }, + { name = "mkdocs", marker = "python_full_version < '3.10'" }, + { name = "tzdata", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/49/64/e2f13dac02f599980798c01156393b781aec983b52a6e4057ee58f07c43a/myst_parser-3.0.1.tar.gz", hash = "sha256:88f0cb406cb363b077d176b51c476f62d60604d68a8dcdf4832e080441301a87", size = 92392, upload-time = "2024-04-28T20:22:42.116Z" } +sdist = { url = "https://files.pythonhosted.org/packages/0f/c5/1d3c4e6ddae6230b89d09105cb79de711655e3ebd6745f7a92efea0f5160/mkdocs_git_revision_date_localized_plugin-1.5.0.tar.gz", hash = "sha256:17345ccfdf69a1905dc96fb1070dce82d03a1eb6b0d48f958081a7589ce3c248", size = 460697, upload-time = "2025-10-31T16:11:34.44Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/de/21aa8394f16add8f7427f0a1326ccd2b3a2a8a3245c9252bc5ac034c6155/myst_parser-3.0.1-py3-none-any.whl", hash = "sha256:6457aaa33a5d474aca678b8ead9b3dc298e89c68e67012e73146ea6fd54babf1", size = 83163, upload-time = "2024-04-28T20:22:39.985Z" }, + { url = "https://files.pythonhosted.org/packages/bc/51/fe0e3fdb16f6eed65c9459d12bae6a4e1f0bb4e2228cb037e7907b002678/mkdocs_git_revision_date_localized_plugin-1.5.0-py3-none-any.whl", hash = "sha256:933f9e35a8c135b113f21bb57610d82e9b7bcc71dd34fb06a029053c97e99656", size = 26153, upload-time = "2025-10-31T16:11:32.987Z" }, ] [[package]] -name = "myst-parser" -version = "4.0.1" +name = "mkdocs-git-revision-date-localized-plugin" +version = "1.5.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ + "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] dependencies = [ - { name = "docutils", marker = "python_full_version == '3.10.*'" }, - { name = "jinja2", marker = "python_full_version == '3.10.*'" }, - { name = "markdown-it-py", version = "3.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "mdit-py-plugins", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "pyyaml", marker = "python_full_version == '3.10.*'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, + { name = "babel", marker = "python_full_version >= '3.10'" }, + { name = "gitpython", marker = "python_full_version >= '3.10'" }, + { name = "mkdocs", marker = "python_full_version >= '3.10'" }, + { name = "tzdata", marker = "python_full_version >= '3.10' and sys_platform == 'win32'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/66/a5/9626ba4f73555b3735ad86247a8077d4603aa8628537687c839ab08bfe44/myst_parser-4.0.1.tar.gz", hash = "sha256:5cfea715e4f3574138aecbf7d54132296bfd72bb614d31168f48c477a830a7c4", size = 93985, upload-time = "2025-02-12T10:53:03.833Z" } +sdist = { url = "https://files.pythonhosted.org/packages/aa/16/25d7b1b930a802bf8b0c6ee64a9b34ea6e7d0a34c6bc69adbbb59b9d2f4b/mkdocs_git_revision_date_localized_plugin-1.5.1.tar.gz", hash = "sha256:2b0239455cd84784dd87ac8dfc9253fe4b2dd35e102696f21b5d34e2175981c6", size = 449557, upload-time = "2026-01-26T13:34:30.912Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/df/76d0321c3797b54b60fef9ec3bd6f4cfd124b9e422182156a1dd418722cf/myst_parser-4.0.1-py3-none-any.whl", hash = "sha256:9134e88959ec3b5780aedf8a99680ea242869d012e8821db3126d427edc9c95d", size = 84579, upload-time = "2025-02-12T10:53:02.078Z" }, + { url = "https://files.pythonhosted.org/packages/4b/3f/4f663fb7e889fbb2fabef7a67ddd96f8355edca917aa724c6c6cda352d01/mkdocs_git_revision_date_localized_plugin-1.5.1-py3-none-any.whl", hash = "sha256:b00fd36ed0f9b2326b1488fd8fa31bf2ce64e68c4aa60a9ce857f10719571903", size = 26150, upload-time = "2026-01-26T13:34:28.768Z" }, ] [[package]] -name = "myst-parser" -version = "5.0.0" +name = "mkdocs-llmstxt" +version = "0.4.0" source = { registry = "https://pypi.org/simple" } resolution-markers = [ - "python_full_version >= '3.11'", + "python_full_version >= '3.9.2' and python_full_version < '3.10'", + "python_full_version < '3.9.2'", ] dependencies = [ - { name = "docutils", marker = "python_full_version >= '3.11'" }, - { name = "jinja2", marker = "python_full_version >= '3.11'" }, - { name = "markdown-it-py", version = "4.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "mdit-py-plugins", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "pyyaml", marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "beautifulsoup4", marker = "python_full_version < '3.10'" }, + { name = "markdownify", marker = "python_full_version < '3.10'" }, + { name = "mdformat", marker = "python_full_version < '3.10'" }, + { name = "mdformat-tables", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/33/fa/7b45eef11b7971f0beb29d27b7bfe0d747d063aa29e170d9edd004733c8a/myst_parser-5.0.0.tar.gz", hash = "sha256:f6f231452c56e8baa662cc352c548158f6a16fcbd6e3800fc594978002b94f3a", size = 98535, upload-time = "2026-01-15T09:08:18.036Z" } +sdist = { url = "https://files.pythonhosted.org/packages/bc/ed/5790e2b18349e17a61549114690ec6e48f5f673ccf8f4ca396412767ea2a/mkdocs_llmstxt-0.4.0.tar.gz", hash = "sha256:a7e4d20496bc8c55b6773b55c8d69cf552448a9ad38915b6e8c657ae3a46c8b8", size = 32048, upload-time = "2025-10-03T14:08:05.431Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/d3/ac/686789b9145413f1a61878c407210e41bfdb097976864e0913078b24098c/myst_parser-5.0.0-py3-none-any.whl", hash = "sha256:ab31e516024918296e169139072b81592336f2fef55b8986aa31c9f04b5f7211", size = 84533, upload-time = "2026-01-15T09:08:16.788Z" }, + { url = "https://files.pythonhosted.org/packages/5d/70/8b87783592a81685c0422c076525acbb50d0ec71c16554a17ffa375c9d69/mkdocs_llmstxt-0.4.0-py3-none-any.whl", hash = "sha256:7244bf0ac917c9964030c93e9c3e26c02d2d14a0f66fc113416007125b6da0fc", size = 11463, upload-time = "2025-10-03T14:08:03.569Z" }, ] [[package]] -name = "nodeenv" -version = "1.10.0" +name = "mkdocs-llmstxt" +version = "0.5.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "beautifulsoup4", marker = "python_full_version >= '3.10'" }, + { name = "markdownify", marker = "python_full_version >= '3.10'" }, + { name = "mdformat", marker = "python_full_version >= '3.10'" }, + { name = "mdformat-tables", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/7f/f5/4c31cdffa7c09bf48d8c7a50d8342dc100abac98ac4150826bc11afc0c9f/mkdocs_llmstxt-0.5.0.tar.gz", hash = "sha256:b2fa9e6d68df41d7467e948a4745725b6c99434a36b36204857dbd7bb3dfe041", size = 33909, upload-time = "2025-11-20T14:02:24.861Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, + { url = "https://files.pythonhosted.org/packages/ad/2b/82928cc9e8d9269cd79e7ebf015efdc4945e6c646e86ec1d4dba1707f215/mkdocs_llmstxt-0.5.0-py3-none-any.whl", hash = "sha256:753c699913d2d619a9072604b26b6dc9f5fb6d257d9b107857f80c8a0b787533", size = 12040, upload-time = "2025-11-20T14:02:23.483Z" }, ] [[package]] -name = "packaging" -version = "25.0" +name = "mkdocs-material" +version = "9.7.6" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +dependencies = [ + { name = "babel" }, + { name = "backrefs", version = "6.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "backrefs", version = "7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "colorama" }, + { name = "jinja2" }, + { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mkdocs" }, + { name = "mkdocs-material-extensions" }, + { name = "paginate" }, + { name = "pygments" }, + { name = "pymdown-extensions" }, + { name = "requests" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/45/29/6d2bcf41ae40802c4beda2432396fff97b8456fb496371d1bc7aad6512ec/mkdocs_material-9.7.6.tar.gz", hash = "sha256:00bdde50574f776d328b1862fe65daeaf581ec309bd150f7bff345a098c64a69", size = 4097959, upload-time = "2026-03-19T15:41:58.161Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, + { url = "https://files.pythonhosted.org/packages/2c/01/bc663630c510822c95c47a66af9fa7a443c295b47d5f041e5e6ae62ef659/mkdocs_material-9.7.6-py3-none-any.whl", hash = "sha256:71b84353921b8ea1ba84fe11c50912cc512da8fe0881038fcc9a0761c0e635ba", size = 9305470, upload-time = "2026-03-19T15:41:55.217Z" }, ] [[package]] -name = "pathspec" -version = "1.0.3" +name = "mkdocs-material-extensions" +version = "1.3.1" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } +sdist = { url = "https://files.pythonhosted.org/packages/79/9b/9b4c96d6593b2a541e1cb8b34899a6d021d208bb357042823d4d2cabdbe7/mkdocs_material_extensions-1.3.1.tar.gz", hash = "sha256:10c9511cea88f568257f960358a467d12b970e1f7b2c0e5fb2bb48cab1928443", size = 11847, upload-time = "2023-11-22T19:09:45.208Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, + { url = "https://files.pythonhosted.org/packages/5b/54/662a4743aa81d9582ee9339d4ffa3c8fd40a4965e033d77b9da9774d3960/mkdocs_material_extensions-1.3.1-py3-none-any.whl", hash = "sha256:adff8b62700b25cb77b53358dad940f3ef973dd6db797907c49e3c2ef3ab4e31", size = 8728, upload-time = "2023-11-22T19:09:43.465Z" }, ] [[package]] -name = "platformdirs" -version = "4.4.0" +name = "mkdocs-redirects" +version = "1.2.2" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.9.2' and python_full_version < '3.10'", "python_full_version < '3.9.2'", ] -sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +dependencies = [ + { name = "mkdocs", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f1/a8/6d44a6cf07e969c7420cb36ab287b0669da636a2044de38a7d2208d5a758/mkdocs_redirects-1.2.2.tar.gz", hash = "sha256:3094981b42ffab29313c2c1b8ac3969861109f58b2dd58c45fc81cd44bfa0095", size = 7162, upload-time = "2024-11-07T14:57:21.109Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, + { url = "https://files.pythonhosted.org/packages/c4/ec/38443b1f2a3821bbcb24e46cd8ba979154417794d54baf949fefde1c2146/mkdocs_redirects-1.2.2-py3-none-any.whl", hash = "sha256:7dbfa5647b79a3589da4401403d69494bd1f4ad03b9c15136720367e1f340ed5", size = 6142, upload-time = "2024-11-07T14:57:19.143Z" }, ] [[package]] -name = "platformdirs" -version = "4.5.1" +name = "mkdocs-redirects" +version = "1.2.3" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] -sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +dependencies = [ + { name = "mkdocs", marker = "python_full_version >= '3.10'" }, + { name = "properdocs", marker = "python_full_version >= '3.10'" }, ] - -[[package]] -name = "pluggy" -version = "1.6.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +sdist = { url = "https://files.pythonhosted.org/packages/73/25/49725f78ca5d3026b09973f7a2b3a8b179cc2e8c15e43d5a13bc79f6b274/mkdocs_redirects-1.2.3.tar.gz", hash = "sha256:5e980330999299729a2d6a125347d1af78023d68a23681a4de3053ce7dfe2e51", size = 7712, upload-time = "2026-03-28T13:57:41.766Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, + { url = "https://files.pythonhosted.org/packages/c6/90/871b1cddc01d2ba1637b858eeeabc2e3013dc8df591306b5567b98ef0870/mkdocs_redirects-1.2.3-py3-none-any.whl", hash = "sha256:ec7312fff462d03ec16395d0c001006a418f8d0c21cdf2b47ff11cf839dc3ce0", size = 6245, upload-time = "2026-03-28T13:57:40.466Z" }, ] [[package]] -name = "pre-commit" -version = "4.3.0" +name = "mkdocs-section-index" +version = "0.3.11" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.9.2' and python_full_version < '3.10'", "python_full_version < '3.9.2'", ] dependencies = [ - { name = "cfgv", version = "3.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "identify", version = "2.6.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "nodeenv", marker = "python_full_version < '3.10'" }, - { name = "pyyaml", marker = "python_full_version < '3.10'" }, - { name = "virtualenv", marker = "python_full_version < '3.10'" }, + { name = "mkdocs", marker = "python_full_version < '3.10'" }, + { name = "properdocs", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } +sdist = { url = "https://files.pythonhosted.org/packages/c3/65/d35e3269bb3fa67984cc69b51cfa4e467a2a990311c1bad1fe69b5452103/mkdocs_section_index-0.3.11.tar.gz", hash = "sha256:81a5948af0e974bfb474f40b45aeddbb621024ff132eb8ace8854b9db6b41812", size = 14559, upload-time = "2026-03-16T23:28:05.862Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, + { url = "https://files.pythonhosted.org/packages/8b/ce/27b89b0d3e2297f0a41b2350438e69bc30c66155db9fba609db111f058b3/mkdocs_section_index-0.3.11-py3-none-any.whl", hash = "sha256:26f008f4860789e6c41dce868e3e1dcd1528f8cbc1db181416c5edc18f0f15a0", size = 8898, upload-time = "2026-03-16T23:28:04.744Z" }, ] [[package]] -name = "pre-commit" -version = "4.5.1" +name = "mkdocs-section-index" +version = "0.3.12" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", "python_full_version == '3.10.*'", ] dependencies = [ - { name = "cfgv", version = "3.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "identify", version = "2.6.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, - { name = "nodeenv", marker = "python_full_version >= '3.10'" }, - { name = "pyyaml", marker = "python_full_version >= '3.10'" }, - { name = "virtualenv", marker = "python_full_version >= '3.10'" }, + { name = "mkdocs", marker = "python_full_version >= '3.10'" }, + { name = "properdocs", marker = "python_full_version >= '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +sdist = { url = "https://files.pythonhosted.org/packages/f1/e2/64d0f3f054ca8efe61e706006ff5f0d49ad99620c62c2e04818573391c33/mkdocs_section_index-0.3.12.tar.gz", hash = "sha256:285635bf86c643b0fc7a343053d7a818049817bff4408f52b80c4367bd5e7268", size = 14946, upload-time = "2026-04-16T19:20:00.953Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, + { url = "https://files.pythonhosted.org/packages/b0/4d/a330cab5e055d45e924cec69da54a3d8ed37643964f8d1fa1a772b496273/mkdocs_section_index-0.3.12-py3-none-any.whl", hash = "sha256:a1100039546beb4ebef63ce6fc91f3195fb9c0c3763105d4d3d7cd31e0a046eb", size = 8932, upload-time = "2026-04-16T19:19:59.741Z" }, +] + +[[package]] +name = "mkdocstrings" +version = "0.30.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9.2' and python_full_version < '3.10'", + "python_full_version < '3.9.2'", +] +dependencies = [ + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2", marker = "python_full_version < '3.10'" }, + { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markupsafe", marker = "python_full_version < '3.10'" }, + { name = "mkdocs", marker = "python_full_version < '3.10'" }, + { name = "mkdocs-autorefs", marker = "python_full_version < '3.10'" }, + { name = "pymdown-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c5/33/2fa3243439f794e685d3e694590d28469a9b8ea733af4b48c250a3ffc9a0/mkdocstrings-0.30.1.tar.gz", hash = "sha256:84a007aae9b707fb0aebfc9da23db4b26fc9ab562eb56e335e9ec480cb19744f", size = 106350, upload-time = "2025-09-19T10:49:26.446Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/7b/2c/f0dc4e1ee7f618f5bff7e05898d20bf8b6e7fa612038f768bfa295f136a4/mkdocstrings-0.30.1-py3-none-any.whl", hash = "sha256:41bd71f284ca4d44a668816193e4025c950b002252081e387433656ae9a70a82", size = 36704, upload-time = "2025-09-19T10:49:24.805Z" }, +] + +[package.optional-dependencies] +python = [ + { name = "mkdocstrings-python", version = "1.18.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, ] [[package]] -name = "pydata-sphinx-theme" -version = "0.15.4" +name = "mkdocstrings" +version = "1.0.4" source = { registry = "https://pypi.org/simple" } resolution-markers = [ + "python_full_version >= '3.11'", "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "jinja2", marker = "python_full_version >= '3.10'" }, + { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markupsafe", marker = "python_full_version >= '3.10'" }, + { name = "mkdocs", marker = "python_full_version >= '3.10'" }, + { name = "mkdocs-autorefs", marker = "python_full_version >= '3.10'" }, + { name = "pymdown-extensions", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1d/5d/f888d4d3eb31359b327bc9b17a212d6ef03fe0b0682fbb3fc2cb849fb12b/mkdocstrings-1.0.4.tar.gz", hash = "sha256:3969a6515b77db65fd097b53c1b7aa4ae840bd71a2ee62a6a3e89503446d7172", size = 100088, upload-time = "2026-04-15T09:16:53.376Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6e/94/be70f8ee9c45f2f62b39a1f0e9303bc20e138a8f3b8e50ffd89498e177e1/mkdocstrings-1.0.4-py3-none-any.whl", hash = "sha256:63464b4b29053514f32a1dbbf604e52876d5e638111b0c295ab7ed3cac73ca9b", size = 35560, upload-time = "2026-04-15T09:16:51.436Z" }, +] + +[package.optional-dependencies] +python = [ + { name = "mkdocstrings-python", version = "2.0.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, +] + +[[package]] +name = "mkdocstrings-python" +version = "1.18.2" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ "python_full_version >= '3.9.2' and python_full_version < '3.10'", "python_full_version < '3.9.2'", ] dependencies = [ - { name = "accessible-pygments", marker = "python_full_version < '3.11'" }, - { name = "babel", marker = "python_full_version < '3.11'" }, - { name = "beautifulsoup4", marker = "python_full_version < '3.11'" }, - { name = "docutils", marker = "python_full_version < '3.11'" }, - { name = "packaging", marker = "python_full_version < '3.11'" }, - { name = "pygments", marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "typing-extensions", marker = "python_full_version < '3.11'" }, + { name = "griffe", marker = "python_full_version < '3.10'" }, + { name = "mkdocs-autorefs", marker = "python_full_version < '3.10'" }, + { name = "mkdocstrings", version = "0.30.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "typing-extensions", marker = "python_full_version < '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/95/ae/58ab2bfbee2792e92a98b97e872f7c003deb903071f75d8d83aa55db28fa/mkdocstrings_python-1.18.2.tar.gz", hash = "sha256:4ad536920a07b6336f50d4c6d5603316fafb1172c5c882370cbbc954770ad323", size = 207972, upload-time = "2025-08-28T16:11:19.847Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d5/8f/ce008599d9adebf33ed144e7736914385e8537f5fc686fdb7cceb8c22431/mkdocstrings_python-1.18.2-py3-none-any.whl", hash = "sha256:944fe6deb8f08f33fa936d538233c4036e9f53e840994f6146e8e94eb71b600d", size = 138215, upload-time = "2025-08-28T16:11:18.176Z" }, +] + +[[package]] +name = "mkdocstrings-python" +version = "2.0.3" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "griffelib", marker = "python_full_version >= '3.10'" }, + { name = "mkdocs-autorefs", marker = "python_full_version >= '3.10'" }, + { name = "mkdocstrings", version = "1.0.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "typing-extensions", marker = "python_full_version == '3.10.*'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/29/33/c225eaf898634bdda489a6766fc35d1683c640bffe0e0acd10646b13536d/mkdocstrings_python-2.0.3.tar.gz", hash = "sha256:c518632751cc869439b31c9d3177678ad2bfa5c21b79b863956ad68fc92c13b8", size = 199083, upload-time = "2026-02-20T10:38:36.368Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/28/79f0f8de97cce916d5ae88a7bee1ad724855e83e6019c0b4d5b3fabc80f3/mkdocstrings_python-2.0.3-py3-none-any.whl", hash = "sha256:0b83513478bdfd803ff05aa43e9b1fca9dd22bcd9471f09ca6257f009bc5ee12", size = 104779, upload-time = "2026-02-20T10:38:34.517Z" }, +] + +[[package]] +name = "mypy" +version = "1.19.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "librt", marker = "platform_python_implementation != 'PyPy'" }, + { name = "mypy-extensions" }, + { name = "pathspec" }, + { name = "tomli", marker = "python_full_version < '3.11'" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/f5/db/4efed9504bc01309ab9c2da7e352cc223569f05478012b5d9ece38fd44d2/mypy-1.19.1.tar.gz", hash = "sha256:19d88bb05303fe63f71dd2c6270daca27cb9401c4ca8255fe50d1d920e0eb9ba", size = 3582404, upload-time = "2025-12-15T05:03:48.42Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/2f/63/e499890d8e39b1ff2df4c0c6ce5d371b6844ee22b8250687a99fd2f657a8/mypy-1.19.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5f05aa3d375b385734388e844bc01733bd33c644ab48e9684faa54e5389775ec", size = 13101333, upload-time = "2025-12-15T05:03:03.28Z" }, + { url = "https://files.pythonhosted.org/packages/72/4b/095626fc136fba96effc4fd4a82b41d688ab92124f8c4f7564bffe5cf1b0/mypy-1.19.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:022ea7279374af1a5d78dfcab853fe6a536eebfda4b59deab53cd21f6cd9f00b", size = 12164102, upload-time = "2025-12-15T05:02:33.611Z" }, + { url = "https://files.pythonhosted.org/packages/0c/5b/952928dd081bf88a83a5ccd49aaecfcd18fd0d2710c7ff07b8fb6f7032b9/mypy-1.19.1-cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ee4c11e460685c3e0c64a4c5de82ae143622410950d6be863303a1c4ba0e36d6", size = 12765799, upload-time = "2025-12-15T05:03:28.44Z" }, + { url = "https://files.pythonhosted.org/packages/2a/0d/93c2e4a287f74ef11a66fb6d49c7a9f05e47b0a4399040e6719b57f500d2/mypy-1.19.1-cp310-cp310-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:de759aafbae8763283b2ee5869c7255391fbc4de3ff171f8f030b5ec48381b74", size = 13522149, upload-time = "2025-12-15T05:02:36.011Z" }, + { url = "https://files.pythonhosted.org/packages/7b/0e/33a294b56aaad2b338d203e3a1d8b453637ac36cb278b45005e0901cf148/mypy-1.19.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:ab43590f9cd5108f41aacf9fca31841142c786827a74ab7cc8a2eacb634e09a1", size = 13810105, upload-time = "2025-12-15T05:02:40.327Z" }, + { url = "https://files.pythonhosted.org/packages/0e/fd/3e82603a0cb66b67c5e7abababce6bf1a929ddf67bf445e652684af5c5a0/mypy-1.19.1-cp310-cp310-win_amd64.whl", hash = "sha256:2899753e2f61e571b3971747e302d5f420c3fd09650e1951e99f823bc3089dac", size = 10057200, upload-time = "2025-12-15T05:02:51.012Z" }, + { url = "https://files.pythonhosted.org/packages/ef/47/6b3ebabd5474d9cdc170d1342fbf9dddc1b0ec13ec90bf9004ee6f391c31/mypy-1.19.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d8dfc6ab58ca7dda47d9237349157500468e404b17213d44fc1cb77bce532288", size = 13028539, upload-time = "2025-12-15T05:03:44.129Z" }, + { url = "https://files.pythonhosted.org/packages/5c/a6/ac7c7a88a3c9c54334f53a941b765e6ec6c4ebd65d3fe8cdcfbe0d0fd7db/mypy-1.19.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e3f276d8493c3c97930e354b2595a44a21348b320d859fb4a2b9f66da9ed27ab", size = 12083163, upload-time = "2025-12-15T05:03:37.679Z" }, + { url = "https://files.pythonhosted.org/packages/67/af/3afa9cf880aa4a2c803798ac24f1d11ef72a0c8079689fac5cfd815e2830/mypy-1.19.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2abb24cf3f17864770d18d673c85235ba52456b36a06b6afc1e07c1fdcd3d0e6", size = 12687629, upload-time = "2025-12-15T05:02:31.526Z" }, + { url = "https://files.pythonhosted.org/packages/2d/46/20f8a7114a56484ab268b0ab372461cb3a8f7deed31ea96b83a4e4cfcfca/mypy-1.19.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a009ffa5a621762d0c926a078c2d639104becab69e79538a494bcccb62cc0331", size = 13436933, upload-time = "2025-12-15T05:03:15.606Z" }, + { url = "https://files.pythonhosted.org/packages/5b/f8/33b291ea85050a21f15da910002460f1f445f8007adb29230f0adea279cb/mypy-1.19.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:f7cee03c9a2e2ee26ec07479f38ea9c884e301d42c6d43a19d20fb014e3ba925", size = 13661754, upload-time = "2025-12-15T05:02:26.731Z" }, + { url = "https://files.pythonhosted.org/packages/fd/a3/47cbd4e85bec4335a9cd80cf67dbc02be21b5d4c9c23ad6b95d6c5196bac/mypy-1.19.1-cp311-cp311-win_amd64.whl", hash = "sha256:4b84a7a18f41e167f7995200a1d07a4a6810e89d29859df936f1c3923d263042", size = 10055772, upload-time = "2025-12-15T05:03:26.179Z" }, + { url = "https://files.pythonhosted.org/packages/06/8a/19bfae96f6615aa8a0604915512e0289b1fad33d5909bf7244f02935d33a/mypy-1.19.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a8174a03289288c1f6c46d55cef02379b478bfbc8e358e02047487cad44c6ca1", size = 13206053, upload-time = "2025-12-15T05:03:46.622Z" }, + { url = "https://files.pythonhosted.org/packages/a5/34/3e63879ab041602154ba2a9f99817bb0c85c4df19a23a1443c8986e4d565/mypy-1.19.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ffcebe56eb09ff0c0885e750036a095e23793ba6c2e894e7e63f6d89ad51f22e", size = 12219134, upload-time = "2025-12-15T05:03:24.367Z" }, + { url = "https://files.pythonhosted.org/packages/89/cc/2db6f0e95366b630364e09845672dbee0cbf0bbe753a204b29a944967cd9/mypy-1.19.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b64d987153888790bcdb03a6473d321820597ab8dd9243b27a92153c4fa50fd2", size = 12731616, upload-time = "2025-12-15T05:02:44.725Z" }, + { url = "https://files.pythonhosted.org/packages/00/be/dd56c1fd4807bc1eba1cf18b2a850d0de7bacb55e158755eb79f77c41f8e/mypy-1.19.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c35d298c2c4bba75feb2195655dfea8124d855dfd7343bf8b8c055421eaf0cf8", size = 13620847, upload-time = "2025-12-15T05:03:39.633Z" }, + { url = "https://files.pythonhosted.org/packages/6d/42/332951aae42b79329f743bf1da088cd75d8d4d9acc18fbcbd84f26c1af4e/mypy-1.19.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:34c81968774648ab5ac09c29a375fdede03ba253f8f8287847bd480782f73a6a", size = 13834976, upload-time = "2025-12-15T05:03:08.786Z" }, + { url = "https://files.pythonhosted.org/packages/6f/63/e7493e5f90e1e085c562bb06e2eb32cae27c5057b9653348d38b47daaecc/mypy-1.19.1-cp312-cp312-win_amd64.whl", hash = "sha256:b10e7c2cd7870ba4ad9b2d8a6102eb5ffc1f16ca35e3de6bfa390c1113029d13", size = 10118104, upload-time = "2025-12-15T05:03:10.834Z" }, + { url = "https://files.pythonhosted.org/packages/de/9f/a6abae693f7a0c697dbb435aac52e958dc8da44e92e08ba88d2e42326176/mypy-1.19.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:e3157c7594ff2ef1634ee058aafc56a82db665c9438fd41b390f3bde1ab12250", size = 13201927, upload-time = "2025-12-15T05:02:29.138Z" }, + { url = "https://files.pythonhosted.org/packages/9a/a4/45c35ccf6e1c65afc23a069f50e2c66f46bd3798cbe0d680c12d12935caa/mypy-1.19.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:bdb12f69bcc02700c2b47e070238f42cb87f18c0bc1fc4cdb4fb2bc5fd7a3b8b", size = 12206730, upload-time = "2025-12-15T05:03:01.325Z" }, + { url = "https://files.pythonhosted.org/packages/05/bb/cdcf89678e26b187650512620eec8368fded4cfd99cfcb431e4cdfd19dec/mypy-1.19.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f859fb09d9583a985be9a493d5cfc5515b56b08f7447759a0c5deaf68d80506e", size = 12724581, upload-time = "2025-12-15T05:03:20.087Z" }, + { url = "https://files.pythonhosted.org/packages/d1/32/dd260d52babf67bad8e6770f8e1102021877ce0edea106e72df5626bb0ec/mypy-1.19.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c9a6538e0415310aad77cb94004ca6482330fece18036b5f360b62c45814c4ef", size = 13616252, upload-time = "2025-12-15T05:02:49.036Z" }, + { url = "https://files.pythonhosted.org/packages/71/d0/5e60a9d2e3bd48432ae2b454b7ef2b62a960ab51292b1eda2a95edd78198/mypy-1.19.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:da4869fc5e7f62a88f3fe0b5c919d1d9f7ea3cef92d3689de2823fd27e40aa75", size = 13840848, upload-time = "2025-12-15T05:02:55.95Z" }, + { url = "https://files.pythonhosted.org/packages/98/76/d32051fa65ecf6cc8c6610956473abdc9b4c43301107476ac03559507843/mypy-1.19.1-cp313-cp313-win_amd64.whl", hash = "sha256:016f2246209095e8eda7538944daa1d60e1e8134d98983b9fc1e92c1fc0cb8dd", size = 10135510, upload-time = "2025-12-15T05:02:58.438Z" }, + { url = "https://files.pythonhosted.org/packages/b5/f7/88436084550ca9af5e610fa45286be04c3b63374df3e021c762fe8c4369f/mypy-1.19.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7bcfc336a03a1aaa26dfce9fff3e287a3ba99872a157561cbfcebe67c13308e3", size = 13102606, upload-time = "2025-12-15T05:02:46.833Z" }, + { url = "https://files.pythonhosted.org/packages/ca/a5/43dfad311a734b48a752790571fd9e12d61893849a01bff346a54011957f/mypy-1.19.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b7951a701c07ea584c4fe327834b92a30825514c868b1f69c30445093fdd9d5a", size = 12164496, upload-time = "2025-12-15T05:03:41.947Z" }, + { url = "https://files.pythonhosted.org/packages/88/f0/efbfa391395cce2f2771f937e0620cfd185ec88f2b9cd88711028a768e96/mypy-1.19.1-cp39-cp39-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b13cfdd6c87fc3efb69ea4ec18ef79c74c3f98b4e5498ca9b85ab3b2c2329a67", size = 12772068, upload-time = "2025-12-15T05:02:53.689Z" }, + { url = "https://files.pythonhosted.org/packages/25/05/58b3ba28f5aed10479e899a12d2120d582ba9fa6288851b20bf1c32cbb4f/mypy-1.19.1-cp39-cp39-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4f28f99c824ecebcdaa2e55d82953e38ff60ee5ec938476796636b86afa3956e", size = 13520385, upload-time = "2025-12-15T05:02:38.328Z" }, + { url = "https://files.pythonhosted.org/packages/c5/a0/c006ccaff50b31e542ae69b92fe7e2f55d99fba3a55e01067dd564325f85/mypy-1.19.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:c608937067d2fc5a4dd1a5ce92fd9e1398691b8c5d012d66e1ddd430e9244376", size = 13796221, upload-time = "2025-12-15T05:03:22.147Z" }, + { url = "https://files.pythonhosted.org/packages/b2/ff/8bdb051cd710f01b880472241bd36b3f817a8e1c5d5540d0b761675b6de2/mypy-1.19.1-cp39-cp39-win_amd64.whl", hash = "sha256:409088884802d511ee52ca067707b90c883426bd95514e8cfda8281dc2effe24", size = 10055456, upload-time = "2025-12-15T05:03:35.169Z" }, + { url = "https://files.pythonhosted.org/packages/8d/f4/4ce9a05ce5ded1de3ec1c1d96cf9f9504a04e54ce0ed55cfa38619a32b8d/mypy-1.19.1-py3-none-any.whl", hash = "sha256:f1235f5ea01b7db5468d53ece6aaddf1ad0b88d9e7462b86ef96fe04995d7247", size = 2471239, upload-time = "2025-12-15T05:03:07.248Z" }, +] + +[[package]] +name = "mypy-extensions" +version = "1.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a2/6e/371856a3fb9d31ca8dac321cda606860fa4548858c0cc45d9d1d4ca2628b/mypy_extensions-1.1.0.tar.gz", hash = "sha256:52e68efc3284861e772bbcd66823fde5ae21fd2fdb51c62a211403730b916558", size = 6343, upload-time = "2025-04-22T14:54:24.164Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/79/7b/2c79738432f5c924bef5071f933bcc9efd0473bac3b4aa584a6f7c1c8df8/mypy_extensions-1.1.0-py3-none-any.whl", hash = "sha256:1be4cccdb0f2482337c4743e60421de3a356cd97508abadd57d47403e94f5505", size = 4963, upload-time = "2025-04-22T14:54:22.983Z" }, +] + +[[package]] +name = "nodeenv" +version = "1.10.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, +] + +[[package]] +name = "packaging" +version = "25.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727, upload-time = "2025-04-19T11:48:59.673Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469, upload-time = "2025-04-19T11:48:57.875Z" }, +] + +[[package]] +name = "paginate" +version = "0.5.7" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/46/68dde5b6bc00c1296ec6466ab27dddede6aec9af1b99090e1107091b3b84/paginate-0.5.7.tar.gz", hash = "sha256:22bd083ab41e1a8b4f3690544afb2c60c25e5c9a63a30fa2f483f6c60c8e5945", size = 19252, upload-time = "2024-08-25T14:17:24.139Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/90/96/04b8e52da071d28f5e21a805b19cb9390aa17a47462ac87f5e2696b9566d/paginate-0.5.7-py2.py3-none-any.whl", hash = "sha256:b885e2af73abcf01d9559fd5216b57ef722f8c42affbb63942377668e35c7591", size = 13746, upload-time = "2024-08-25T14:17:22.55Z" }, +] + +[[package]] +name = "pathspec" +version = "1.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4c/b2/bb8e495d5262bfec41ab5cb18f522f1012933347fb5d9e62452d446baca2/pathspec-1.0.3.tar.gz", hash = "sha256:bac5cf97ae2c2876e2d25ebb15078eb04d76e4b98921ee31c6f85ade8b59444d", size = 130841, upload-time = "2026-01-09T15:46:46.009Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/32/2b/121e912bd60eebd623f873fd090de0e84f322972ab25a7f9044c056804ed/pathspec-1.0.3-py3-none-any.whl", hash = "sha256:e80767021c1cc524aa3fb14bedda9c34406591343cc42797b386ce7b9354fb6c", size = 55021, upload-time = "2026-01-09T15:46:44.652Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.4.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9.2' and python_full_version < '3.10'", + "python_full_version < '3.9.2'", +] +sdist = { url = "https://files.pythonhosted.org/packages/23/e8/21db9c9987b0e728855bd57bff6984f67952bea55d6f75e055c46b5383e8/platformdirs-4.4.0.tar.gz", hash = "sha256:ca753cf4d81dc309bc67b0ea38fd15dc97bc30ce419a7f58d13eb3bf14c4febf", size = 21634, upload-time = "2025-08-26T14:32:04.268Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/4b/2028861e724d3bd36227adfa20d3fd24c3fc6d52032f4a93c133be5d17ce/platformdirs-4.4.0-py3-none-any.whl", hash = "sha256:abd01743f24e5287cd7a5db3752faf1a2d65353f38ec26d98e25a6db65958c85", size = 18654, upload-time = "2025-08-26T14:32:02.735Z" }, +] + +[[package]] +name = "platformdirs" +version = "4.5.1" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +sdist = { url = "https://files.pythonhosted.org/packages/cf/86/0248f086a84f01b37aaec0fa567b397df1a119f73c16f6c7a9aac73ea309/platformdirs-4.5.1.tar.gz", hash = "sha256:61d5cdcc6065745cdd94f0f878977f8de9437be93de97c1c12f853c9c0cdcbda", size = 21715, upload-time = "2025-12-05T13:52:58.638Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/28/3bfe2fa5a7b9c46fe7e13c97bda14c895fb10fa2ebf1d0abb90e0cea7ee1/platformdirs-4.5.1-py3-none-any.whl", hash = "sha256:d03afa3963c806a9bed9d5125c8f4cb2fdaf74a55ab60e5d59b3fde758104d31", size = 18731, upload-time = "2025-12-05T13:52:56.823Z" }, +] + +[[package]] +name = "pluggy" +version = "1.6.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/f9/e2/3e91f31a7d2b083fe6ef3fa267035b518369d9511ffab804f839851d2779/pluggy-1.6.0.tar.gz", hash = "sha256:7dcc130b76258d33b90f61b658791dede3486c3e6bfb003ee5c9bfb396dd22f3", size = 69412, upload-time = "2025-05-15T12:30:07.975Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/54/20/4d324d65cc6d9205fabedc306948156824eb9f0ee1633355a8f7ec5c66bf/pluggy-1.6.0-py3-none-any.whl", hash = "sha256:e920276dd6813095e9377c0bc5566d94c932c33b27a3e3945d8389c374dd4746", size = 20538, upload-time = "2025-05-15T12:30:06.134Z" }, +] + +[[package]] +name = "pre-commit" +version = "4.3.0" +source = { registry = "https://pypi.org/simple" } +resolution-markers = [ + "python_full_version >= '3.9.2' and python_full_version < '3.10'", + "python_full_version < '3.9.2'", +] +dependencies = [ + { name = "cfgv", version = "3.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "identify", version = "2.6.15", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "nodeenv", marker = "python_full_version < '3.10'" }, + { name = "pyyaml", marker = "python_full_version < '3.10'" }, + { name = "virtualenv", marker = "python_full_version < '3.10'" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/67/ea/3ab478cccacc2e8ef69892c42c44ae547bae089f356c4b47caf61730958d/pydata_sphinx_theme-0.15.4.tar.gz", hash = "sha256:7762ec0ac59df3acecf49fd2f889e1b4565dbce8b88b2e29ee06fdd90645a06d", size = 2400673, upload-time = "2024-06-25T19:28:45.041Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ff/29/7cf5bbc236333876e4b41f56e06857a87937ce4bf91e117a6991a2dbb02a/pre_commit-4.3.0.tar.gz", hash = "sha256:499fe450cc9d42e9d58e606262795ecb64dd05438943c62b66f6a8673da30b16", size = 193792, upload-time = "2025-08-09T18:56:14.651Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e7/d3/c622950d87a2ffd1654208733b5bd1c5645930014abed8f4c0d74863988b/pydata_sphinx_theme-0.15.4-py3-none-any.whl", hash = "sha256:2136ad0e9500d0949f96167e63f3e298620040aea8f9c74621959eda5d4cf8e6", size = 4640157, upload-time = "2024-06-25T19:28:42.383Z" }, + { url = "https://files.pythonhosted.org/packages/5b/a5/987a405322d78a73b66e39e4a90e4ef156fd7141bf71df987e50717c321b/pre_commit-4.3.0-py2.py3-none-any.whl", hash = "sha256:2b0747ad7e6e967169136edffee14c16e148a778a54e4f967921aa1ebf2308d8", size = 220965, upload-time = "2025-08-09T18:56:13.192Z" }, ] [[package]] -name = "pydata-sphinx-theme" -version = "0.16.1" +name = "pre-commit" +version = "4.5.1" source = { registry = "https://pypi.org/simple" } resolution-markers = [ "python_full_version >= '3.11'", + "python_full_version == '3.10.*'", +] +dependencies = [ + { name = "cfgv", version = "3.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "identify", version = "2.6.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "nodeenv", marker = "python_full_version >= '3.10'" }, + { name = "pyyaml", marker = "python_full_version >= '3.10'" }, + { name = "virtualenv", marker = "python_full_version >= '3.10'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/40/f1/6d86a29246dfd2e9b6237f0b5823717f60cad94d47ddc26afa916d21f525/pre_commit-4.5.1.tar.gz", hash = "sha256:eb545fcff725875197837263e977ea257a402056661f09dae08e4b149b030a61", size = 198232, upload-time = "2025-12-16T21:14:33.552Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5d/19/fd3ef348460c80af7bb4669ea7926651d1f95c23ff2df18b9d24bab4f3fa/pre_commit-4.5.1-py2.py3-none-any.whl", hash = "sha256:3b3afd891e97337708c1674210f8eba659b52a38ea5f822ff142d10786221f77", size = 226437, upload-time = "2025-12-16T21:14:32.409Z" }, ] + +[[package]] +name = "properdocs" +version = "1.6.7" +source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "accessible-pygments", marker = "python_full_version >= '3.11'" }, - { name = "babel", marker = "python_full_version >= '3.11'" }, - { name = "beautifulsoup4", marker = "python_full_version >= '3.11'" }, - { name = "docutils", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "typing-extensions", marker = "python_full_version >= '3.11'" }, + { name = "click", version = "8.1.8", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "click", version = "8.3.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "colorama", marker = "sys_platform == 'win32'" }, + { name = "ghp-import" }, + { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, + { name = "jinja2" }, + { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "markupsafe" }, + { name = "packaging" }, + { name = "pathspec" }, + { name = "platformdirs", version = "4.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "platformdirs", version = "4.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyyaml" }, + { name = "pyyaml-env-tag" }, + { name = "watchdog" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/00/20/bb50f9de3a6de69e6abd6b087b52fa2418a0418b19597601605f855ad044/pydata_sphinx_theme-0.16.1.tar.gz", hash = "sha256:a08b7f0b7f70387219dc659bff0893a7554d5eb39b59d3b8ef37b8401b7642d7", size = 2412693, upload-time = "2024-12-17T10:53:39.537Z" } +sdist = { url = "https://files.pythonhosted.org/packages/ec/29/f27a4e1eddf72ed3db6e47818fbafe6debbf09fd7051f9c1a007239b46ef/properdocs-1.6.7.tar.gz", hash = "sha256:adc7b16e562890af0e098a7e5b02e3a81c20894a87d6a28d345c9300de73c26e", size = 276141, upload-time = "2026-03-20T20:07:48.167Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/e2/0d/8ba33fa83a7dcde13eb3c1c2a0c1cc29950a048bfed6d9b0d8b6bd710b4c/pydata_sphinx_theme-0.16.1-py3-none-any.whl", hash = "sha256:225331e8ac4b32682c18fcac5a57a6f717c4e632cea5dd0e247b55155faeccde", size = 6723264, upload-time = "2024-12-17T10:53:35.645Z" }, + { url = "https://files.pythonhosted.org/packages/bd/4d/fc923f5c85318ee8cc903566dc4e0ebe41b2dfc1d2ecf5546db232397ed6/properdocs-1.6.7-py3-none-any.whl", hash = "sha256:6fa0cfa2e01bf338f684892c8a506cf70ea88ae7f3479c933b6fa20168101cbd", size = 225406, upload-time = "2026-03-20T20:07:46.875Z" }, ] [[package]] @@ -1185,6 +1482,20 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, ] +[[package]] +name = "pymdown-extensions" +version = "10.21.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown", version = "3.9", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "markdown", version = "3.10.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/df/08/f1c908c581fd11913da4711ea7ba32c0eee40b0190000996bb863b0c9349/pymdown_extensions-10.21.2.tar.gz", hash = "sha256:c3f55a5b8a1d0edf6699e35dcbea71d978d34ff3fa79f3d807b8a5b3fa90fbdc", size = 853922, upload-time = "2026-03-29T15:01:55.233Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/27/a2fc51a4a122dfd1015e921ae9d22fee3d20b0b8080d9a704578bf9deece/pymdown_extensions-10.21.2-py3-none-any.whl", hash = "sha256:5c0fd2a2bea14eb39af8ff284f1066d898ab2187d81b889b75d46d4348c01638", size = 268901, upload-time = "2026-03-29T15:01:53.244Z" }, +] + [[package]] name = "pytest" version = "8.4.2" @@ -1230,6 +1541,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/a7/4b/8b78d126e275efa2379b1c2e09dc52cf70df16fc3b90613ef82531499d73/pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a", size = 21949, upload-time = "2023-05-24T18:44:54.079Z" }, ] +[[package]] +name = "python-dateutil" +version = "2.9.0.post0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/66/c0/0c8b6ad9f17a802ee498c46e004a0eb49bc148f2fd230864601a86dcf6db/python-dateutil-2.9.0.post0.tar.gz", hash = "sha256:37dd54208da7e1cd875388217d5e00ebd4179249f90fb72437e91a35459a0ad3", size = 342432, upload-time = "2024-03-01T18:36:20.211Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ec/57/56b9bcc3c9c6a792fcbaf139543cee77261f3651ca9da0c93f5c1221264b/python_dateutil-2.9.0.post0-py2.py3-none-any.whl", hash = "sha256:a8b2bc7bffae282281c8140a97d3aa9c14da0b136dfe83f850eea9a5f7470427", size = 229892, upload-time = "2024-03-01T18:36:18.57Z" }, +] + [[package]] name = "python-dotenv" version = "1.2.1" @@ -1350,6 +1673,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/f0/0c/25113e0b5e103d7f1490c0e947e303fe4a696c10b501dea7a9f49d4e876c/pyyaml-6.0.3-cp39-cp39-win_amd64.whl", hash = "sha256:2e71d11abed7344e42a8849600193d15b6def118602c4c176f748e4583246007", size = 158777, upload-time = "2025-09-25T21:33:15.55Z" }, ] +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/2e/79c822141bfd05a853236b504869ebc6b70159afc570e1d5a20641782eaa/pyyaml_env_tag-1.1.tar.gz", hash = "sha256:2eb38b75a2d21ee0475d6d97ec19c63287a7e140231e4214969d0eac923cd7ff", size = 5737, upload-time = "2025-05-13T15:24:01.64Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/04/11/432f32f8097b03e3cd5fe57e88efb685d964e2e5178a48ed61e841f7fdce/pyyaml_env_tag-1.1-py3-none-any.whl", hash = "sha256:17109e1a528561e32f026364712fee1264bc2ea6715120891174ed1b980d2e04", size = 4722, upload-time = "2025-05-13T15:23:59.629Z" }, +] + [[package]] name = "redis" version = "7.0.1" @@ -1398,33 +1733,21 @@ wheels = [ ] [[package]] -name = "roman-numerals" -version = "4.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ae/f9/41dc953bbeb056c17d5f7a519f50fdf010bd0553be2d630bc69d1e022703/roman_numerals-4.1.0.tar.gz", hash = "sha256:1af8b147eb1405d5839e78aeb93131690495fe9da5c91856cb33ad55a7f1e5b2", size = 9077, upload-time = "2025-12-17T18:25:34.381Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/04/54/6f679c435d28e0a568d8e8a7c0a93a09010818634c3c3907fc98d8983770/roman_numerals-4.1.0-py3-none-any.whl", hash = "sha256:647ba99caddc2cc1e55a51e4360689115551bf4476d90e8162cf8c345fe233c7", size = 7676, upload-time = "2025-12-17T18:25:33.098Z" }, -] - -[[package]] -name = "roman-numerals-py" -version = "4.1.0" +name = "six" +version = "1.17.0" source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "roman-numerals", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/cb/b5/de96fca640f4f656eb79bbee0e79aeec52e3e0e359f8a3e6a0d366378b64/roman_numerals_py-4.1.0.tar.gz", hash = "sha256:f5d7b2b4ca52dd855ef7ab8eb3590f428c0b1ea480736ce32b01fef2a5f8daf9", size = 4274, upload-time = "2025-12-17T18:25:41.153Z" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/27/2c/daca29684cbe9fd4bc711f8246da3c10adca1ccc4d24436b17572eb2590e/roman_numerals_py-4.1.0-py3-none-any.whl", hash = "sha256:553114c1167141c1283a51743759723ecd05604a1b6b507225e91dc1a6df0780", size = 4547, upload-time = "2025-12-17T18:25:40.136Z" }, + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, ] [[package]] -name = "snowballstemmer" -version = "3.0.1" +name = "smmap" +version = "5.0.3" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/75/a7/9810d872919697c9d01295633f5d574fb416d47e535f258272ca1f01f447/snowballstemmer-3.0.1.tar.gz", hash = "sha256:6d5eeeec8e9f84d4d56b847692bacf79bc2c8e90c7f80ca4444ff8b6f2e52895", size = 105575, upload-time = "2025-05-09T16:34:51.843Z" } +sdist = { url = "https://files.pythonhosted.org/packages/1f/ea/49c993d6dfdd7338c9b1000a0f36817ed7ec84577ae2e52f890d1a4ff909/smmap-5.0.3.tar.gz", hash = "sha256:4d9debb8b99007ae47165abc08670bd74cb74b5227dda7f643eccc4e9eb5642c", size = 22506, upload-time = "2026-03-09T03:43:26.1Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/c8/78/3565d011c61f5a43488987ee32b6f3f656e7f107ac2782dd57bdd7d91d9a/snowballstemmer-3.0.1-py3-none-any.whl", hash = "sha256:6cd7b3897da8d6c9ffb968a6781fa6532dce9c3618a4b127d920dab764a19064", size = 103274, upload-time = "2025-05-09T16:34:50.371Z" }, + { url = "https://files.pythonhosted.org/packages/c1/d4/59e74daffcb57a07668852eeeb6035af9f32cbfd7a1d2511f17d2fe6a738/smmap-5.0.3-py3-none-any.whl", hash = "sha256:c106e05d5a61449cf6ba9a1e650227ecfb141590d2a98412103ff35d89fc7b2f", size = 24390, upload-time = "2026-03-09T03:43:24.361Z" }, ] [[package]] @@ -1436,237 +1759,6 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/46/2c/1462b1d0a634697ae9e55b3cecdcb64788e8b7d63f54d923fcd0bb140aed/soupsieve-2.8.3-py3-none-any.whl", hash = "sha256:ed64f2ba4eebeab06cc4962affce381647455978ffc1e36bb79a545b91f45a95", size = 37016, upload-time = "2026-01-20T04:27:01.012Z" }, ] -[[package]] -name = "sphinx" -version = "7.4.7" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.9.2' and python_full_version < '3.10'", - "python_full_version < '3.9.2'", -] -dependencies = [ - { name = "alabaster", version = "0.7.16", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "babel", marker = "python_full_version < '3.10'" }, - { name = "colorama", marker = "python_full_version < '3.10' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version < '3.10'" }, - { name = "imagesize", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "importlib-metadata", marker = "python_full_version < '3.10'" }, - { name = "jinja2", marker = "python_full_version < '3.10'" }, - { name = "packaging", marker = "python_full_version < '3.10'" }, - { name = "pygments", marker = "python_full_version < '3.10'" }, - { name = "requests", marker = "python_full_version < '3.10'" }, - { name = "snowballstemmer", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version < '3.10'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version < '3.10'" }, - { name = "tomli", marker = "python_full_version < '3.10'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/5b/be/50e50cb4f2eff47df05673d361095cafd95521d2a22521b920c67a372dcb/sphinx-7.4.7.tar.gz", hash = "sha256:242f92a7ea7e6c5b406fdc2615413890ba9f699114a9c09192d7dfead2ee9cfe", size = 8067911, upload-time = "2024-07-20T14:46:56.059Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0d/ef/153f6803c5d5f8917dbb7f7fcf6d34a871ede3296fa89c2c703f5f8a6c8e/sphinx-7.4.7-py3-none-any.whl", hash = "sha256:c2419e2135d11f1951cd994d6eb18a1835bd8fdd8429f9ca375dc1f3281bd239", size = 3401624, upload-time = "2024-07-20T14:46:52.142Z" }, -] - -[[package]] -name = "sphinx" -version = "8.1.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.10.*'", -] -dependencies = [ - { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "babel", marker = "python_full_version == '3.10.*'" }, - { name = "colorama", marker = "python_full_version == '3.10.*' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version == '3.10.*'" }, - { name = "imagesize", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "jinja2", marker = "python_full_version == '3.10.*'" }, - { name = "packaging", marker = "python_full_version == '3.10.*'" }, - { name = "pygments", marker = "python_full_version == '3.10.*'" }, - { name = "requests", marker = "python_full_version == '3.10.*'" }, - { name = "snowballstemmer", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version == '3.10.*'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version == '3.10.*'" }, - { name = "tomli", marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/be0b61178fe2cdcb67e2a92fc9ebb488e3c51c4f74a36a7824c0adf23425/sphinx-8.1.3.tar.gz", hash = "sha256:43c1911eecb0d3e161ad78611bc905d1ad0e523e4ddc202a58a821773dc4c927", size = 8184611, upload-time = "2024-10-13T20:27:13.93Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/26/60/1ddff83a56d33aaf6f10ec8ce84b4c007d9368b21008876fceda7e7381ef/sphinx-8.1.3-py3-none-any.whl", hash = "sha256:09719015511837b76bf6e03e42eb7595ac8c2e41eeb9c29c5b755c6b677992a2", size = 3487125, upload-time = "2024-10-13T20:27:10.448Z" }, -] - -[[package]] -name = "sphinx" -version = "8.2.3" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -dependencies = [ - { name = "alabaster", version = "1.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "babel", marker = "python_full_version >= '3.11'" }, - { name = "colorama", marker = "python_full_version >= '3.11' and sys_platform == 'win32'" }, - { name = "docutils", marker = "python_full_version >= '3.11'" }, - { name = "imagesize", version = "2.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "jinja2", marker = "python_full_version >= '3.11'" }, - { name = "packaging", marker = "python_full_version >= '3.11'" }, - { name = "pygments", marker = "python_full_version >= '3.11'" }, - { name = "requests", marker = "python_full_version >= '3.11'" }, - { name = "roman-numerals-py", marker = "python_full_version >= '3.11'" }, - { name = "snowballstemmer", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-applehelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-devhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-htmlhelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-jsmath", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-qthelp", marker = "python_full_version >= '3.11'" }, - { name = "sphinxcontrib-serializinghtml", marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/38/ad/4360e50ed56cb483667b8e6dadf2d3fda62359593faabbe749a27c4eaca6/sphinx-8.2.3.tar.gz", hash = "sha256:398ad29dee7f63a75888314e9424d40f52ce5a6a87ae88e7071e80af296ec348", size = 8321876, upload-time = "2025-03-02T22:31:59.658Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/31/53/136e9eca6e0b9dc0e1962e2c908fbea2e5ac000c2a2fbd9a35797958c48b/sphinx-8.2.3-py3-none-any.whl", hash = "sha256:4405915165f13521d875a8c29c8970800a0141c14cc5416a38feca4ea5d9b9c3", size = 3589741, upload-time = "2025-03-02T22:31:56.836Z" }, -] - -[[package]] -name = "sphinx-book-theme" -version = "1.1.4" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.10.*'", - "python_full_version >= '3.9.2' and python_full_version < '3.10'", - "python_full_version < '3.9.2'", -] -dependencies = [ - { name = "pydata-sphinx-theme", version = "0.15.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/45/19/d002ed96bdc7738c15847c730e1e88282d738263deac705d5713b4d8fa94/sphinx_book_theme-1.1.4.tar.gz", hash = "sha256:73efe28af871d0a89bd05856d300e61edce0d5b2fbb7984e84454be0fedfe9ed", size = 439188, upload-time = "2025-02-20T16:32:32.581Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/51/9e/c41d68be04eef5b6202b468e0f90faf0c469f3a03353f2a218fd78279710/sphinx_book_theme-1.1.4-py3-none-any.whl", hash = "sha256:843b3f5c8684640f4a2d01abd298beb66452d1b2394cd9ef5be5ebd5640ea0e1", size = 433952, upload-time = "2025-02-20T16:32:31.009Z" }, -] - -[[package]] -name = "sphinx-book-theme" -version = "1.2.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -dependencies = [ - { name = "pydata-sphinx-theme", version = "0.16.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/eb/f7/154786f3cfb7692cd7acc24b6dfe4dcd1146b66f376b17df9e47125555e9/sphinx_book_theme-1.2.0.tar.gz", hash = "sha256:4a7ebfc7da4395309ac942ddfc38fbec5c5254c3be22195e99ad12586fbda9e3", size = 443962, upload-time = "2026-03-09T23:20:30.442Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/02/bf/6f506a37c7f8ecc4576caf9486e303c7af249f6d70447bb51dde9d78cb99/sphinx_book_theme-1.2.0-py3-none-any.whl", hash = "sha256:709605d308e1991c5ef0cf19c481dbe9084b62852e317fafab74382a0ee7ccfa", size = 455936, upload-time = "2026-03-09T23:20:28.788Z" }, -] - -[[package]] -name = "sphinx-copybutton" -version = "0.5.2" -source = { registry = "https://pypi.org/simple" } -dependencies = [ - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/fc/2b/a964715e7f5295f77509e59309959f4125122d648f86b4fe7d70ca1d882c/sphinx-copybutton-0.5.2.tar.gz", hash = "sha256:4cf17c82fb9646d1bc9ca92ac280813a3b605d8c421225fd9913154103ee1fbd", size = 23039, upload-time = "2023-04-14T08:10:22.998Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/9e/48/1ea60e74949eecb12cdd6ac43987f9fd331156388dcc2319b45e2ebb81bf/sphinx_copybutton-0.5.2-py3-none-any.whl", hash = "sha256:fb543fd386d917746c9a2c50360c7905b605726b9355cd26e9974857afeae06e", size = 13343, upload-time = "2023-04-14T08:10:20.844Z" }, -] - -[[package]] -name = "sphinx-design" -version = "0.6.1" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version == '3.10.*'", - "python_full_version >= '3.9.2' and python_full_version < '3.10'", - "python_full_version < '3.9.2'", -] -dependencies = [ - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/2b/69/b34e0cb5336f09c6866d53b4a19d76c227cdec1bbc7ac4de63ca7d58c9c7/sphinx_design-0.6.1.tar.gz", hash = "sha256:b44eea3719386d04d765c1a8257caca2b3e6f8421d7b3a5e742c0fd45f84e632", size = 2193689, upload-time = "2024-08-02T13:48:44.277Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c6/43/65c0acbd8cc6f50195a3a1fc195c404988b15c67090e73c7a41a9f57d6bd/sphinx_design-0.6.1-py3-none-any.whl", hash = "sha256:b11f37db1a802a183d61b159d9a202314d4d2fe29c163437001324fe2f19549c", size = 2215338, upload-time = "2024-08-02T13:48:42.106Z" }, -] - -[[package]] -name = "sphinx-design" -version = "0.7.0" -source = { registry = "https://pypi.org/simple" } -resolution-markers = [ - "python_full_version >= '3.11'", -] -dependencies = [ - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, -] -sdist = { url = "https://files.pythonhosted.org/packages/13/7b/804f311da4663a4aecc6cf7abd83443f3d4ded970826d0c958edc77d4527/sphinx_design-0.7.0.tar.gz", hash = "sha256:d2a3f5b19c24b916adb52f97c5f00efab4009ca337812001109084a740ec9b7a", size = 2203582, upload-time = "2026-01-19T13:12:53.297Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/30/cf/45dd359f6ca0c3762ce0490f681da242f0530c49c81050c035c016bfdd3a/sphinx_design-0.7.0-py3-none-any.whl", hash = "sha256:f82bf179951d58f55dca78ab3706aeafa496b741a91b1911d371441127d64282", size = 2220350, upload-time = "2026-01-19T13:12:51.077Z" }, -] - -[[package]] -name = "sphinxcontrib-applehelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/ba/6e/b837e84a1a704953c62ef8776d45c3e8d759876b4a84fe14eba2859106fe/sphinxcontrib_applehelp-2.0.0.tar.gz", hash = "sha256:2f29ef331735ce958efa4734873f084941970894c6090408b079c61b2e1c06d1", size = 20053, upload-time = "2024-07-29T01:09:00.465Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/5d/85/9ebeae2f76e9e77b952f4b274c27238156eae7979c5421fba91a28f4970d/sphinxcontrib_applehelp-2.0.0-py3-none-any.whl", hash = "sha256:4cd3f0ec4ac5dd9c17ec65e9ab272c9b867ea77425228e68ecf08d6b28ddbdb5", size = 119300, upload-time = "2024-07-29T01:08:58.99Z" }, -] - -[[package]] -name = "sphinxcontrib-devhelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/f6/d2/5beee64d3e4e747f316bae86b55943f51e82bb86ecd325883ef65741e7da/sphinxcontrib_devhelp-2.0.0.tar.gz", hash = "sha256:411f5d96d445d1d73bb5d52133377b4248ec79db5c793ce7dbe59e074b4dd1ad", size = 12967, upload-time = "2024-07-29T01:09:23.417Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/35/7a/987e583882f985fe4d7323774889ec58049171828b58c2217e7f79cdf44e/sphinxcontrib_devhelp-2.0.0-py3-none-any.whl", hash = "sha256:aefb8b83854e4b0998877524d1029fd3e6879210422ee3780459e28a1f03a8a2", size = 82530, upload-time = "2024-07-29T01:09:21.945Z" }, -] - -[[package]] -name = "sphinxcontrib-htmlhelp" -version = "2.1.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/43/93/983afd9aa001e5201eab16b5a444ed5b9b0a7a010541e0ddfbbfd0b2470c/sphinxcontrib_htmlhelp-2.1.0.tar.gz", hash = "sha256:c9e2916ace8aad64cc13a0d233ee22317f2b9025b9cf3295249fa985cc7082e9", size = 22617, upload-time = "2024-07-29T01:09:37.889Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/0a/7b/18a8c0bcec9182c05a0b3ec2a776bba4ead82750a55ff798e8d406dae604/sphinxcontrib_htmlhelp-2.1.0-py3-none-any.whl", hash = "sha256:166759820b47002d22914d64a075ce08f4c46818e17cfc9470a9786b759b19f8", size = 98705, upload-time = "2024-07-29T01:09:36.407Z" }, -] - -[[package]] -name = "sphinxcontrib-jsmath" -version = "1.0.1" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/b2/e8/9ed3830aeed71f17c026a07a5097edcf44b692850ef215b161b8ad875729/sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8", size = 5787, upload-time = "2019-01-21T16:10:16.347Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/c2/42/4c8646762ee83602e3fb3fbe774c2fac12f317deb0b5dbeeedd2d3ba4b77/sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178", size = 5071, upload-time = "2019-01-21T16:10:14.333Z" }, -] - -[[package]] -name = "sphinxcontrib-qthelp" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/68/bc/9104308fc285eb3e0b31b67688235db556cd5b0ef31d96f30e45f2e51cae/sphinxcontrib_qthelp-2.0.0.tar.gz", hash = "sha256:4fe7d0ac8fc171045be623aba3e2a8f613f8682731f9153bb2e40ece16b9bbab", size = 17165, upload-time = "2024-07-29T01:09:56.435Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/27/83/859ecdd180cacc13b1f7e857abf8582a64552ea7a061057a6c716e790fce/sphinxcontrib_qthelp-2.0.0-py3-none-any.whl", hash = "sha256:b18a828cdba941ccd6ee8445dbe72ffa3ef8cbe7505d8cd1fa0d42d3f2d5f3eb", size = 88743, upload-time = "2024-07-29T01:09:54.885Z" }, -] - -[[package]] -name = "sphinxcontrib-serializinghtml" -version = "2.0.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3b/44/6716b257b0aa6bfd51a1b31665d1c205fb12cb5ad56de752dfa15657de2f/sphinxcontrib_serializinghtml-2.0.0.tar.gz", hash = "sha256:e9d912827f872c029017a53f0ef2180b327c3f7fd23c87229f7a8e8b70031d4d", size = 16080, upload-time = "2024-07-29T01:10:09.332Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/52/a7/d2782e4e3f77c8450f727ba74a8f12756d5ba823d81b941f1b04da9d033a/sphinxcontrib_serializinghtml-2.0.0-py3-none-any.whl", hash = "sha256:6e2cb0eef194e10c27ec0023bfeb25badbbb5868244cf5bc5bdc04e4464bf331", size = 92072, upload-time = "2024-07-29T01:10:08.203Z" }, -] - [[package]] name = "sql-redis" version = "0.5.0" @@ -1693,17 +1785,19 @@ dev = [ { name = "testcontainers", version = "4.13.3", source = { registry = "https://pypi.org/simple" }, extra = ["redis"], marker = "python_full_version >= '3.9.2'" }, ] docs = [ - { name = "myst-parser", version = "3.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "myst-parser", version = "4.0.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "myst-parser", version = "5.0.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx", version = "7.4.7", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, - { name = "sphinx", version = "8.1.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version == '3.10.*'" }, - { name = "sphinx", version = "8.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-book-theme", version = "1.1.4", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-book-theme", version = "1.2.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, - { name = "sphinx-copybutton" }, - { name = "sphinx-design", version = "0.6.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.11'" }, - { name = "sphinx-design", version = "0.7.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.11'" }, + { name = "mkdocs" }, + { name = "mkdocs-autorefs" }, + { name = "mkdocs-git-revision-date-localized-plugin", version = "1.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "mkdocs-git-revision-date-localized-plugin", version = "1.5.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mkdocs-llmstxt", version = "0.4.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "mkdocs-llmstxt", version = "0.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mkdocs-material" }, + { name = "mkdocs-redirects", version = "1.2.2", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "mkdocs-redirects", version = "1.2.3", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mkdocs-section-index", version = "0.3.11", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.10'" }, + { name = "mkdocs-section-index", version = "0.3.12", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.10'" }, + { name = "mkdocstrings", version = "0.30.1", source = { registry = "https://pypi.org/simple" }, extra = ["python"], marker = "python_full_version < '3.10'" }, + { name = "mkdocstrings", version = "1.0.4", source = { registry = "https://pypi.org/simple" }, extra = ["python"], marker = "python_full_version >= '3.10'" }, ] [package.metadata] @@ -1725,11 +1819,14 @@ dev = [ { name = "testcontainers", extras = ["redis"], specifier = ">=4.0.0,<5" }, ] docs = [ - { name = "myst-parser", specifier = ">=3.0" }, - { name = "sphinx", specifier = ">=7.3,<9" }, - { name = "sphinx-book-theme", specifier = ">=1.1" }, - { name = "sphinx-copybutton", specifier = ">=0.5" }, - { name = "sphinx-design", specifier = ">=0.6" }, + { name = "mkdocs", specifier = ">=1.6" }, + { name = "mkdocs-autorefs", specifier = ">=1.2" }, + { name = "mkdocs-git-revision-date-localized-plugin", specifier = ">=1.2" }, + { name = "mkdocs-llmstxt", specifier = ">=0.2" }, + { name = "mkdocs-material", specifier = ">=9.5" }, + { name = "mkdocs-redirects", specifier = ">=1.2" }, + { name = "mkdocs-section-index", specifier = ">=0.3" }, + { name = "mkdocstrings", extras = ["python"], specifier = ">=0.26" }, ] [[package]] @@ -1834,6 +1931,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, ] +[[package]] +name = "tzdata" +version = "2026.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ba/19/1b9b0e29f30c6d35cb345486df41110984ea67ae69dddbc0e8a100999493/tzdata-2026.2.tar.gz", hash = "sha256:9173fde7d80d9018e02a662e168e5a2d04f87c41ea174b139fbef642eda62d10", size = 198254, upload-time = "2026-04-24T15:22:08.651Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ce/e4/dccd7f47c4b64213ac01ef921a1337ee6e30e8c6466046018326977efd95/tzdata-2026.2-py2.py3-none-any.whl", hash = "sha256:bbe9af844f658da81a5f95019480da3a89415801f6cc966806612cc7169bffe7", size = 349321, upload-time = "2026-04-24T15:22:05.876Z" }, +] + [[package]] name = "urllib3" version = "2.6.2" @@ -1860,6 +1966,52 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/6a/2a/dc2228b2888f51192c7dc766106cd475f1b768c10caaf9727659726f7391/virtualenv-20.36.1-py3-none-any.whl", hash = "sha256:575a8d6b124ef88f6f51d56d656132389f961062a9177016a50e4f507bbcc19f", size = 6008258, upload-time = "2026-01-09T18:20:59.425Z" }, ] +[[package]] +name = "watchdog" +version = "6.0.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/db/7d/7f3d619e951c88ed75c6037b246ddcf2d322812ee8ea189be89511721d54/watchdog-6.0.0.tar.gz", hash = "sha256:9ddf7c82fda3ae8e24decda1338ede66e1c99883db93711d8fb941eaa2d8c282", size = 131220, upload-time = "2024-11-01T14:07:13.037Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/56/90994d789c61df619bfc5ce2ecdabd5eeff564e1eb47512bd01b5e019569/watchdog-6.0.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:d1cdb490583ebd691c012b3d6dae011000fe42edb7a82ece80965b42abd61f26", size = 96390, upload-time = "2024-11-01T14:06:24.793Z" }, + { url = "https://files.pythonhosted.org/packages/55/46/9a67ee697342ddf3c6daa97e3a587a56d6c4052f881ed926a849fcf7371c/watchdog-6.0.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bc64ab3bdb6a04d69d4023b29422170b74681784ffb9463ed4870cf2f3e66112", size = 88389, upload-time = "2024-11-01T14:06:27.112Z" }, + { url = "https://files.pythonhosted.org/packages/44/65/91b0985747c52064d8701e1075eb96f8c40a79df889e59a399453adfb882/watchdog-6.0.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:c897ac1b55c5a1461e16dae288d22bb2e412ba9807df8397a635d88f671d36c3", size = 89020, upload-time = "2024-11-01T14:06:29.876Z" }, + { url = "https://files.pythonhosted.org/packages/e0/24/d9be5cd6642a6aa68352ded4b4b10fb0d7889cb7f45814fb92cecd35f101/watchdog-6.0.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:6eb11feb5a0d452ee41f824e271ca311a09e250441c262ca2fd7ebcf2461a06c", size = 96393, upload-time = "2024-11-01T14:06:31.756Z" }, + { url = "https://files.pythonhosted.org/packages/63/7a/6013b0d8dbc56adca7fdd4f0beed381c59f6752341b12fa0886fa7afc78b/watchdog-6.0.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:ef810fbf7b781a5a593894e4f439773830bdecb885e6880d957d5b9382a960d2", size = 88392, upload-time = "2024-11-01T14:06:32.99Z" }, + { url = "https://files.pythonhosted.org/packages/d1/40/b75381494851556de56281e053700e46bff5b37bf4c7267e858640af5a7f/watchdog-6.0.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:afd0fe1b2270917c5e23c2a65ce50c2a4abb63daafb0d419fde368e272a76b7c", size = 89019, upload-time = "2024-11-01T14:06:34.963Z" }, + { url = "https://files.pythonhosted.org/packages/39/ea/3930d07dafc9e286ed356a679aa02d777c06e9bfd1164fa7c19c288a5483/watchdog-6.0.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:bdd4e6f14b8b18c334febb9c4425a878a2ac20efd1e0b231978e7b150f92a948", size = 96471, upload-time = "2024-11-01T14:06:37.745Z" }, + { url = "https://files.pythonhosted.org/packages/12/87/48361531f70b1f87928b045df868a9fd4e253d9ae087fa4cf3f7113be363/watchdog-6.0.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:c7c15dda13c4eb00d6fb6fc508b3c0ed88b9d5d374056b239c4ad1611125c860", size = 88449, upload-time = "2024-11-01T14:06:39.748Z" }, + { url = "https://files.pythonhosted.org/packages/5b/7e/8f322f5e600812e6f9a31b75d242631068ca8f4ef0582dd3ae6e72daecc8/watchdog-6.0.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f10cb2d5902447c7d0da897e2c6768bca89174d0c6e1e30abec5421af97a5b0", size = 89054, upload-time = "2024-11-01T14:06:41.009Z" }, + { url = "https://files.pythonhosted.org/packages/68/98/b0345cabdce2041a01293ba483333582891a3bd5769b08eceb0d406056ef/watchdog-6.0.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:490ab2ef84f11129844c23fb14ecf30ef3d8a6abafd3754a6f75ca1e6654136c", size = 96480, upload-time = "2024-11-01T14:06:42.952Z" }, + { url = "https://files.pythonhosted.org/packages/85/83/cdf13902c626b28eedef7ec4f10745c52aad8a8fe7eb04ed7b1f111ca20e/watchdog-6.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:76aae96b00ae814b181bb25b1b98076d5fc84e8a53cd8885a318b42b6d3a5134", size = 88451, upload-time = "2024-11-01T14:06:45.084Z" }, + { url = "https://files.pythonhosted.org/packages/fe/c4/225c87bae08c8b9ec99030cd48ae9c4eca050a59bf5c2255853e18c87b50/watchdog-6.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:a175f755fc2279e0b7312c0035d52e27211a5bc39719dd529625b1930917345b", size = 89057, upload-time = "2024-11-01T14:06:47.324Z" }, + { url = "https://files.pythonhosted.org/packages/05/52/7223011bb760fce8ddc53416beb65b83a3ea6d7d13738dde75eeb2c89679/watchdog-6.0.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:e6f0e77c9417e7cd62af82529b10563db3423625c5fce018430b249bf977f9e8", size = 96390, upload-time = "2024-11-01T14:06:49.325Z" }, + { url = "https://files.pythonhosted.org/packages/9c/62/d2b21bc4e706d3a9d467561f487c2938cbd881c69f3808c43ac1ec242391/watchdog-6.0.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:90c8e78f3b94014f7aaae121e6b909674df5b46ec24d6bebc45c44c56729af2a", size = 88386, upload-time = "2024-11-01T14:06:50.536Z" }, + { url = "https://files.pythonhosted.org/packages/ea/22/1c90b20eda9f4132e4603a26296108728a8bfe9584b006bd05dd94548853/watchdog-6.0.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e7631a77ffb1f7d2eefa4445ebbee491c720a5661ddf6df3498ebecae5ed375c", size = 89017, upload-time = "2024-11-01T14:06:51.717Z" }, + { url = "https://files.pythonhosted.org/packages/30/ad/d17b5d42e28a8b91f8ed01cb949da092827afb9995d4559fd448d0472763/watchdog-6.0.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:c7ac31a19f4545dd92fc25d200694098f42c9a8e391bc00bdd362c5736dbf881", size = 87902, upload-time = "2024-11-01T14:06:53.119Z" }, + { url = "https://files.pythonhosted.org/packages/5c/ca/c3649991d140ff6ab67bfc85ab42b165ead119c9e12211e08089d763ece5/watchdog-6.0.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:9513f27a1a582d9808cf21a07dae516f0fab1cf2d7683a742c498b93eedabb11", size = 88380, upload-time = "2024-11-01T14:06:55.19Z" }, + { url = "https://files.pythonhosted.org/packages/5b/79/69f2b0e8d3f2afd462029031baafb1b75d11bb62703f0e1022b2e54d49ee/watchdog-6.0.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7a0e56874cfbc4b9b05c60c8a1926fedf56324bb08cfbc188969777940aef3aa", size = 87903, upload-time = "2024-11-01T14:06:57.052Z" }, + { url = "https://files.pythonhosted.org/packages/e2/2b/dc048dd71c2e5f0f7ebc04dd7912981ec45793a03c0dc462438e0591ba5d/watchdog-6.0.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:e6439e374fc012255b4ec786ae3c4bc838cd7309a540e5fe0952d03687d8804e", size = 88381, upload-time = "2024-11-01T14:06:58.193Z" }, + { url = "https://files.pythonhosted.org/packages/a9/c7/ca4bf3e518cb57a686b2feb4f55a1892fd9a3dd13f470fca14e00f80ea36/watchdog-6.0.0-py3-none-manylinux2014_aarch64.whl", hash = "sha256:7607498efa04a3542ae3e05e64da8202e58159aa1fa4acddf7678d34a35d4f13", size = 79079, upload-time = "2024-11-01T14:06:59.472Z" }, + { url = "https://files.pythonhosted.org/packages/5c/51/d46dc9332f9a647593c947b4b88e2381c8dfc0942d15b8edc0310fa4abb1/watchdog-6.0.0-py3-none-manylinux2014_armv7l.whl", hash = "sha256:9041567ee8953024c83343288ccc458fd0a2d811d6a0fd68c4c22609e3490379", size = 79078, upload-time = "2024-11-01T14:07:01.431Z" }, + { url = "https://files.pythonhosted.org/packages/d4/57/04edbf5e169cd318d5f07b4766fee38e825d64b6913ca157ca32d1a42267/watchdog-6.0.0-py3-none-manylinux2014_i686.whl", hash = "sha256:82dc3e3143c7e38ec49d61af98d6558288c415eac98486a5c581726e0737c00e", size = 79076, upload-time = "2024-11-01T14:07:02.568Z" }, + { url = "https://files.pythonhosted.org/packages/ab/cc/da8422b300e13cb187d2203f20b9253e91058aaf7db65b74142013478e66/watchdog-6.0.0-py3-none-manylinux2014_ppc64.whl", hash = "sha256:212ac9b8bf1161dc91bd09c048048a95ca3a4c4f5e5d4a7d1b1a7d5752a7f96f", size = 79077, upload-time = "2024-11-01T14:07:03.893Z" }, + { url = "https://files.pythonhosted.org/packages/2c/3b/b8964e04ae1a025c44ba8e4291f86e97fac443bca31de8bd98d3263d2fcf/watchdog-6.0.0-py3-none-manylinux2014_ppc64le.whl", hash = "sha256:e3df4cbb9a450c6d49318f6d14f4bbc80d763fa587ba46ec86f99f9e6876bb26", size = 79078, upload-time = "2024-11-01T14:07:05.189Z" }, + { url = "https://files.pythonhosted.org/packages/62/ae/a696eb424bedff7407801c257d4b1afda455fe40821a2be430e173660e81/watchdog-6.0.0-py3-none-manylinux2014_s390x.whl", hash = "sha256:2cce7cfc2008eb51feb6aab51251fd79b85d9894e98ba847408f662b3395ca3c", size = 79077, upload-time = "2024-11-01T14:07:06.376Z" }, + { url = "https://files.pythonhosted.org/packages/b5/e8/dbf020b4d98251a9860752a094d09a65e1b436ad181faf929983f697048f/watchdog-6.0.0-py3-none-manylinux2014_x86_64.whl", hash = "sha256:20ffe5b202af80ab4266dcd3e91aae72bf2da48c0d33bdb15c66658e685e94e2", size = 79078, upload-time = "2024-11-01T14:07:07.547Z" }, + { url = "https://files.pythonhosted.org/packages/07/f6/d0e5b343768e8bcb4cda79f0f2f55051bf26177ecd5651f84c07567461cf/watchdog-6.0.0-py3-none-win32.whl", hash = "sha256:07df1fdd701c5d4c8e55ef6cf55b8f0120fe1aef7ef39a1c6fc6bc2e606d517a", size = 79065, upload-time = "2024-11-01T14:07:09.525Z" }, + { url = "https://files.pythonhosted.org/packages/db/d9/c495884c6e548fce18a8f40568ff120bc3a4b7b99813081c8ac0c936fa64/watchdog-6.0.0-py3-none-win_amd64.whl", hash = "sha256:cbafb470cf848d93b5d013e2ecb245d4aa1c8fd0504e863ccefa32445359d680", size = 79070, upload-time = "2024-11-01T14:07:10.686Z" }, + { url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" }, +] + +[[package]] +name = "wcwidth" +version = "0.7.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/2c/ee/afaf0f85a9a18fe47a67f1e4422ed6cf1fe642f0ae0a2f81166231303c52/wcwidth-0.7.0.tar.gz", hash = "sha256:90e3a7ea092341c44b99562e75d09e4d5160fe7a3974c6fb842a101a95e7eed0", size = 182132, upload-time = "2026-05-02T16:04:12.653Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/41/52/e465037f5375f43533d1a80b6923955201596a99142ed524d77b571a1418/wcwidth-0.7.0-py3-none-any.whl", hash = "sha256:5d69154c429a82910e241c738cd0e2976fac8a2dd47a1a805f4afed1c0f136f2", size = 110825, upload-time = "2026-05-02T16:04:11.033Z" }, +] + [[package]] name = "wrapt" version = "2.0.1"