From 6c5b74a050fd40be2a7a09148b8f6615a78c7b68 Mon Sep 17 00:00:00 2001 From: thomasloux Date: Thu, 29 Jan 2026 15:16:57 +0000 Subject: [PATCH] add mkdocs --- .github/workflows/docs.yml | 59 ++++++ .gitignore | 2 + docs/_static/altrove_logo.png | Bin 0 -> 45840 bytes docs/_static/custom_css.css | 178 +++++++++++++++++ docs/_static/fonts/Ingeo-SemiBold.ttf | Bin 0 -> 37676 bytes docs/api-reference/cli.md | 65 ++++++ docs/api-reference/core.md | 38 ++++ docs/api-reference/io.md | 58 ++++++ docs/contributing.md | 166 ++++++++++++++++ docs/examples.md | 247 +++++++++++++++++++++++ docs/features/callable-references.md | 178 +++++++++++++++++ docs/features/cli-overrides.md | 102 ++++++++++ docs/features/dag-references.md | 138 +++++++++++++ docs/features/file-overrides.md | 118 +++++++++++ docs/features/partial-configs.md | 170 ++++++++++++++++ docs/getting-started/basic-concepts.md | 92 +++++++++ docs/getting-started/installation.md | 51 +++++ docs/getting-started/quick-start.md | 74 +++++++ docs/index.md | 42 ++++ mkdocs.yml | 132 +++++++++++++ pyproject.toml | 5 + uv.lock | 261 +++++++++++++++++++++++++ 22 files changed, 2176 insertions(+) create mode 100644 .github/workflows/docs.yml create mode 100755 docs/_static/altrove_logo.png create mode 100644 docs/_static/custom_css.css create mode 100644 docs/_static/fonts/Ingeo-SemiBold.ttf create mode 100644 docs/api-reference/cli.md create mode 100644 docs/api-reference/core.md create mode 100644 docs/api-reference/io.md create mode 100644 docs/contributing.md create mode 100644 docs/examples.md create mode 100644 docs/features/callable-references.md create mode 100644 docs/features/cli-overrides.md create mode 100644 docs/features/dag-references.md create mode 100644 docs/features/file-overrides.md create mode 100644 docs/features/partial-configs.md create mode 100644 docs/getting-started/basic-concepts.md create mode 100644 docs/getting-started/installation.md create mode 100644 docs/getting-started/quick-start.md create mode 100644 docs/index.md create mode 100644 mkdocs.yml diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 0000000..e02377b --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,59 @@ +name: Deploy Documentation + +on: + push: + branches: + - main + pull_request: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Check out repo + uses: actions/checkout@v6 + - name: Setup environment + uses: ./.github/actions/environment + with: + app-id: ${{ secrets.CI_BOT_ID }} + private-key: ${{ secrets.CI_BOT_KEY }} + + - name: Install dependencies + run: | + uv sync --frozen --group docs + + - name: Build documentation + run: | + uv run mkdocs build + + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: site + + deploy: + if: github.event_name == 'push' && github.ref == 'refs/heads/main' + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 + with: + publish_dir: ./site + publish_branch: gh-pages diff --git a/.gitignore b/.gitignore index 505a3b1..f42feb8 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ wheels/ # Virtual environments .venv + +site/ \ No newline at end of file diff --git a/docs/_static/altrove_logo.png b/docs/_static/altrove_logo.png new file mode 100755 index 0000000000000000000000000000000000000000..f76270ca9b1535494d9ad452578f694f44ca71c1 GIT binary patch literal 45840 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 literal 0 HcmV?d00001 diff --git a/docs/_static/custom_css.css b/docs/_static/custom_css.css new file mode 100644 index 0000000..5cdd94c --- /dev/null +++ b/docs/_static/custom_css.css @@ -0,0 +1,178 @@ +/* Fix /page#foo going to the top of the viewport and being hidden by the navbar */ +html { + scroll-padding-top: 50px; +} + +.md-header__button.md-logo img { + height: 1.8rem; /* adjust as needed */ +} + +/* Fit the Twitter handle alongside the GitHub one in the top right. */ + +div.md-header__source { + width: revert; + max-width: revert; +} + +a.md-source { + display: inline-block; +} + +.md-source__repository { + max-width: 100%; +} + +/* Emphasise sections of nav on left hand side */ + +nav.md-nav { + padding-left: 5px; +} + +nav.md-nav--secondary { + border-left: revert !important; +} + +.md-nav__title { + font-size: 0.9rem; +} + +.md-nav__item--section > .md-nav__link { + font-size: 0.9rem; +} + +/* Indent autogenerated documentation */ + +div.doc-contents { + padding-left: 25px; + border-left: 4px solid rgba(230, 230, 230); +} + +/* Increase visibility of splitters "---" */ + +[data-md-color-scheme="default"] .md-typeset hr { + border-bottom-color: rgb(0, 0, 0); + border-bottom-width: 1pt; +} + +[data-md-color-scheme="slate"] .md-typeset hr { + border-bottom-color: rgb(230, 230, 230); +} + +/* More space at the bottom of the page */ + +.md-main__inner { + margin-bottom: 1.5rem; +} + +/* Remove prev/next footer buttons */ + +.md-footer__inner { + display: none; +} + +/* Change font sizes */ + +html { + /* Decrease font size for overall webpage + Down from 137.5% which is the Material default */ + font-size: 110%; +} + +.md-typeset .admonition { + /* Increase font size in admonitions */ + font-size: 100% !important; +} + +.md-typeset details { + /* Increase font size in details */ + font-size: 100% !important; +} + +.md-typeset h1 { + font-size: 1.6rem; +} + +.md-typeset h2 { + font-size: 1.5rem; +} + +.md-typeset h3 { + font-size: 1.3rem; +} + +.md-typeset h4 { + font-size: 1.1rem; +} + +.md-typeset h5 { + font-size: 0.9rem; +} + +.md-typeset h6 { + font-size: 0.8rem; +} + +/* Bugfix: remove the superfluous parts generated when doing: + +??? Blah + + ::: library.something +*/ + +.md-typeset details .mkdocstrings > h4 { + display: none; +} + +.md-typeset details .mkdocstrings > h5 { + display: none; +} + +/* Change default colours for tags */ + +[data-md-color-scheme="default"] { + --md-typeset-a-color: rgb(0, 189, 164) !important; +} +[data-md-color-scheme="slate"] { + --md-typeset-a-color: rgb(0, 189, 164) !important; +} + +/* Highlight functions, classes etc. type signatures. Really helps to make clear where + one item ends and another begins. */ + +[data-md-color-scheme="default"] { + --doc-heading-color: #DDD; + --doc-heading-border-color: #CCC; + --doc-heading-color-alt: #F0F0F0; +} +[data-md-color-scheme="slate"] { + --doc-heading-color: rgb(25,25,33); + --doc-heading-border-color: rgb(25,25,33); + --doc-heading-color-alt: rgb(33,33,44); + --md-code-bg-color: rgb(38,38,50); +} + +h4.doc-heading { + /* NOT var(--md-code-bg-color) as that's not visually distinct from other code blocks.*/ + background-color: var(--doc-heading-color); + border: solid var(--doc-heading-border-color); + border-width: 1.5pt; + border-radius: 2pt; + padding: 0pt 5pt 2pt 5pt; +} +h5.doc-heading, h6.heading { + background-color: var(--doc-heading-color-alt); + border-radius: 2pt; + padding: 0pt 5pt 2pt 5pt; +} + +/* Make errors in notebooks have scrolling */ +.output_error > pre { + overflow: auto; +} + +@font-face { + font-family: 'Ingeo'; + src: url('./fonts/Ingeo-SemiBold.ttf') format('truetype'); + font-weight: 600; + font-style: normal; +} diff --git a/docs/_static/fonts/Ingeo-SemiBold.ttf b/docs/_static/fonts/Ingeo-SemiBold.ttf new file mode 100644 index 0000000000000000000000000000000000000000..9566e0fa262e28b2f18922afa0bf1715f424d6fe GIT binary patch literal 37676 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 I&zQ6S4+nZfy#N3J literal 0 HcmV?d00001 diff --git a/docs/api-reference/cli.md b/docs/api-reference/cli.md new file mode 100644 index 0000000..79f9594 --- /dev/null +++ b/docs/api-reference/cli.md @@ -0,0 +1,65 @@ +# CLI API + +Command-line interface functions. + +## Overview + +The CLI module provides the `parse_cli()` function for easy command-line integration. + +## parse_cli() + +Parse configuration from command-line arguments: + +```python +import fiddledyn as dyn +import fiddle as fdl + +# Parse and build +config = dyn.parse_cli() +obj = fdl.build(config) +``` + +## Usage Examples + +### Basic Usage + +```python +# main.py +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.parse_cli() +obj = fdl.build(config) +``` + +Run with: + +```bash +python main.py -f config.yaml model.lr=0.001 +``` + +### With Custom Processing + +```python +config = dyn.parse_cli() + +# Apply custom transformations +if config.get('debug'): + # Enable debug mode + pass + +obj = fdl.build(config) +``` + +## Command-Line Syntax + +- `-f config.yaml` - Load configuration file +- `key=value` - Override parameter value +- `key=@file.yaml` - Load value from file + +## See Also + +- [CLI Overrides](../features/cli-overrides.md) +- [Quick Start](../getting-started/quick-start.md) + + diff --git a/docs/api-reference/core.md b/docs/api-reference/core.md new file mode 100644 index 0000000..1cddae5 --- /dev/null +++ b/docs/api-reference/core.md @@ -0,0 +1,38 @@ +# Core API + +Core components of FiddleDyn for managing configuration state and references. + +## Overview + +The core module provides the fundamental building blocks for FiddleDyn: + +- **ParserContext** - Manages state during parsing including registry and references +- **Reference** - Represents DAG references with IDs +- **Types** - Core type definitions + +## ParserContext + +The `ParserContext` class tracks state during configuration parsing: + +```python +from fiddledyn.core import ParserContext + +ctx = ParserContext() +ctx.registry["key"] = "value" # Add to registry +``` + +### Methods + +- `registry` - Dictionary for storing placeholder values +- `references` - Dictionary for tracking DAG references + +## Key Classes + +See the source code in `src/fiddledyn/core/` for detailed implementation. + +## See Also + +- [Getting Started](../getting-started/installation.md) +- [Basic Concepts](../getting-started/basic-concepts.md) + + diff --git a/docs/api-reference/io.md b/docs/api-reference/io.md new file mode 100644 index 0000000..75689df --- /dev/null +++ b/docs/api-reference/io.md @@ -0,0 +1,58 @@ +# IO API + +File loading and configuration I/O functions. + +## Overview + +The IO module provides convenient functions for loading and saving configurations: + +- **load_yaml()** - Load YAML configuration files +- **parse_cli()** - Parse command-line arguments for configuration +- **serialize()** - Serialize configurations to different formats + +## Main Functions + +### load_yaml() + +Load a YAML configuration file: + +```python +import fiddledyn as dyn + +config = dyn.load_yaml("config.yaml") +``` + +With a parser context: + +```python +ctx = dyn.ParserContext() +config = dyn.load_yaml("config.yaml", ctx=ctx) +``` + +### parse_cli() + +Parse configuration from command-line arguments: + +```python +config = dyn.parse_cli() +``` + +This automatically handles: +- `-f` flag for config file path +- Override arguments like `key=value` +- File references with `@file.yaml` + +### resolve_placeholders() + +Resolve placeholder references in configuration: + +```python +dyn.resolve_placeholders(config, ctx.registry) +``` + +## See Also + +- [Quick Start](../getting-started/quick-start.md) +- [CLI Overrides](../features/cli-overrides.md) + + diff --git a/docs/contributing.md b/docs/contributing.md new file mode 100644 index 0000000..ea43d9b --- /dev/null +++ b/docs/contributing.md @@ -0,0 +1,166 @@ +# Contributing + +We welcome contributions to FiddleDyn! This guide will help you get started. + +## Setting Up Development Environment + +### Prerequisites + +- Python 3.12+ +- [uv](https://github.com/astral-sh/uv) package manager + +### Install from Source + +```bash +git clone https://github.com/altrove-ai/fiddledyn.git +cd fiddledyn +uv sync +``` + +This installs the project in editable mode with all development dependencies. + +## Running Tests + +Run the test suite: + +```bash +pytest +``` + +Run with coverage: + +```bash +pytest --cov=src/fiddledyn --cov-report=html +``` + +Run specific tests: + +```bash +pytest tests/test_cli.py -v +``` + +## Code Quality + +We use several tools to maintain code quality: + +### Linting with Ruff + +```bash +ruff check src/ tests/ +``` + +Auto-fix issues: + +```bash +ruff check --fix src/ tests/ +``` + +### Type Checking with Pyright + +```bash +pyright +``` + +## Documentation + +Documentation is built with [MkDocs](https://www.mkdocs.org/) and the [Material theme](https://squidfunk.github.io/mkdocs-material/). + +### Build Locally + +```bash +pip install mkdocs mkdocs-material mkdocstrings +mkdocs serve +``` + +Visit `http://localhost:8000` to see the docs. + +### Add New Documentation + +1. Create a new `.md` file in the `docs/` directory +2. Update `mkdocs.yml` to include the new page in the navigation +3. Write your documentation using Markdown +4. Run `mkdocs serve` to preview + +## Development Workflow + +1. Create a feature branch: + ```bash + git checkout -b feature/your-feature-name + ``` + +2. Make your changes and add tests + +3. Run tests and code quality checks: + ```bash + pytest + ruff check --fix src/ tests/ + pyright + ``` + +4. Commit your changes: + ```bash + git add . + git commit -m "Add descriptive commit message" + ``` + +5. Push to your fork and open a pull request + +## Project Structure + +``` +fiddledyn/ +├── src/fiddledyn/ # Main source code +│ ├── core/ # Core classes (ParserContext, Reference, etc.) +│ ├── parsing/ # YAML parsing logic +│ ├── resolution/ # Placeholder resolution +│ ├── serialization/ # Serialization to different backends +│ ├── cli.py # Command-line interface +│ ├── io.py # File I/O functions +│ └── utils.py # Utility functions +├── tests/ # Test suite +├── docs/ # Documentation source +├── pyproject.toml # Project configuration +├── ruff.toml # Ruff configuration +└── README.md # Project README +``` + +## Key Concepts + +Before contributing, familiarize yourself with these concepts: + +- **ParserContext** - Tracks state during parsing (registry, references) +- **Reference** - DAG reference with `_id_` and `_ref_` +- **Placeholder** - Unresolved value marked with `$` +- **Partial** - Configuration marked with `_partial_: true` +- **CLI Overrides** - Modifications via command-line arguments + +See [Basic Concepts](../getting-started/basic-concepts.md) for more details. + +## Reporting Issues + +Found a bug? Please open an issue on GitHub with: + +- Clear description of the problem +- Steps to reproduce +- Expected vs actual behavior +- Python version and environment + +## Asking Questions + +Have a question? + +- Check the [documentation](https://github.com/altrove-ai/fiddledyn) +- Search existing issues +- Open a new discussion on GitHub + +## Code of Conduct + +Please be respectful and constructive in all interactions. We aim to maintain a welcoming and inclusive community. + +## License + +By contributing, you agree that your contributions will be licensed under the project's license. + +## Questions? + +Feel free to open an issue or start a discussion on GitHub. We're happy to help! diff --git a/docs/examples.md b/docs/examples.md new file mode 100644 index 0000000..64d71cd --- /dev/null +++ b/docs/examples.md @@ -0,0 +1,247 @@ +# Examples + +Practical examples demonstrating FiddleDyn features. + +## Basic Configuration + +A simple example with a model and optimizer: + +```yaml +# config.yaml +model: + type: SimpleModel + hidden_dim: 256 + dropout: 0.1 + +optimizer: + type: Adam + lr: 0.001 + betas: [0.9, 0.999] + +batch_size: 32 +num_epochs: 10 +``` + +Load and use: + +```python +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.parse_cli() +obj = fdl.build(config) + +print(f"Learning rate: {obj.optimizer.lr}") +print(f"Model dimension: {obj.model.hidden_dim}") +``` + +Run with overrides: + +```bash +python train.py -f config.yaml model.hidden_dim=512 optimizer.lr=0.01 +``` + +## Multi-File Configuration + +Organize large projects with multiple files. + +**Directory structure:** + +``` +configs/ +├── base.yaml +├── models/ +│ ├── small.yaml +│ └── large.yaml +├── optimizers/ +│ ├── adam.yaml +│ └── sgd.yaml +└── datasets/ + ├── mnist.yaml + └── cifar10.yaml +``` + +**configs/base.yaml:** + +```yaml +model: {} # Will be overridden +optimizer: {} # Will be overridden +dataset: {} # Will be overridden +num_epochs: 100 +``` + +**configs/models/large.yaml:** + +```yaml +type: ResNet +depth: 152 +width_multiplier: 2 +``` + +**configs/optimizers/adam.yaml:** + +```yaml +type: Adam +lr: 0.001 +weight_decay: 0.0001 +``` + +Run with different combinations: + +```bash +# Small model with SGD +python train.py -f configs/base.yaml \ + model=@configs/models/small.yaml \ + optimizer=@configs/optimizers/sgd.yaml + +# Large model with Adam +python train.py -f configs/base.yaml \ + model=@configs/models/large.yaml \ + optimizer=@configs/optimizers/adam.yaml +``` + +## Shared Components with DAG References + +Define a reusable component: + +```yaml +# configs/shared/encoder.yaml +_id_: shared_encoder +type: TransformerEncoder +hidden_dim: 512 +num_layers: 6 +num_heads: 8 +``` + +Use it in multiple places: + +```yaml +# configs/model.yaml +encoder: + _ref_: shared_encoder + +decoder: + _ref_: shared_encoder # Same instance! + +projection: + input_dim: 512 # From encoder +``` + +Load with context: + +```python +import fiddledyn as dyn + +ctx = dyn.ParserContext() +config = dyn.load_yaml("configs/model.yaml", ctx=ctx) +# encoder and decoder now share the same object +``` + +## Partial Configurations for Flexibility + +Define model factories: + +```yaml +# configs/models/factory.yaml +factory: + _partial_: true + type: TransformerModel + hidden_dim: 512 + num_layers: 6 +``` + +Use with different input sizes: + +```python +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.load_yaml("configs/models/factory.yaml") +model_fn = fdl.build(config.factory) + +# Create models with different input dimensions +small_model = model_fn(input_dim=128) +large_model = model_fn(input_dim=1024) +``` + +## Complex Training Configuration + +A complete training example: + +```yaml +# configs/train.yaml +data: + train_split: 0.8 + batch_size: 32 + shuffle: true + num_workers: 4 + +model: + type: TransformerModel + hidden_dim: 512 + num_layers: 6 + dropout: 0.1 + +optimizer: + type: AdamW + lr: 0.001 + weight_decay: 0.01 + +scheduler: + type: CosineAnnealingLR + T_max: 100 + eta_min: 1.0e-6 + +training: + num_epochs: 100 + warmup_epochs: 5 + log_interval: 100 + save_interval: 1000 + +callbacks: + - type: EarlyStopping + monitor: val_loss + patience: 10 + - type: ModelCheckpoint + dirpath: ./checkpoints +``` + +Load and train: + +```python +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.parse_cli() +cfg = fdl.build(config) + +# Access config values +train_loader = create_dataloader(**cfg.data) +model = create_model(**cfg.model) +optimizer = create_optimizer(model.parameters(), **cfg.optimizer) +scheduler = create_scheduler(optimizer, **cfg.scheduler) + +# Train with your framework +for epoch in range(cfg.training.num_epochs): + # Your training loop here + pass +``` + +Run with different configurations: + +```bash +# Default configuration +python train.py -f configs/train.yaml + +# Quick experiment with smaller model +python train.py -f configs/train.yaml model.hidden_dim=256 + +# Production run with larger batch size +python train.py -f configs/train.yaml data.batch_size=128 +``` + +## See Also + +- [Quick Start](getting-started/quick-start.md) +- [Features](features/cli-overrides.md) - Learn more about each feature +- [API Reference](api-reference/core.md) - Detailed API documentation diff --git a/docs/features/callable-references.md b/docs/features/callable-references.md new file mode 100644 index 0000000..7a43c4e --- /dev/null +++ b/docs/features/callable-references.md @@ -0,0 +1,178 @@ +# Callable References + +Pass functions, classes, and other callables as configuration values. + +## Motivation + +Sometimes you need to pass a callable (function, class, method) as a configuration value, rather than the result of calling it. FiddleDyn makes this easy with the `_call_: false` syntax. + +## Basic Usage + +Mark callables with `_call_: false`: + +```yaml +loss_fn: + _call_: false + _target_: torch.nn.CrossEntropyLoss +``` + +When built, this returns the class itself, not an instance: + +```python +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.load_yaml("config.yaml") +obj = fdl.build(config) + +# obj.loss_fn is the CrossEntropyLoss class, not an instance +loss = obj.loss_fn(reduction='mean') # Call it yourself +``` + +## Examples + +### Custom Loss Functions + +```yaml +model: + loss_fn: + _call_: false + _target_: my_module.CustomLoss + metrics: + - _call_: false + _target_: sklearn.metrics.accuracy_score + - _call_: false + _target_: sklearn.metrics.f1_score +``` + +### Activation Functions + +```yaml +layers: + - type: Linear + dim_out: 512 + activation: + _call_: false + _target_: torch.nn.ReLU + + - type: Linear + dim_out: 256 + activation: + _call_: false + _target_: torch.nn.GELU +``` + +### Callbacks and Hooks + +```yaml +training: + callbacks: + - _call_: false + _target_: pytorch_lightning.callbacks.EarlyStopping + monitor: val_loss + patience: 10 + + - _call_: false + _target_: pytorch_lightning.callbacks.ModelCheckpoint + dirpath: ./checkpoints +``` + +## Combined with Partial + +Use `_call_: false` with partial configurations: + +```yaml +optimizer_factory: + _partial_: true + _call_: false + _target_: torch.optim.Adam +``` + +This returns the Adam class itself (not built or instantiated), which you can then use as a partial later. + +## Use Cases + +### 1. Pluggable Components + +Allow users to provide custom implementations: + +```python +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.load_yaml("config.yaml") +obj = fdl.build(config) + +# User-provided loss function +my_loss = obj.loss_fn(weight=[1, 2, 3]) +``` + +### 2. Callbacks and Hooks + +Pass callbacks to frameworks: + +```yaml +trainer: + callbacks: + - _call_: false + _target_: my_callbacks.LoggingCallback + - _call_: false + _target_: my_callbacks.ValidateCallback +``` + +```python +config = dyn.load_yaml("config.yaml") +trainer_config = fdl.build(config.trainer) + +# trainer_config.callbacks contains the callback classes +trainer = Trainer(**trainer_config) +``` + +### 3. Dynamic Function Selection + +Choose behavior based on configuration: + +```yaml +data_processing: + preprocessor: + _call_: false + _target_: my_module.StandardScaler + + augmentation: + _call_: false + _target_: my_module.RandomAugmentation +``` + +```python +config = dyn.load_yaml("config.yaml") +processing = fdl.build(config.data_processing) + +# Get the classes +preprocessor = processing.preprocessor() +augmenter = processing.augmentation(seed=42) +``` + +## Comparison: `_call_: true` vs `_call_: false` + +| Setting | Returns | Use Case | +|---------|---------|----------| +| `_call_: true` (default) | Instance | You want the object immediately | +| `_call_: false` | Callable (class/function) | You want to call it yourself later | + +## Type Hints + +When using callable references, type hints help your IDE: + +```python +from typing import Type, Callable +import torch.nn as nn + +class Config: + loss_fn: Type[nn.Module] # The class itself + activation: Callable[[torch.Tensor], torch.Tensor] # Any callable +``` + +## See Also + +- [Partial Configs](partial-configs.md) +- [Basic Concepts](../getting-started/basic-concepts.md) diff --git a/docs/features/cli-overrides.md b/docs/features/cli-overrides.md new file mode 100644 index 0000000..9f4083c --- /dev/null +++ b/docs/features/cli-overrides.md @@ -0,0 +1,102 @@ +# CLI Overrides + +Modify configuration values from the command line without editing files. + +## Basic Syntax + +Use dot notation to specify nested values: + +```bash +python main.py -f config.yaml key=value +``` + +## Nested Values + +Access nested parameters with dots: + +```bash +python main.py -f config.yaml model.encoder.dim=512 +``` + +This modifies: + +```yaml +model: + encoder: + dim: 512 # Changed from original value +``` + +## Type Inference + +FiddleDyn automatically infers types from the original config: + +```bash +# Original config has model.lr: 0.001 (float) +python main.py -f config.yaml model.lr=0.01 + +# Correctly parsed as float, not string +``` + +## Boolean Values + +Use `true` or `false` for boolean values: + +```bash +python main.py -f config.yaml model.use_dropout=true +``` + +## Lists and Collections + +Specify list indices using bracket notation: + +```bash +python main.py -f config.yaml model.layers.0=128 +``` + +## File References + +Replace entire sections with another configuration file using `@`: + +```bash +python main.py -f config.yaml optimizer=@adam.yaml +``` + +This merges `adam.yaml` at the `optimizer` key. + +## Multiple Overrides + +Chain multiple overrides: + +```bash +python main.py -f config.yaml \ + model.lr=0.01 \ + batch_size=64 \ + optimizer=@adam.yaml +``` + +## Practical Examples + +### Change Learning Rate and Batch Size + +```bash +python train.py -f configs/base.yaml model.lr=0.001 batch_size=128 +``` + +### Use Alternative Optimizer + +```bash +python train.py -f configs/base.yaml optimizer=@configs/optimizers/adam.yaml +``` + +### Experiment with Architecture + +```bash +python train.py -f configs/base.yaml \ + model.encoder.dim=512 \ + model.encoder.layers=8 \ + model.decoder.dim=512 +``` + +## CLI API Reference + +See [CLI Documentation](../api-reference/cli.md) for programmatic usage. diff --git a/docs/features/dag-references.md b/docs/features/dag-references.md new file mode 100644 index 0000000..9dadd3f --- /dev/null +++ b/docs/features/dag-references.md @@ -0,0 +1,138 @@ +# DAG References + +Share instances and configurations across multiple files using DAG references. + +## Motivation + +In complex systems, you often want to reuse the same component in multiple places. DAG references enable this pattern while maintaining clear data flow. + +## Using DAG References + +### Define a Reference + +Use `_id_` to mark a configuration block as reusable: + +```yaml +# encoders.yaml +_id_: transformer_encoder +type: TransformerEncoder +dim: 512 +num_layers: 6 +num_heads: 8 +``` + +### Reference It Later + +Use `_ref_` to point to a previously defined ID: + +```yaml +# model.yaml +encoder: + _ref_: transformer_encoder + +decoder: + _ref_: transformer_encoder # Same instance! +``` + +Both `encoder` and `decoder` will point to the exact same object after building. + +## Loading Multiple Files + +When loading files with references, use a context to track IDs: + +```python +import fiddledyn as dyn + +ctx = dyn.ParserContext() + +# Load files - they build up the registry +config1 = dyn.load_yaml("encoders.yaml", ctx=ctx) +config2 = dyn.load_yaml("model.yaml", ctx=ctx) + +# Resolve references +dyn.resolve_placeholders(config2, ctx.registry) + +import fiddle as fdl +model = fdl.build(config2) +``` + +## Nested IDs + +You can define IDs at any nesting level: + +```yaml +model: + _id_: shared_model + encoder: + dim: 256 + decoder: + dim: 256 +``` + +Reference the entire sub-config: + +```yaml +trainer: + primary_model: + _ref_: shared_model + backup_model: + _ref_: shared_model +``` + +## Practical Example: Multi-GPU Training + +Define a single encoder configuration: + +```yaml +# modules/encoder.yaml +_id_: main_encoder +type: TransformerEncoder +dim: 768 +layers: 12 +``` + +Use it in multiple models: + +```yaml +# configs/model_a.yaml +encoder: + _ref_: main_encoder +decoder: + type: MLPDecoder + input_dim: 768 + +# configs/model_b.yaml +encoder: + _ref_: main_encoder # Same encoder! +projection: + input_dim: 768 +``` + +## Benefits + +- **Consistency** - Changes to the shared config apply everywhere +- **Memory Efficiency** - Shared instances use less memory +- **Clear Dependencies** - Easy to see which components are shared +- **Maintainability** - Single source of truth for shared components + +## Advanced: Partial Sharing + +Combine DAG references with partial configs: + +```yaml +# base_model.yaml +_id_: base_model +_partial_: true +type: Model +encoder: + dim: 256 + +# experiment.yaml +model: + _ref_: base_model + encoder: + dim: 512 # Override for this instance +``` + +!!! note + References are resolved at build time, not at parse time. This allows flexibility in how and when you use the referenced configurations. diff --git a/docs/features/file-overrides.md b/docs/features/file-overrides.md new file mode 100644 index 0000000..d9110f7 --- /dev/null +++ b/docs/features/file-overrides.md @@ -0,0 +1,118 @@ +# File Overrides + +Replace entire configuration sections with alternative files. + +## Basic Usage + +Use the `@filename.yaml` syntax in CLI overrides: + +```bash +python main.py -f config.yaml optimizer=@optimizers/adam.yaml +``` + +This replaces the `optimizer` section with the contents of `optimizers/adam.yaml`. + +## Directory Structure + +Organize your configs logically: + +``` +configs/ +├── base.yaml +├── optimizers/ +│ ├── adam.yaml +│ ├── sgd.yaml +│ └── adamw.yaml +├── models/ +│ ├── small.yaml +│ ├── base.yaml +│ └── large.yaml +└── schedulers/ + ├── constant.yaml + └── cosine.yaml +``` + +## Examples + +### Swap Optimizer + +```bash +# Use Adam optimizer +python train.py -f configs/base.yaml optimizer=@configs/optimizers/adam.yaml + +# Use SGD optimizer +python train.py -f configs/base.yaml optimizer=@configs/optimizers/sgd.yaml +``` + +### Change Model Size + +```bash +# Small model +python train.py -f configs/base.yaml model=@configs/models/small.yaml + +# Large model +python train.py -f configs/base.yaml model=@configs/models/large.yaml +``` + +### Multiple File Overrides + +```bash +python train.py -f configs/base.yaml \ + model=@configs/models/large.yaml \ + optimizer=@configs/optimizers/adamw.yaml \ + scheduler=@configs/schedulers/cosine.yaml +``` + +## File Contents + +Each file should contain valid YAML: + +```yaml +# configs/optimizers/adam.yaml +type: Adam +lr: 0.001 +betas: [0.9, 0.999] +eps: 1.0e-8 +weight_decay: 0.0 + +# configs/optimizers/sgd.yaml +type: SGD +lr: 0.01 +momentum: 0.9 +weight_decay: 0.0001 +``` + +## Combining with Other Overrides + +Mix file overrides with parameter overrides: + +```bash +python train.py -f configs/base.yaml \ + optimizer=@configs/optimizers/adam.yaml \ + optimizer.lr=0.01 # Override the loaded file's lr +``` + +## Relative Paths + +File references use paths relative to the current working directory: + +```bash +# If running from project root +python train.py -f configs/base.yaml optimizer=@configs/optimizers/adam.yaml + +# If running from configs/ directory +cd configs +python ../train.py -f base.yaml optimizer=@optimizers/adam.yaml +``` + +## Tips + +- **Use descriptive names** - `adamw.yaml` is clearer than `opt1.yaml` +- **Organize hierarchically** - Group related configs in subdirectories +- **Document options** - Add comments to config files explaining the settings +- **Version your configs** - Keep important combinations under source control + +## See Also + +- [CLI Overrides](cli-overrides.md) - Learn about parameter overrides +- [Basic Concepts](../getting-started/basic-concepts.md) - Understand configuration basics diff --git a/docs/features/partial-configs.md b/docs/features/partial-configs.md new file mode 100644 index 0000000..85811b8 --- /dev/null +++ b/docs/features/partial-configs.md @@ -0,0 +1,170 @@ +# Partial Configurations + +Defer object instantiation by using partial configurations. + +## What is a Partial Config? + +A **partial configuration** specifies how to create an object, but doesn't actually create it. When built, it returns a callable (partial function) that you can invoke later with additional arguments. + +## Basic Usage + +Mark a configuration as partial with `_partial_: true`: + +```yaml +model: + _partial_: true + type: MyModel + hidden_dim: 256 +``` + +Build it: + +```python +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.load_yaml("config.yaml") +partial_fn = fdl.build(config) + +# partial_fn is now a callable function +# Call it later with additional arguments +model = partial_fn(input_size=10) +``` + +## Real-World Example + +Define a model factory: + +```yaml +# configs/models/transformer.yaml +model: + _partial_: true + _target_: TransformerModel + hidden_dim: 512 + num_layers: 6 + num_heads: 8 + dropout: 0.1 +``` + +Use it to create models with different input sizes: + +```python +import fiddledyn as dyn +import fiddle as fdl + +config = dyn.load_yaml("configs/models/transformer.yaml") +model_factory = fdl.build(config.model) + +# Create model for different input sizes +small_model = model_factory(input_dim=128) +large_model = model_factory(input_dim=1024) +``` + +## Partial with Positional Arguments + +Use `_args_` to specify positional arguments that should be passed when the partial is called: + +```yaml +optimizer: + _partial_: true + _target_: torch.optim.Adam + _args_: + - lr: 0.001 + betas: [0.9, 0.999] +``` + +When called later: + +```python +optimizer = fdl.build(config.optimizer) +# optimizer now expects the parameters argument +actual_optimizer = optimizer(model.parameters()) +``` + +## Nested Partials + +You can nest partial configurations: + +```yaml +training: + model: + _partial_: true + type: MyModel + dim: 256 + + optimizer: + _partial_: true + type: Adam + lr: 0.001 +``` + +When built, both return partials: + +```python +config = dyn.load_yaml("config.yaml") +training = fdl.build(config.training) + +# Both are partials +model = training.model(...) +optimizer = training.optimizer(...) +``` + +## Use Cases + +### 1. Data Loading + +Create data loaders with variable batch sizes: + +```yaml +data: + _partial_: true + type: DataLoader + dataset: $dataset_config + num_workers: 4 + shuffle: true +``` + +```python +data_loader_fn = fdl.build(config.data) +train_loader = data_loader_fn(batch_size=32) +val_loader = data_loader_fn(batch_size=128) +``` + +### 2. Model Architecture + +Create model factories for different input dimensions: + +```yaml +encoder: + _partial_: true + type: TransformerEncoder + hidden_dim: 512 + num_layers: 6 +``` + +### 3. Deferred Initialization + +Initialize components that depend on runtime information: + +```python +# Config specifies the structure +config = dyn.load_yaml("config.yaml") + +# Get partial function +create_model = fdl.build(config.model) + +# Initialize with runtime information +model = create_model(vocab_size=10000) +``` + +## Benefits + +- **Flexibility** - Delay decisions until runtime +- **Reusability** - Use the same config for different scenarios +- **Type Safety** - Maintain type information through the partial +- **Composition** - Easily combine partial configs + +## See Also + +- [Basic Concepts](../getting-started/basic-concepts.md) +- [Quick Start](../getting-started/quick-start.md) diff --git a/docs/getting-started/basic-concepts.md b/docs/getting-started/basic-concepts.md new file mode 100644 index 0000000..5a1281d --- /dev/null +++ b/docs/getting-started/basic-concepts.md @@ -0,0 +1,92 @@ +# Basic Concepts + +Understand the core terminology and concepts in FiddleDyn. + +## Configuration + +A **configuration** is a YAML or Python structure that defines how to instantiate objects. It contains parameter values, references to other configurations, and metadata about partial instantiation. + +## Fiddle + +FiddleDyn is built on top of [Fiddle](https://github.com/google/fiddle), Google's framework for configuration management. Fiddle provides: + +- **Config objects** - Declarative representations of function/class calls +- **Build** - Convert configs into actual objects +- **Serialization** - Convert configs to/from various formats + +## ParserContext + +A context object that tracks state during parsing: + +```python +ctx = dyn.ParserContext() +``` + +It stores: +- **Registry** - Key-value pairs of placeholder values +- **References** - DAG references and IDs for shared instances + +## Placeholders + +Placeholders are values to be filled later. Use the `@` symbol: + +```yaml +layer_size: 256 +model: + hidden_dim: $layer_size # Reference to layer_size +``` + +Resolve with: + +```python +dyn.resolve_placeholders(config, ctx.registry) +``` + +## Partial Configurations + +A **partial configuration** is one that's not fully instantiated. Mark with `_partial_: true`: + +```yaml +model: + _partial_: true + _target_: MyModel + dim: 256 +``` + +Building returns a partial function, not the actual object: + +```python +partial_fn = fdl.build(config) # Returns partial function +obj = partial_fn(x, y) # Call it later with additional args +``` + +## DAG References + +Use global DAG references to share instances across files: + +```yaml +# encoder.yaml +_id_: shared_encoder +encoder: + dim: 256 + layers: 4 + +# model.yaml +encoder: + _ref_: shared_encoder # Reference the shared encoder +decoder: + similar_to_encoder: true +``` + +## Backends + +FiddleDyn supports multiple serialization backends: + +- **FIDDLE** - Native Fiddle format +- **NEMO** - NeMo Run format (requires `nemo_run` package) + +Specify the backend when serializing: + +```python +dyn.serialize(config, backend=dyn.Backend.FIDDLE) +``` diff --git a/docs/getting-started/installation.md b/docs/getting-started/installation.md new file mode 100644 index 0000000..8b62930 --- /dev/null +++ b/docs/getting-started/installation.md @@ -0,0 +1,51 @@ +# Installation + +## Prerequisites + +- Python 3.12 or higher +- pip or uv package manager + +## Core Installation + +To install FiddleDyn with Fiddle backend support: + +```bash +pip install fiddledyn +``` + +## With NeMo Run Support + +To use FiddleDyn with NeMo Run backend: + +```bash +pip install fiddledyn[nemo] +``` + +This installs the optional `nemo_run` dependency, enabling the NEMO backend for configuration serialization. + +## Development Installation + +Clone the repository and install in development mode: + +```bash +git clone https://github.com/altrove-ai/fiddledyn.git +cd fiddledyn +uv sync +``` + +Then run tests to verify the installation: + +```bash +pytest +``` + +## Verifying Installation + +To verify FiddleDyn is installed correctly: + +```python +import fiddledyn as dyn +print(dyn.__version__) +``` + +You should see the version number printed without errors. diff --git a/docs/getting-started/quick-start.md b/docs/getting-started/quick-start.md new file mode 100644 index 0000000..9ba438f --- /dev/null +++ b/docs/getting-started/quick-start.md @@ -0,0 +1,74 @@ +# Quick Start + +Get up and running with FiddleDyn in 5 minutes. + +## 1. Create a Configuration File + +Create a `config.yaml` file: + +```yaml +model: + lr: 0.001 + encoder: + dim: 256 + layers: 4 + +optimizer: adam +batch_size: 32 +``` + +## 2. Load and Use Configuration + +Create `main.py`: + +```python +import fiddledyn as dyn +import fiddle as fdl + +# Parse configuration (from file or CLI) +config = dyn.parse_cli() + +# Build the configured object +trainer = fdl.build(config) +``` + +## 3. Run with CLI Overrides + +```bash +# Use default config +python main.py -f config.yaml + +# Override nested values +python main.py -f config.yaml model.lr=0.01 batch_size=64 + +# Use alternative config file +python main.py -f other_config.yaml +``` + +## 4. Using FiddleDyn Programmatically + +```python +import fiddledyn as dyn + +# Create a context for tracking references +ctx = dyn.ParserContext() + +# Load YAML with context +config = dyn.load_yaml("config.yaml", ctx=ctx) + +# Add registry values for placeholders +ctx.registry["size"] = 256 + +# Resolve any placeholders in the config +dyn.resolve_placeholders(config, ctx.registry) + +# Build the final object +import fiddle as fdl +obj = fdl.build(config) +``` + +## Next Steps + +- Learn about [Basic Concepts](basic-concepts.md) +- Explore [CLI Overrides](../features/cli-overrides.md) +- Check out the [API Reference](../api-reference/core.md) diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..822ac21 --- /dev/null +++ b/docs/index.md @@ -0,0 +1,42 @@ +# Welcome to FiddleDyn + +**Structure-Aware Configuration for Fiddle and NeMo Run** + +FiddleDyn extends [Fiddle](https://github.com/google/fiddle) and [NeMo Run](https://github.com/NVIDIA/nemo_run) with powerful features for configuration management: + +## Key Features + +- **CLI Overrides** - Deep nested values via dot notation +- **DAG References** - Share instances across files with `_id_`/`_ref_` +- **Partial Configs** - Deferred instantiation with `_partial_: true` +- **Callable References** - Pass any callable (class, function, method) +- **File Overrides** - Replace branches with `key=@file.yaml` +- **Round-Trip Safe** - Full serialization preserving all metadata + +## Quick Example + +```python +import fiddledyn as dyn +import fiddle as fdl + +# Load configuration from YAML +config = dyn.parse_cli() + +# Build the configured object +trainer = fdl.build(config) +``` + +```bash +# Use from command line with overrides +python main.py -f config.yaml model.lr=0.001 optimizer=@adam.yaml +``` + +## Getting Started + +- [Installation](getting-started/installation.md) - Set up FiddleDyn +- [Quick Start](getting-started/quick-start.md) - Your first configuration +- [Basic Concepts](getting-started/basic-concepts.md) - Core terminology + +## Documentation Structure + +Browse the documentation using the sidebar to explore features, API reference, and examples. diff --git a/mkdocs.yml b/mkdocs.yml new file mode 100644 index 0000000..423372e --- /dev/null +++ b/mkdocs.yml @@ -0,0 +1,132 @@ +theme: + name: material + features: + - navigation.tabs + - navigation.sections + - toc.integrate + - header.autohide + - content.action.edit + - content.tabs.link + - content.code.copy + - content.code.annotate + - content.tooltips + - navigation.instant + - navigation.indexes + - navigation.top + - search.highlight + - search.share + - content.tabs + - navigation.expand + - navigation.footer + - navigation.path + - navigation.tracking + - content.buttons + - content.grid + - content.footnotes + - versioning.switcher # header disappears as you scroll + palette: + # Light mode / dark mode + # We deliberately don't automatically use `media` to check a user's preferences. We default to light mode as + # (a) it looks more professional, and (b) is more obvious about the fact that it offers a (dark mode) toggle. + - scheme: default + primary: white + accent: amber + toggle: + icon: material/weather-night + name: Switch to dark mode + - scheme: slate + primary: black + accent: amber + toggle: + icon: material/weather-sunny + name: Switch to light mode + icon: + repo: fontawesome/brands/github # GitHub logo in top right + # Equinox logo in top left + favicon: "_static/altrove_logo.png" + + # These additions are my own custom ones, having overridden a partial. + website_name: "Altrove" + website_url: "https://altrove.ai/" + +extra: + version: + provider: mike + social: + - icon: fontawesome/brands/github + link: https://github.com/altrove-ai/fiddledyn + +extra_css: + - _static/custom_css.css + +site_name: fiddledyn +site_description: Data contracts and I/O utilities for atomic structure datasets +site_url: https://altrove-ai.github.io/fiddledyn +repo_url: https://github.com/altrove-ai/fiddledyn +repo_name: altrove-ai/fiddledyn + +markdown_extensions: + - pymdownx.highlight: + use_pygments: true + pygments_style: github-dark + pygments_lang_class: true + line_spans: __span + anchor_linenums: true + auto_title: true + - pymdownx.inlinehilite + - pymdownx.snippets + - pymdownx.superfences + - pymdownx.tabbed: + alternate_style: true + - admonition + - pymdownx.details + - pymdownx.arithmatex: + generic: true + - footnotes + - pymdownx.critic + - pymdownx.caret + - pymdownx.keys + - pymdownx.mark + - pymdownx.tilde + - attr_list + - md_in_html + - toc: + permalink: true + +plugins: + - search + - autorefs # Cross-links to headings + - mkdocstrings: + #default_handler: python + handlers: + python: + options: + members_order: source + separate_signature: false + filters: ['!^_'] + docstring_options: + ignore_init_summary: true + merge_init_into_class: true + show_signature_annotations: false + signature_crossrefs: true + line_length: 120 + paths: [.src] + +nav: + - Home: index.md + - Getting Started: + - Installation: getting-started/installation.md + - Quick Start: getting-started/quick-start.md + - Basic Concepts: getting-started/basic-concepts.md + - Features: + - CLI Overrides: features/cli-overrides.md + - DAG References: features/dag-references.md + - File Overrides: features/file-overrides.md + - Partial Configs: features/partial-configs.md + - Callable References: features/callable-references.md + - API Reference: + - Core: api-reference/core.md + - IO: api-reference/io.md + - CLI: api-reference/cli.md + - Examples: examples.md + - Contributing: contributing.md diff --git a/pyproject.toml b/pyproject.toml index 0a32cb7..47ab6ea 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,11 @@ dev = [ "ipykernel>=7.1.0", "matplotlib>=3.10.8", ] +docs = [ + "mkdocs>=1.6.1", + "mkdocs-material>=9.7.1", + "mkdocstrings[python]>=1.0.2", +] [tool.uv] environments = ["sys_platform == 'linux'", "sys_platform == 'darwin'"] diff --git a/uv.lock b/uv.lock index f032370..9031b59 100644 --- a/uv.lock +++ b/uv.lock @@ -150,6 +150,27 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, ] +[[package]] +name = "babel" +version = "2.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/7d/6b/d52e42361e1aa00709585ecc30b3f9684b3ab62530771402248b1b1d6240/babel-2.17.0.tar.gz", hash = "sha256:0c54cffb19f690cdcc52a3b50bcbf71e07a808d1c80d549f2459b9d2cf0afb9d", size = 9951852, upload-time = "2025-02-01T15:17:41.026Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/b8/3fe70c75fe32afc4bb507f75563d39bc5642255d1d94f1f23604725780bf/babel-2.17.0-py3-none-any.whl", hash = "sha256:4d0b53093fdfb4b21c92b5213dba5a1b23885afa8383709427046b21c366e5f2", size = 10182537, upload-time = "2025-02-01T15:17:37.39Z" }, +] + +[[package]] +name = "backrefs" +version = "6.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/86/e3/bb3a439d5cb255c4774724810ad8073830fac9c9dee123555820c1bcc806/backrefs-6.1.tar.gz", hash = "sha256:3bba1749aafe1db9b915f00e0dd166cba613b6f788ffd63060ac3485dc9be231", size = 7011962, upload-time = "2025-11-15T14:52:08.323Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3b/ee/c216d52f58ea75b5e1841022bbae24438b19834a29b163cb32aa3a2a7c6e/backrefs-6.1-py310-none-any.whl", hash = "sha256:2a2ccb96302337ce61ee4717ceacfbf26ba4efb1d55af86564b8bbaeda39cac1", size = 381059, upload-time = "2025-11-15T14:51:59.758Z" }, + { url = "https://files.pythonhosted.org/packages/e6/9a/8da246d988ded941da96c7ed945d63e94a445637eaad985a0ed88787cb89/backrefs-6.1-py311-none-any.whl", hash = "sha256:e82bba3875ee4430f4de4b6db19429a27275d95a5f3773c57e9e18abc23fd2b7", size = 392854, upload-time = "2025-11-15T14:52:01.194Z" }, + { url = "https://files.pythonhosted.org/packages/37/c9/fd117a6f9300c62bbc33bc337fd2b3c6bfe28b6e9701de336b52d7a797ad/backrefs-6.1-py312-none-any.whl", hash = "sha256:c64698c8d2269343d88947c0735cb4b78745bd3ba590e10313fbf3f78c34da5a", size = 398770, upload-time = "2025-11-15T14:52:02.584Z" }, + { url = "https://files.pythonhosted.org/packages/02/e3/a4fa1946722c4c7b063cc25043a12d9ce9b4323777f89643be74cef2993c/backrefs-6.1-py39-none-any.whl", hash = "sha256:a9e99b8a4867852cad177a6430e31b0f6e495d65f8c6c134b68c14c3c95bf4b0", size = 381058, upload-time = "2025-11-15T14:52:06.698Z" }, +] + [[package]] name = "bcrypt" version = "5.0.0" @@ -269,6 +290,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/96/43/dae06432d0c4b1dc9e9149ad37b4ca8384cf6eb7700cd9215b177b914f0a/cloudpickle-3.0.0-py3-none-any.whl", hash = "sha256:246ee7d0c295602a036e86369c77fecda4ab17b506496730f2f576d9016fd9c7", size = 20088, upload-time = "2023-10-16T12:51:24.415Z" }, ] +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + [[package]] name = "colorful" version = "0.5.8" @@ -501,6 +531,11 @@ dev = [ { name = "pytest", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, { name = "ruff", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, ] +docs = [ + { name = "mkdocs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs-material", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocstrings", extra = ["python"], marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] [package.metadata] requires-dist = [ @@ -518,6 +553,11 @@ dev = [ { name = "pytest", specifier = ">=9.0.2" }, { name = "ruff", specifier = ">=0.14.11" }, ] +docs = [ + { name = "mkdocs", specifier = ">=1.6.1" }, + { name = "mkdocs-material", specifier = ">=9.7.1" }, + { name = "mkdocstrings", extras = ["python"], specifier = ">=1.0.2" }, +] [[package]] name = "filelock" @@ -574,6 +614,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/01/c9/97cc5aae1648dcb851958a3ddf73ccd7dbe5650d95203ecb4d7720b4cdbf/fsspec-2026.1.0-py3-none-any.whl", hash = "sha256:cb76aa913c2285a3b49bdd5fc55b1d7c708d7208126b60f2eb8194fe1b4cbdcc", size = 201838, upload-time = "2026-01-09T15:21:34.041Z" }, ] +[[package]] +name = "ghp-import" +version = "2.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "python-dateutil", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +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/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 = "google-api-core" version = "2.29.0" @@ -625,6 +677,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/91/4c/e0ce1ef95d4000ebc1c11801f9b944fa5910ecc15b5e351865763d8657f8/graphviz-0.21-py3-none-any.whl", hash = "sha256:54f33de9f4f911d7e84e4191749cac8cc5653f815b06738c54db9a15ab8b1e42", size = 47300, upload-time = "2025-06-15T09:35:04.433Z" }, ] +[[package]] +name = "griffe" +version = "1.15.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/0d/0c/3a471b6e31951dce2360477420d0a8d1e00dea6cf33b70f3e8c3ab6e28e1/griffe-1.15.0.tar.gz", hash = "sha256:7726e3afd6f298fbc3696e67958803e7ac843c1cfe59734b6251a40cdbfb5eea", size = 424112, upload-time = "2025-11-10T15:03:15.52Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9c/83/3b1d03d36f224edded98e9affd0467630fc09d766c0e56fb1498cbb04a9b/griffe-1.15.0-py3-none-any.whl", hash = "sha256:6f6762661949411031f5fcda9593f586e6ce8340f0ba88921a0f2ef7a81eb9a3", size = 150705, upload-time = "2025-11-10T15:03:13.549Z" }, +] + [[package]] name = "grpcio" version = "1.76.0" @@ -1034,6 +1098,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/0c/29/0348de65b8cc732daa3e33e67806420b2ae89bdce2b04af740289c5c6c8c/loguru-0.7.3-py3-none-any.whl", hash = "sha256:31a33c10c8e1e10422bfd431aeb5d351c7cf7fa671e3c4df004162264b28220c", size = 61595, upload-time = "2024-12-06T11:20:54.538Z" }, ] +[[package]] +name = "markdown" +version = "3.10.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b7/b1/af95bcae8549f1f3fd70faacb29075826a0d689a27f232e8cee315efa053/markdown-3.10.1.tar.gz", hash = "sha256:1c19c10bd5c14ac948c53d0d762a04e2fa35a6d58a6b7b1e6bfcbe6fefc0001a", size = 365402, upload-time = "2026-01-21T18:09:28.206Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/59/1b/6ef961f543593969d25b2afe57a3564200280528caa9bd1082eecdd7b3bc/markdown-3.10.1-py3-none-any.whl", hash = "sha256:867d788939fe33e4b736426f5b9f651ad0c0ae0ecf89df0ca5d1176c70812fe3", size = 107684, upload-time = "2026-01-21T18:09:27.203Z" }, +] + [[package]] name = "markdown-it-py" version = "4.0.0" @@ -1107,6 +1180,133 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, ] +[[package]] +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 = "click", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "ghp-import", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "jinja2", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "markdown", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mergedeep", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs-get-deps", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "packaging", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pathspec", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml-env-tag", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "watchdog", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +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/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 = "mkdocs-autorefs" +version = "1.4.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/51/fa/9124cd63d822e2bcbea1450ae68cdc3faf3655c69b455f3a7ed36ce6c628/mkdocs_autorefs-1.4.3.tar.gz", hash = "sha256:beee715b254455c4aa93b6ef3c67579c399ca092259cc41b7d9342573ff1fc75", size = 55425, upload-time = "2025-08-26T14:23:17.223Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/4d/7123b6fa2278000688ebd338e2a06d16870aaf9eceae6ba047ea05f92df1/mkdocs_autorefs-1.4.3-py3-none-any.whl", hash = "sha256:469d85eb3114801d08e9cc55d102b3ba65917a869b893403b8987b601cf55dc9", size = 25034, upload-time = "2025-08-26T14:23:15.906Z" }, +] + +[[package]] +name = "mkdocs-get-deps" +version = "0.2.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mergedeep", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "platformdirs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/98/f5/ed29cd50067784976f25ed0ed6fcd3c2ce9eb90650aa3b2796ddf7b6870b/mkdocs_get_deps-0.2.0.tar.gz", hash = "sha256:162b3d129c7fad9b19abfdcb9c1458a651628e4b1dea628ac68790fb3061c60c", size = 10239, upload-time = "2023-11-20T17:51:09.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9f/d4/029f984e8d3f3b6b726bd33cafc473b75e9e44c0f7e80a5b29abc466bdea/mkdocs_get_deps-0.2.0-py3-none-any.whl", hash = "sha256:2bf11d0b133e77a0dd036abeeb06dec8775e46efa526dc70667d8863eefc6134", size = 9521, upload-time = "2023-11-20T17:51:08.587Z" }, +] + +[[package]] +name = "mkdocs-material" +version = "9.7.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "babel", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "backrefs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "colorama", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "jinja2", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "markdown", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs-material-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "paginate", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pygments", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pymdown-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "requests", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/27/e2/2ffc356cd72f1473d07c7719d82a8f2cbd261666828614ecb95b12169f41/mkdocs_material-9.7.1.tar.gz", hash = "sha256:89601b8f2c3e6c6ee0a918cc3566cb201d40bf37c3cd3c2067e26fadb8cce2b8", size = 4094392, upload-time = "2025-12-18T09:49:00.308Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3e/32/ed071cb721aca8c227718cffcf7bd539620e9799bbf2619e90c757bfd030/mkdocs_material-9.7.1-py3-none-any.whl", hash = "sha256:3f6100937d7d731f87f1e3e3b021c97f7239666b9ba1151ab476cabb96c60d5c", size = 9297166, upload-time = "2025-12-18T09:48:56.664Z" }, +] + +[[package]] +name = "mkdocs-material-extensions" +version = "1.3.1" +source = { registry = "https://pypi.org/simple" } +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/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 = "mkdocstrings" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "jinja2", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "markdown", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "markupsafe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs-autorefs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pymdown-extensions", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/63/4d/1ca8a9432579184599714aaeb36591414cc3d3bfd9d494f6db540c995ae4/mkdocstrings-1.0.2.tar.gz", hash = "sha256:48edd0ccbcb9e30a3121684e165261a9d6af4d63385fc4f39a54a49ac3b32ea8", size = 101048, upload-time = "2026-01-24T15:57:25.735Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/57/32/407a9a5fdd7d8ecb4af8d830b9bcdf47ea68f916869b3f44bac31f081250/mkdocstrings-1.0.2-py3-none-any.whl", hash = "sha256:41897815a8026c3634fe5d51472c3a569f92ded0ad8c7a640550873eea3b6817", size = 35443, upload-time = "2026-01-24T15:57:23.933Z" }, +] + +[package.optional-dependencies] +python = [ + { name = "mkdocstrings-python", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] + +[[package]] +name = "mkdocstrings-python" +version = "2.0.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "griffe", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocs-autorefs", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "mkdocstrings", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/24/75/d30af27a2906f00eb90143470272376d728521997800f5dce5b340ba35bc/mkdocstrings_python-2.0.1.tar.gz", hash = "sha256:843a562221e6a471fefdd4b45cc6c22d2607ccbad632879234fa9692e9cf7732", size = 199345, upload-time = "2025-12-03T14:26:11.755Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/81/06/c5f8deba7d2cbdfa7967a716ae801aa9ca5f734b8f54fd473ef77a088dbe/mkdocstrings_python-2.0.1-py3-none-any.whl", hash = "sha256:66ecff45c5f8b71bf174e11d49afc845c2dfc7fc0ab17a86b6b337e0f24d8d90", size = 105055, upload-time = "2025-12-03T14:26:10.184Z" }, +] + [[package]] name = "msgpack" version = "1.1.2" @@ -1329,6 +1529,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/b7/b9/c538f279a4e237a006a2c98387d081e9eb060d203d8ed34467cc0f0b9b53/packaging-26.0-py3-none-any.whl", hash = "sha256:b36f1fef9334a5588b4166f8bcd26a14e521f2b55e6b9de3aaa80d3ff7a37529", size = 74366, upload-time = "2026-01-21T20:50:37.788Z" }, ] +[[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 = "paramiko" version = "4.0.0" @@ -1353,6 +1562,15 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/16/32/f8e3c85d1d5250232a5d3477a2a28cc291968ff175caeadaf3cc19ce0e4a/parso-0.8.5-py2.py3-none-any.whl", hash = "sha256:646204b5ee239c396d040b90f9e272e9a8017c630092bf59980beb62fd033887", size = 106668, upload-time = "2025-08-23T15:15:25.663Z" }, ] +[[package]] +name = "pathspec" +version = "1.0.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/fa/36/e27608899f9b8d4dff0617b2d9ab17ca5608956ca44461ac14ac48b44015/pathspec-1.0.4.tar.gz", hash = "sha256:0210e2ae8a21a9137c0d470578cb0e595af87edaa6ebf12ff176f14a02e0e645", size = 131200, upload-time = "2026-01-27T03:59:46.938Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/ef/3c/2c197d226f9ea224a9ab8d197933f9da0ae0aac5b6e0f884e2b8d9c8e9f7/pathspec-1.0.4-py3-none-any.whl", hash = "sha256:fb6ae2fd4e7c921a165808a552060e722767cfa526f99ca5156ed2ce45a5c723", size = 55206, upload-time = "2026-01-27T03:59:45.137Z" }, +] + [[package]] name = "pexpect" version = "4.9.0" @@ -1630,6 +1848,19 @@ 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.20.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/1e/6c/9e370934bfa30e889d12e61d0dae009991294f40055c238980066a7fbd83/pymdown_extensions-10.20.1.tar.gz", hash = "sha256:e7e39c865727338d434b55f1dd8da51febcffcaebd6e1a0b9c836243f660740a", size = 852860, upload-time = "2026-01-24T05:56:56.758Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/40/6d/b6ee155462a0156b94312bdd82d2b92ea56e909740045a87ccb98bf52405/pymdown_extensions-10.20.1-py3-none-any.whl", hash = "sha256:24af7feacbca56504b313b7b418c4f5e1317bb5fea60f03d57be7fcc40912aa0", size = 268768, upload-time = "2026-01-24T05:56:54.537Z" }, +] + [[package]] name = "pynacl" version = "1.6.2" @@ -1736,6 +1967,18 @@ wheels = [ { url = "https://files.pythonhosted.org/packages/be/8e/98435a21d1d4b46590d5459a22d88128103f8da4c2d4cb8f14f2a96504e1/pyyaml-6.0.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:41715c910c881bc081f1e8872880d3c650acf13dfa8214bad49ed4cede7c34ea", size = 790181, upload-time = "2025-09-25T21:32:18.834Z" }, ] +[[package]] +name = "pyyaml-env-tag" +version = "1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyyaml", marker = "sys_platform == 'darwin' or sys_platform == 'linux'" }, +] +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 = "pyzmq" version = "27.1.0" @@ -2125,6 +2368,24 @@ 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/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/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" }, +] + [[package]] name = "wcwidth" version = "0.5.0"