From 1c90a2bfbc341aa42fdaa825732a91c703002e88 Mon Sep 17 00:00:00 2001 From: Vilson Rodrigues Date: Thu, 27 Nov 2025 02:18:54 -0300 Subject: [PATCH 1/3] docs: Add CLAUDE.md with project guidelines - Complete project structure overview - Common commands and workflows - Release process (always use ./scripts/release.sh) - Architecture details and optimizations - Linting, testing, and CI/CD guides - Troubleshooting tips This file provides context for Claude Code to work more effectively with the project without repeating instructions. --- CLAUDE.md | 291 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 291 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..bf15bc6 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,291 @@ +# CLAUDE.md + +This file provides guidance to Claude Code when working with code in this repository. + +## Overview + +**msgspec-ext** is a high-performance settings management library built on top of msgspec. It provides a pydantic-like API for loading settings from environment variables and .env files, with **3.8x better performance** than pydantic-settings. + +## Project Structure + +``` +msgspec-ext/ +├── src/msgspec_ext/ # Source code +│ ├── settings.py # Core BaseSettings implementation +│ └── version.py # Version string +├── tests/ # Test suite +│ └── test_settings.py # Comprehensive unit tests (22 tests) +├── examples/ # Practical examples +│ ├── 01_basic_usage.py +│ ├── 02_env_prefix.py +│ ├── 03_dotenv_file.py +│ ├── 04_advanced_types.py +│ ├── 05_serialization.py +│ └── README.md +├── scripts/ # Automation scripts +│ ├── release.sh # Release automation (use this!) +│ └── setup-branch-protection.sh +└── benchmark.py # Performance benchmarks + +## Common Commands + +### Development + +```bash +# Install dependencies +uv sync + +# Install with dev dependencies +uv sync --group dev + +# Add new dependency +uv add + +# Run tests +uv run pytest tests/ -v + +# Run specific test +uv run pytest tests/test_settings.py::test_name -v + +# Run linter (only checks src/) +uv run ruff check src/ + +# Format code +uv run ruff format + +# Run benchmark +uv run python benchmark.py +``` + +### Testing + +All tests are in `tests/test_settings.py`: +- 22 comprehensive unit tests +- Tests cover: basic usage, env vars, type conversion, .env files, validation, serialization +- Run with: `uv run pytest tests/ -v` +- Should complete in < 0.1s + +### Releases + +**IMPORTANT**: Always use the release script: + +```bash +# Create a new release +./scripts/release.sh + +# Example: +./scripts/release.sh 0.2.0 +``` + +The script will: +1. Update `src/msgspec_ext/version.py` +2. Create git tag +3. Push to upstream +4. Trigger GitHub Actions for PyPI publishing + +**Never manually edit version.py** - always use the release script! + +## Architecture + +### Core Implementation (`settings.py`) + +**Key optimization**: Uses bulk JSON decoding instead of field-by-field validation. + +```python +# Old approach (slow): +for field in fields: + value = msgspec.convert(env_value, field_type) # Python loop + +# New approach (fast): +json_bytes = encoder.encode(all_values) # Cached encoder +return decoder.decode(json_bytes) # Cached decoder, all in C! +``` + +**Important classes**: +- `BaseSettings`: Wrapper factory that creates msgspec.Struct instances +- `SettingsConfigDict`: Configuration (env_file, env_prefix, etc.) + +**Performance optimizations**: +- Cached encoders/decoders (ClassVar) +- Automatic field ordering (required before optional) +- Bulk JSON decoding in C +- Zero Python loops for validation + +### Type Handling + +Environment variables are always strings, but we need proper types: + +```python +_preprocess_env_value(env_value: str, field_type: type) -> Any +``` + +Handles: +- `bool`: "true"/"false"/"1"/"0" → True/False +- `int`/`float`: String to number conversion +- `list`/`dict`: JSON parsing for complex types +- `Optional[T]`: Unwraps Union types correctly + +### Field Ordering + +`msgspec.defstruct` requires required fields before optional fields. We handle this automatically in `_create_struct_class()`: + +```python +required_fields = [(name, type), ...] +optional_fields = [(name, type, default), ...] +fields = required_fields + optional_fields # Correct order +``` + +## Linting & Code Style + +**Ruff configuration** (`pyproject.toml`): +- Target: Python 3.10+ +- Line length: 88 +- Strict linting for `src/` +- Relaxed rules for `tests/` and `examples/` + +**Running lint**: +```bash +# Check only src/ (recommended for quick checks) +uv run ruff check src/ + +# Check everything +uv run ruff check + +# Auto-fix issues +uv run ruff check --fix + +# Format code +uv run ruff format +``` + +**Important per-file ignores**: +- `tests/`: Ignores D (docstrings), S104 (binding), S105 (passwords), etc. +- `examples/`: Ignores D, S101, S104, S105, T201, F401 +- `benchmark.py`: Ignores D, S101, S105, T201, etc. + +## Examples + +5 practical examples in `examples/`: +1. **Basic usage**: Defaults, env vars, explicit values +2. **Environment prefixes**: Organizing settings with `env_prefix` +3. **.env files**: Loading from dotenv files +4. **Advanced types**: Optional, lists, dicts, JSON parsing +5. **Serialization**: `model_dump()`, `model_dump_json()`, `schema()` + +Run examples: +```bash +uv run python examples/01_basic_usage.py +``` + +## Benchmarking + +```bash +uv run python benchmark.py +``` + +Expected results: +- msgspec-ext: ~0.7ms per load +- pydantic-settings: ~2.7ms per load (if installed) +- **Speedup**: 3.8x faster than pydantic-settings + +## CI/CD + +GitHub Actions workflows: +- **CI**: Runs on every push (Ruff, Tests, Build) +- **Publish**: Publishes to PyPI on new tags +- **Release Drafter**: Auto-generates release notes + +**Creating a PR**: +1. Create feature branch from main +2. Make changes +3. Run tests: `uv run pytest tests/ -v` +4. Run lint: `uv run ruff check src/` +5. Format: `uv run ruff format` +6. Push and create PR +7. Ensure CI passes (Ruff, Tests, Build) + +## Common Workflows + +### Adding a new feature + +1. Create branch: `git checkout -b feat/feature-name` +2. Implement feature in `src/msgspec_ext/settings.py` +3. Add tests in `tests/test_settings.py` +4. Add example in `examples/` (if user-facing) +5. Run tests: `uv run pytest tests/ -v` +6. Run lint: `uv run ruff check src/` +7. Create PR + +### Fixing a bug + +1. Add failing test in `tests/test_settings.py` +2. Fix bug in `src/msgspec_ext/settings.py` +3. Verify test passes +4. Run full test suite +5. Create PR + +### Updating dependencies + +```bash +# Update specific package +uv add package@latest + +# Update all dependencies +uv sync --upgrade +``` + +## Performance Tips + +- **Always use cached encoder/decoder**: Don't create new instances +- **Bulk operations**: Process all fields at once via JSON decode +- **Avoid Python loops**: Let msgspec handle validation in C +- **Field ordering**: Required before optional (automatic) +- **Type hints**: Proper annotations enable better performance + +## Troubleshooting + +**Tests failing**: +```bash +uv run pytest tests/ -v # See detailed output +``` + +**Lint errors**: +```bash +uv run ruff check src/ # Check src only +uv run ruff check --fix # Auto-fix +``` + +**Import errors**: +```bash +uv sync # Reinstall dependencies +``` + +**Performance regression**: +```bash +uv run python benchmark.py # Compare with baseline +``` + +## Key Files + +- `src/msgspec_ext/settings.py` - Core implementation (most important) +- `src/msgspec_ext/version.py` - Version (updated by release script) +- `tests/test_settings.py` - Test suite (22 tests) +- `benchmark.py` - Performance benchmarks +- `scripts/release.sh` - Release automation (**use this for releases!**) +- `pyproject.toml` - Project config, dependencies, ruff settings + +## Documentation + +- `README.md` - User-facing docs with quickstart +- `examples/README.md` - Example gallery guide +- `CONTRIBUTING.md` - Contribution guidelines +- `CLAUDE.md` - This file (for Claude Code) + +## Notes for Claude Code + +- **Releases**: Always use `./scripts/release.sh `, never edit version.py manually +- **Linting**: Focus on `src/` only (`uv run ruff check src/`) +- **Tests**: Must maintain 100% pass rate (22/22) +- **Performance**: Benchmark should show ~0.7ms per load, 3.8x vs pydantic +- **Examples**: Update if changing user-facing APIs +- **Breaking changes**: Require major version bump From d266e224b9c32bfcfa9f9a51fda9ee1c82790b11 Mon Sep 17 00:00:00 2001 From: Vilson Rodrigues Date: Thu, 27 Nov 2025 19:03:49 -0300 Subject: [PATCH 2/3] Add CHANGELOG.md for release tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- CHANGELOG.md | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 CHANGELOG.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..b3e5447 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,35 @@ +# Changelog + +All notable changes to this project will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +### Added +- Comprehensive benchmark suite with statistical analysis +- Support for nested configuration via environment variables +- Improved documentation with accurate performance claims + +### Changed +- Moved benchmark files to dedicated `/benchmark` directory +- Updated performance benchmarks: 2.7x faster than pydantic-settings +- Enhanced benchmark with 10 runs and statistical validation + +### Fixed +- Merge-bot workflow now correctly handles PR branch checkouts +- Lint and formatting issues in benchmark code + +## [0.1.0] - 2025-01-15 + +### Added +- Initial release +- BaseSettings class for environment-based configuration +- .env file support via python-dotenv +- Type validation using msgspec +- Support for common types: str, int, float, bool, list +- Field prefixes and delimiters +- Case-sensitive/insensitive matching +- JSON schema generation +- Performance optimizations with bulk JSON decoding From caf3fa3b4f42ceab7845f8d2e1d6fd38e4fe2981 Mon Sep 17 00:00:00 2001 From: Vilson Rodrigues Date: Sun, 22 Mar 2026 19:37:17 -0300 Subject: [PATCH 3/3] feat: expose load_dotenv in public API and document usage Co-Authored-By: Claude Opus 4.6 --- README.md | 18 ++++++++++++++++++ src/msgspec_ext/__init__.py | 2 ++ 2 files changed, 20 insertions(+) diff --git a/README.md b/README.md index a26ad6c..f8168b1 100644 --- a/README.md +++ b/README.md @@ -387,6 +387,24 @@ APP_DATABASE__HOST=localhost APP_DATABASE__PORT=5432 ``` +### Standalone `load_dotenv` + +```python +from msgspec_ext import load_dotenv + +# Load .env file into os.environ (does not override existing vars) +load_dotenv() + +# Load from a custom path +load_dotenv("config/.env") + +# Override existing environment variables +load_dotenv(".env", override=True) + +# Custom encoding +load_dotenv(".env", encoding="latin-1") +``` + ### Nested Configuration ```python diff --git a/src/msgspec_ext/__init__.py b/src/msgspec_ext/__init__.py index ce25982..1a3fc53 100644 --- a/src/msgspec_ext/__init__.py +++ b/src/msgspec_ext/__init__.py @@ -1,5 +1,6 @@ import msgspec +from .fast_dotenv import load_dotenv from .settings import BaseSettings, SettingsConfigDict, _dec_hook, _enc_hook from .types import ( AnyUrl, @@ -69,4 +70,5 @@ "SettingsConfigDict", "dec_hook", "enc_hook", + "load_dotenv", ]