Add dataclass and pydantic support with simplified implementation#33
Merged
Add dataclass and pydantic support with simplified implementation#33
Conversation
- Replace deprecated pyo3::PyObject with Py<PyAny> - Add #[cfg(feature = "pydantic_support")] to is_module_installed to fix unused function warning - Replace deprecated Python::with_gil with Python::attach in tests 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Fix typo: 'representtion' → 'representation' in src/de.rs - Improve clarity of OnceCell safety comments in src/py_module_cache.rs 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Dataclasses are part of Python's standard library (Python 3.7+), so there's no need to feature-gate them. This commit: - Removes the `dataclass_support` feature flag - Makes `once_cell` a regular dependency (always enabled) - Removes all `#[cfg(feature = "dataclass_support")]` gates - Keeps `pydantic_support` as an optional feature (external library) Dataclass support is now always available by default. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit improves test organization and documentation: **Test Documentation:** - Add module-level documentation to all test files explaining their purpose - Clarify test direction: check_revertible.rs tests Rust → Python → Rust only - Clarify python_types.rs tests Python → Rust deserialization - Document that reverse direction (Python → Rust → Python) is NOT tested **Test Reorganization:** - Split Python-specific type tests into separate file `tests/python_types.rs` - Move 4 tests from `check_revertible.rs` to new file: * check_python_object (custom Python classes) * check_dataclass_object (dataclasses) * check_dataclass_object_nested (nested dataclasses) * check_pydantic_object (Pydantic models) **Benefits:** - Clearer separation of concerns between generic Rust types and Python types - Explicit documentation of test coverage and limitations - Easier to understand what each test suite covers - Better maintainability as Python type support grows 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Split `tests/python_types.rs` into three specialized test files for better organization and clarity: **New Test Files:** - `tests/python_custom_class.rs` - Custom Python classes with `__dict__` * Tests basic user-defined Python classes * 1 test: check_python_object - `tests/python_dataclass.rs` - Python dataclasses (standard library) * Tests Python 3.7+ dataclasses * 2 tests: check_dataclass_object, check_dataclass_object_nested * Includes nested dataclass structures - `tests/python_pydantic.rs` - Pydantic models (optional feature) * Tests Pydantic BaseModel subclasses * 1 test: check_pydantic_object * Requires `pydantic_support` feature * Uses `#![cfg(feature = "pydantic_support")]` at file level **Changes:** - Deleted `tests/python_types.rs` - Added comprehensive documentation to each new test file explaining: * What Python type is being tested * How the type works in Python * The testing strategy used - Updated references in `check_revertible.rs` to point to new files - Applied feature gate at file level for pydantic tests **Benefits:** - Each Python type has its own dedicated test file - Easier to locate and maintain tests for specific Python types - Clear separation between standard library (dataclasses) and external library (Pydantic) features - Better discoverability for contributors - Pydantic tests are completely skipped when feature is disabled 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
Configure CI to test all 4 combinations of pydantic (installed/not installed) and abi3 (enabled/disabled) with fail-fast disabled to ensure comprehensive coverage across different dependency configurations. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
- Rename py_module_cache.rs to pydantic.rs - Remove caching mechanism (once_cell) for simpler implementation - Implement pydantic_model_as_dict similar to dataclass_as_dict - Replace expect() calls with proper error propagation - Add debug logging when pydantic is not found - Update de.rs to use unified pydantic_model_as_dict function 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
If pydantic module is not found, the test now returns early with a skip message instead of failing. This allows tests to pass in environments where pydantic is not installed. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
There was a problem hiding this comment.
Pull Request Overview
This PR adds support for Python dataclasses and Pydantic models to the deserialization pipeline, enabling seamless conversion of these Python types to Rust structs. The implementation includes infrastructure improvements, better error handling, and comprehensive testing.
Key changes:
- Added dataclass and Pydantic model deserialization support via new
dataclass.rsandpydantic.rsmodules - Updated to PyO3 0.27.0, replacing deprecated
downcast()withcast()andDowncastErrorwithCastError - Enhanced CI testing with matrix strategy covering pydantic presence and abi3 feature combinations
Reviewed Changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| src/dataclass.rs | New module for dataclass-to-dict conversion using dataclasses.asdict() |
| src/pydantic.rs | New module for Pydantic model-to-dict conversion using model_dump() with graceful fallback |
| src/de.rs | Updated deserializer to check for dataclass/Pydantic types before __dict__ fallback; replaced deprecated downcast() with cast() |
| src/error.rs | Replaced deprecated DowncastError with CastError |
| src/lib.rs | Added module declarations for dataclass and pydantic |
| tests/python_dataclass.rs | New test suite for dataclass deserialization (simple and nested) |
| tests/python_pydantic.rs | New test suite for Pydantic model deserialization with auto-skip when unavailable |
| tests/python_custom_class.rs | New test suite for custom Python classes, extracted from check_revertible.rs |
| tests/check_revertible.rs | Added comprehensive documentation and removed custom class test (moved to dedicated file) |
| tests/to_json_to_pyobject.rs | Added comprehensive documentation explaining cross-validation approach |
| Cargo.toml | Version bump to 0.8.0, updated PyO3 to 0.27.0, added log dependency, removed abi3-py38 feature |
| .github/workflows/rust.yml | Added test matrix for pydantic and abi3 combinations |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR adds support for Python dataclasses and Pydantic models, building upon the work in #23 with significant architectural improvements.
Overview
This implementation extends deserialization capabilities to support:
Both types are now seamlessly converted to Rust structs via
from_pyobject.Key Differences from #23
Simplified Architecture
once_celldependency for simpler, more maintainable codedataclass.rsandpydantic.rsfor better organization*_as_dict()pattern returningOption<PyDict>Improved Error Handling
.expect()calls with proper?error propagationNoneinstead of panicking)Better Testing
API Changes
downcast()withcast()throughoutDowncastErrorwithCastErrorImplementation Details
Dataclass Support (
src/dataclass.rs)dataclasses.is_dataclass()to check if object is a dataclassdataclasses.asdict()Nonefor non-dataclass objectsPydantic Support (
src/pydantic.rs)Noneif not available)BaseModelmodel_dump()methodNonefor non-pydantic objectsDeserialization Flow (
src/de.rs)The deserializer now checks types in this order:
dataclass_as_dict()pydantic_model_as_dict()__dict__attribute__slots__attributeTesting
New test files organized by Python object type:
tests/python_custom_class.rs- Custom classes with__dict__tests/python_dataclass.rs- Dataclass tests (simple and nested)tests/python_pydantic.rs- Pydantic model tests (auto-skips if not installed)All tests follow the pattern:
Why Remove Caching?
The caching mechanism from #23 added complexity with minimal benefit:
once_cellremoved)Migration from #23
If you were using the internal API from #23:
is_pydantic_base_model()→pydantic_model_as_dict()booltoOption<PyDict>py_module_cache→pydanticThe public API (
from_pyobject,to_pyobject) remains unchanged.Version Bump
Version bumped to 0.8.0 to reflect the new features and breaking changes in internal APIs.