Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/check_pull_request_title.yml
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ jobs:
Invalid: "add feature", "Adds feature", "Add.", "Fix"
# Disable type prefixes (we don't use conventional commits format)
requireScope: false
ignoreLabels:
- ignore-title-check
ignoreLabels: |
ignore-title-check
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ dataset:
```python
from sparkwheel import Config

config = Config.load("config.yaml")
config = Config()
config.update("config.yaml")
model = config.resolve("model") # Actual torch.nn.Linear(784, 10) instance!
```

Expand Down Expand Up @@ -88,7 +89,6 @@ model:
- [Full Documentation](https://project-lighter.github.io/sparkwheel/)
- [Quick Start Guide](https://project-lighter.github.io/sparkwheel/getting-started/quickstart/)
- [Core Concepts](https://project-lighter.github.io/sparkwheel/user-guide/basics/)
- [Examples](https://project-lighter.github.io/sparkwheel/examples/simple/)
- [API Reference](https://project-lighter.github.io/sparkwheel/reference/)

## Community
Expand Down
1 change: 0 additions & 1 deletion docs/getting-started/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,5 @@ python train.py training::learning_rate=0.01 dataset::batch_size=64
Now that you've seen the basics:

- **[Core Concepts](../user-guide/basics.md)** - Learn more about references, expressions, and instantiation
- **[Examples](../examples/simple.md)** - See complete real-world examples
- **[Composition & Operators](../user-guide/operators.md)** - Master config composition with `=` and `~`
- **[Schema Validation](../user-guide/schema-validation.md)** - Validate configs with dataclasses
8 changes: 0 additions & 8 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,6 @@ Sparkwheel has two types of references with distinct purposes:

[:octicons-arrow-right-24: Core Concepts](user-guide/basics.md)

- :material-lightbulb-on-outline:{ .lg .middle } __Examples__

---

See complete real-world configuration patterns

[:octicons-arrow-right-24: View Examples](examples/simple.md)

- :material-code-tags:{ .lg .middle } __API Reference__

---
Expand Down
18 changes: 12 additions & 6 deletions docs/user-guide/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -171,7 +171,8 @@ model: # Merges by default!
```python
from sparkwheel import Config

config = Config.load("base.yaml")
config = Config()
config.update("base.yaml")
config.update("override.yaml")

# Result:
Expand Down Expand Up @@ -213,7 +214,8 @@ Use `~key: null` to delete a key, or `~key: [items]` to delete specific items fr
```

```python
config = Config.load("base.yaml")
config = Config()
config.update("base.yaml")
config.update({"~model::dropout": None}) # Remove entire key
config.update({"~plugins": [0, 2]}) # Remove list items
config.update({"~dataloaders": ["train", "test"]}) # Remove dict keys
Expand All @@ -226,7 +228,8 @@ config.update({"~dataloaders": ["train", "test"]}) # Remove dict keys
Apply operators programmatically:

```python
config = Config.load("config.yaml")
config = Config()
config.update("config.yaml")

# Set individual values
config.set("model::hidden_size", 1024)
Expand Down Expand Up @@ -267,7 +270,8 @@ Sparkwheel provides helpful error messages with suggestions:
```python
from sparkwheel import Config, ConfigKeyError

config = Config.load({
config = Config()
config.update({
"model": {"hidden_size": 512, "num_layers": 4},
"training": {"batch_size": 32}
})
Expand Down Expand Up @@ -295,7 +299,8 @@ Pre-import modules for use in expressions:
from sparkwheel import Config

# Pre-import torch for all expressions
config = Config.load("config.yaml", globals={"torch": "torch", "np": "numpy"})
config = Config(globals={"torch": "torch", "np": "numpy"})
config.update("config.yaml")

# Now expressions can use torch and np without importing
```
Expand All @@ -312,7 +317,8 @@ data: "$np.array([1, 2, 3])"
```python
from sparkwheel import Config

config: Config = Config.load("config.yaml")
config: Config = Config()
config.update("config.yaml")
resolved: dict = config.resolve()
```

Expand Down
2 changes: 1 addition & 1 deletion docs/user-guide/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -190,4 +190,4 @@ python train.py base.yaml optimizer::lr=0.001 trainer::epochs=100
- **[Configuration Basics](basics.md)** - Loading and accessing configs
- **[Operators](operators.md)** - Composition, replacement, and deletion
- **[Schema Validation](schema-validation.md)** - Type-safe configs with dataclasses
- **[API Reference](references.md)** - Full API documentation
- **[API Reference](reference/)** - Full API documentation
4 changes: 2 additions & 2 deletions docs/user-guide/expressions.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@ learning_rate: "$0.001 * (@training::batch_size ** 0.5)"
from sparkwheel import Config

try:
config = Config.load("config.yaml")
config = Config()
config.update("config.yaml")
resolved = config.resolve()
except SyntaxError as e:
print(f"Expression syntax error: {e}")
Expand All @@ -247,4 +248,3 @@ except Exception as e:

- [Instantiation](instantiation.md) - Create objects with expressions
- [Advanced Features](advanced.md) - Complex expression patterns
- [Examples](../examples/deep-learning.md) - Real-world expression usage
3 changes: 2 additions & 1 deletion docs/user-guide/instantiation.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ model:
```python
from sparkwheel import Config

config = Config.load("config.yaml")
config = Config()
config.update("config.yaml")

# Instantiate the object
model = config.resolve("model")
Expand Down
1 change: 0 additions & 1 deletion docs/user-guide/operators.md
Original file line number Diff line number Diff line change
Expand Up @@ -535,4 +535,3 @@ Sparkwheel goes beyond Hydra with:

- **[Configuration Basics](basics.md)** - Core config management
- **[Advanced Features](advanced.md)** - Macros and power features
- **[Examples](../examples/simple.md)** - Real-world patterns
54 changes: 51 additions & 3 deletions docs/user-guide/references.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,61 @@ Sparkwheel provides two types of references for linking configuration values:
| Feature | `@ref` (Resolved) | `%ref` (Raw) | `$expr` (Expression) |
|---------|-------------------|--------------|----------------------|
| **Returns** | Final computed value | Raw YAML content | Evaluated expression result |
| **When processed** | Lazy (`resolve()`) | Eager (`update()`) | Lazy (`resolve()`) |
| **Instantiates objects** | ✅ Yes | ❌ No | ✅ Yes (if referenced) |
| **Evaluates expressions** | ✅ Yes | ❌ No | ✅ Yes |
| **Use in dataclass validation** | ✅ Yes | ⚠️ Limited | ✅ Yes |
| **CLI override compatible** | ✅ Yes | ✅ Yes | ❌ No |
| **Cross-file references** | ✅ Yes | ✅ Yes | ❌ No |
| **When to use** | Get computed results | Copy config structures | Compute new values |

## Two-Stage Processing Model

Sparkwheel processes references at different times to enable safe config composition:

!!! abstract "When References Are Processed"

**Stage 1: Eager Processing (during `update()`)**

- **Raw References (`%`)** are expanded immediately when configs are merged
- Enables safe config composition and pruning workflows
- External file references resolved at load time

**Stage 2: Lazy Processing (during `resolve()`)**

- **Resolved References (`@`)** are processed on-demand
- **Expressions (`$`)** are evaluated when needed
- **Components (`_target_`)** are instantiated only when requested
- Supports complex dependency graphs and deferred instantiation

**Why two stages?**

This separation enables powerful workflows like config pruning:

```yaml
# base.yaml
system:
lr: 0.001
batch_size: 32

experiment:
model:
optimizer:
lr: "%system::lr" # Copies raw value 0.001 eagerly

~system: null # Delete system section after copying
```

```python
config = Config()
config.update("base.yaml")
# % references already expanded during update()
# ~system deletion applied after expansion
# Result: experiment::model::optimizer::lr = 0.001 (system deleted safely)
```

With `@` references, this would fail because they resolve lazily after deletion.

## Resolution Flow

!!! abstract "How References Are Resolved"
Expand All @@ -25,10 +73,10 @@ Sparkwheel provides two types of references for linking configuration values:

**Step 2: Determine Type**

- **`@key`** → Proceed to dependency resolution
- **`%key`** → Return raw YAML immediately ✅
- **`%key`** → Expanded eagerly during `update()` ✅
- **`@key`** → Proceed to dependency resolution (lazy)

**Step 3: Resolve Dependencies** (for `@` references)
**Step 3: Resolve Dependencies** (for `@` references during `resolve()`)

- Check for circular references → ❌ **Error if found**
- Resolve all dependencies first
Expand Down
4 changes: 2 additions & 2 deletions src/sparkwheel/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,8 @@
EvaluationError,
FrozenConfigError,
InstantiationError,
ModuleNotFoundError,
SourceLocation,
TargetNotFoundError,
)

__version__ = "0.0.6"
Expand All @@ -47,7 +47,7 @@
"REMOVE_KEY",
"REPLACE_KEY",
"BaseError",
"ModuleNotFoundError",
"TargetNotFoundError",
"CircularReferenceError",
"InstantiationError",
"ConfigKeyError",
Expand Down
8 changes: 7 additions & 1 deletion src/sparkwheel/coercion.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
"""Type coercion for schema validation."""
"""Type coercion for schema validation.

Automatically converts values between compatible types when safe and unambiguous,
making configs more flexible while maintaining type safety. Supports string to
numeric/bool conversions, int to float, and recursive coercion through nested
structures. Enabled by default via `Config(coerce=True)`.
"""

import dataclasses
import types
Expand Down
Loading
Loading