-
Notifications
You must be signed in to change notification settings - Fork 2
Add DataclassAdapter using Pydantic TypeAdapter #147
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
- Implement DataclassAdapter that works with standard library dataclasses - Leverage Pydantic's TypeAdapter for validation and serialization - Support both single dataclasses and list[dataclass] types - Include comprehensive test suite with 11 tests covering: - Basic CRUD operations - Default value handling - Validation error handling (ignore/raise modes) - Nested dataclasses - List dataclass support with proper wrapping - Batch operations (put_many, get_many, delete_many) - TTL operations - Type validation on initialization - Validate dataclass types using dataclasses.is_dataclass() Resolves #140 Co-authored-by: William Easton <strawgate@users.noreply.github.com>
📝 WalkthroughWalkthroughExtracts Pydantic validation/serialization into new BasePydanticAdapter (sync + async), makes PydanticAdapter inherit it, adds async and sync DataclassAdapter implementations, updates package re-exports, and adds tests for dataclass and list-valued adapters. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant DataclassAdapter
participant PydanticAdapter
participant BasePydanticAdapter
participant KeyValue
rect rgb(245,250,255)
Note over BasePydanticAdapter: Centralized (de)serialization & validation
end
Client->>DataclassAdapter: put(key, dataclass_instance)
DataclassAdapter->>BasePydanticAdapter: _serialize_model(value)
BasePydanticAdapter->>KeyValue: put(serialized_payload)
Client->>PydanticAdapter: get(key)
PydanticAdapter->>BasePydanticAdapter: get(key) -> raw payload
BasePydanticAdapter->>BasePydanticAdapter: _validate_model(raw)
BasePydanticAdapter-->>PydanticAdapter: validated model
PydanticAdapter-->>Client: model
Possibly related PRs
Suggested labels
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
📜 Recent review detailsConfiguration used: CodeRabbit UI Review profile: ASSERTIVE Plan: Pro 📒 Files selected for processing (2)
🧰 Additional context used📓 Path-based instructions (3)**/*.py📄 CodeRabbit inference engine (AGENTS.md)
Files:
key-value/key-value-sync/**📄 CodeRabbit inference engine (AGENTS.md)
Files:
key-value/key-value-aio/**📄 CodeRabbit inference engine (AGENTS.md)
Files:
🧬 Code graph analysis (2)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (2)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (10)
🔇 Additional comments (9)
Comment |
|
@claude should the pydantic and dataclass adapters share a BasePydanticAdapter to share code? |
|
Claude finished @strawgate's task —— View job Analysis: Should PydanticAdapter and DataclassAdapter share a base class?After thoroughly reviewing both adapters, here's my architectural recommendation: Shared Code AnalysisThe two adapters have nearly identical implementations (~95% code duplication): Identical sections:
Only differences:
Recommendation: Yes, create a
|
|
@claude please refactor into the basepydanticadapter |
|
Claude finished @strawgate's task —— View job Refactoring to BasePydanticAdapterI've successfully refactored both PydanticAdapter and DataclassAdapter to share a common BasePydanticAdapter base class, eliminating code duplication. Tasks
SummaryWhat was done: Code reduction:
Architecture:
Benefits:
Testing:
| |
Created a new BasePydanticAdapter abstract base class that contains all shared functionality between PydanticAdapter and DataclassAdapter. This eliminates ~200 lines of duplicated code and addresses the SonarQube code duplication warning (9.5% -> expected to be <3%). Key changes: - Added BasePydanticAdapter in adapters/base/ with all shared methods - Refactored PydanticAdapter to inherit from BasePydanticAdapter - Refactored DataclassAdapter to inherit from BasePydanticAdapter - Both adapters now only contain their __init__ and _get_model_type_name() Benefits: - DRY principle: eliminates code duplication - Maintainability: bug fixes only need to be made once - Consistency: ensures both adapters behave identically - Future-proofing: new adapters can reuse the base class All tests pass (17 tests), type checking passes with 0 errors. Co-authored-by: William Easton <strawgate@users.noreply.github.com>
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
14-47: Refactor to base adapter looks solid.Initialization and model naming are consistent. Consider mirroring DataclassAdapter’s list-arg presence check for earlier, clearer errors when a generic list lacks type args.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (6)
key-value/key-value-aio/src/key_value/aio/adapters/base/__init__.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/base/adapter.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/__init__.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py(2 hunks)key-value/key-value-aio/tests/adapters/test_dataclass.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python code must adhere to Ruff formatting and linting with a maximum line length of 140 characters
Use Basedpyright in strict mode for type checking
Use Beartype for runtime type checking (can be disabled via PY_KEY_VALUE_DISABLE_BEARTYPE=true)
Code must be compatible with Python 3.10+ (codegen targets 3.10)
Files:
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/base/__init__.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.pykey-value/key-value-aio/tests/adapters/test_dataclass.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/__init__.pykey-value/key-value-aio/src/key_value/aio/adapters/base/adapter.py
key-value/key-value-aio/**
📄 CodeRabbit inference engine (AGENTS.md)
Async-first: Make all code changes in the async package (key-value/key-value-aio) and regenerate sync
Files:
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/base/__init__.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.pykey-value/key-value-aio/tests/adapters/test_dataclass.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/__init__.pykey-value/key-value-aio/src/key_value/aio/adapters/base/adapter.py
🧬 Code graph analysis (6)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (3)
key-value/key-value-aio/src/key_value/aio/adapters/base/adapter.py (2)
BasePydanticAdapter(15-232)_get_model_type_name(30-36)key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (1)
AsyncKeyValue(184-189)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
_get_model_type_name(45-47)
key-value/key-value-aio/src/key_value/aio/adapters/base/__init__.py (1)
key-value/key-value-aio/src/key_value/aio/adapters/base/adapter.py (1)
BasePydanticAdapter(15-232)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (2)
key-value/key-value-aio/src/key_value/aio/adapters/base/adapter.py (2)
BasePydanticAdapter(15-232)_get_model_type_name(30-36)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
_get_model_type_name(68-70)
key-value/key-value-aio/tests/adapters/test_dataclass.py (2)
key-value/key-value-shared/src/key_value/shared/errors/key_value.py (1)
DeserializationError(14-15)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
DataclassAdapter(14-70)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/__init__.py (1)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
DataclassAdapter(14-70)
key-value/key-value-aio/src/key_value/aio/adapters/base/adapter.py (4)
key-value/key-value-shared/src/key_value/shared/errors/key_value.py (2)
DeserializationError(14-15)SerializationError(10-11)key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (1)
AsyncKeyValue(184-189)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
_get_model_type_name(68-70)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
_get_model_type_name(45-47)
🔇 Additional comments (6)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/__init__.py (1)
1-3: Public export looks good.Re-export is minimal and consistent with package layout and tests.
key-value/key-value-aio/src/key_value/aio/adapters/base/__init__.py (1)
1-3: LGTM.Public API for BasePydanticAdapter is correctly exposed.
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (4)
1-9: LGTM!The imports are well-organized and all necessary for the implementation.
11-21: LGTM!The class definition and docstring are clear. The unbounded TypeVar is acceptable since runtime validation ensures the type is a dataclass.
57-57: Verify the necessity of type: ignore.The assignment of
dataclass_type(type[T]) to_inner_type(type[Any]) should be type-safe without requiring# type: ignore[assignment]. Please verify whether this ignore comment is necessary with Basedpyright in strict mode, or if it can be removed.
68-70: LGTM!The implementation correctly returns "dataclass" for error messages, following the same pattern as PydanticAdapter.
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py
Outdated
Show resolved
Hide resolved
|
@claude please incorporate the feedback |
|
Claude finished @strawgate's task —— View job CodeRabbit Review Feedback AddressedI've successfully incorporated all the feedback from CodeRabbit's review! Changes MadeCritical Fixes:
Major Fix:
Minor Improvements:
Edge-Case Tests Added:
Verification
|
- Replace truthiness checks with explicit None checks in BasePydanticAdapter to correctly handle empty dicts/lists - Validate missing 'items' wrapper for list models instead of silently accepting malformed payloads - Restrict list model detection to only 'list' type (not all Sequence types) in DataclassAdapter - Update @bear_spray comment to accurately describe why it's needed - Include type in error message for missing type argument - Add edge-case tests for empty list TTL and missing 'items' wrapper scenarios - All 28 adapter tests passing, linting and type checking clean Co-authored-by: William Easton <strawgate@users.noreply.github.com>
|
@claude is there any check we can do to Pydantic models or data classes or special feature of the type adapter or something where we can confirm the model is serializable on init of the adapter instead of when it tries to serialize the first time? Know what I mean? |
|
Claude finished @strawgate's task —— View job Investigating serialization validation at adapter initLooking into whether we can validate model serializability during adapter initialization instead of waiting for the first serialization attempt. Tasks:
Research FindingsI investigated Pydantic's TypeAdapter API and tested several approaches for early serialization validation. Here's what I found: What TypeAdapter Provides
Key DiscoveryTypeAdapter and Pydantic cannot detect serialization issues without actual data. The serialization errors only occur at runtime when you call I tested this with a dataclass containing an @dataclass
class BadDataclass:
name: str
func: Any # Lambda is not JSON serializable
adapter = TypeAdapter(BadDataclass)
adapter.json_schema() # ✅ Succeeds
adapter.pydantic_complete # ✅ Returns True
instance = BadDataclass(name='test', func=lambda x: x)
adapter.dump_python(instance, mode='json') # ❌ PydanticSerializationErrorPossible Solution: Dummy Instance ValidationWe CAN create minimal "test instances" to validate serialization at init time: def _validate_serializable(self) -> None:
"""Validate that the model type is serializable by creating and serializing a test instance.
Raises:
TypeError: If the model cannot be instantiated with default values.
SerializationError: If the model cannot be serialized to JSON.
"""
# For list models, test with an empty list
if self._is_list_model:
try:
self._type_adapter.dump_python([], mode="json")
except PydanticSerializationError as e:
msg = f"List model type is not JSON serializable: {e}"
raise SerializationError(msg) from e
return
# For single models, try to create a minimal instance
test_instance = self._create_test_instance()
if test_instance is None:
# Can't create test instance, skip validation
return
try:
self._type_adapter.dump_python(test_instance, mode="json")
except PydanticSerializationError as e:
msg = f"{self._get_model_type_name()} type is not JSON serializable: {e}"
raise SerializationError(msg) from e
def _create_test_instance(self) -> T | None:
"""Try to create a minimal test instance for serialization validation.
Returns:
A minimal instance with default/empty values, or None if instance cannot be created.
"""
# For dataclasses
if is_dataclass(self._inner_type):
from dataclasses import fields, MISSING
kwargs = {}
for f in fields(self._inner_type):
# Skip fields with defaults
if f.default is not MISSING or f.default_factory is not MISSING:
continue
# Create minimal value based on type
field_type = f.type
origin = get_origin(field_type)
if field_type in (str, 'str'):
kwargs[f.name] = ''
elif field_type in (int, 'int'):
kwargs[f.name] = 0
elif field_type in (float, 'float'):
kwargs[f.name] = 0.0
elif field_type in (bool, 'bool'):
kwargs[f.name] = False
elif origin is list:
kwargs[f.name] = []
elif origin is dict:
kwargs[f.name] = {}
elif origin is set:
kwargs[f.name] = set()
else:
# Can't create minimal value for this type
return None
try:
return self._inner_type(**kwargs)
except Exception:
return None
# For Pydantic models
if isinstance(self._inner_type, type) and issubclass(self._inner_type, BaseModel):
try:
# Pydantic models can often be constructed with construct()
return self._inner_type.model_construct()
except Exception:
return None
return NoneRecommendationI recommend implementing this validation as an optional feature: Option A: Add
|
|
@claude makes sense, let's skip that Please do a through review of the pr |
|
Claude finished @strawgate's task —— View job Thorough PR ReviewI've completed a comprehensive review of PR #147. This is an excellent refactoring that successfully addresses code duplication while adding valuable new functionality. Here's my detailed analysis: Summary AssessmentOverall: ✅ EXCELLENT - This PR demonstrates strong software engineering practices with a well-designed architecture, comprehensive testing, and proper handling of previous review feedback. Key Achievements:
Architecture & Design Review✅ Excellent: BasePydanticAdapter PatternThe refactoring to extract a Strengths:
Key Design Decisions:
File:
Code Quality Review✅ Excellent: Implementation Quality1. Validation Logic (lines 38-68 in base/adapter.py)Strengths:
if self._is_list_model:
if "items" not in value:
if self._raise_on_validation_error:
msg = f"Invalid {self._get_model_type_name()} payload: missing 'items' wrapper"
raise DeserializationError(msg)
return NoneThis is exactly right - no silent failures on malformed data. 2. Serialization Logic (lines 70-94 in base/adapter.py)Strengths:
3. None Handling (Critical Fix)Excellent work addressing CodeRabbit's critical feedback: All truthiness checks replaced with explicit
This correctly handles empty dicts 4. DataclassAdapter Implementation (dataclass/adapter.py)Strengths:
Minor observation: Line 56 has a 5. PydanticAdapter Refactoring (pydantic/adapter.py)Excellent refactoring:
Note: Line 39 still uses
Test Coverage Review✅ Excellent: Comprehensive Test SuiteFile: Test Coverage Summary:
Total: 14 tests (11 original + 3 edge cases added per CodeRabbit feedback) Highlights:
Test Data Models:
Type Safety Review✅ Excellent: Type Safety MaintainedGeneric Type Usage:
Overload Signatures:
Runtime Type Checking:
Verification:
CodeRabbit Feedback Verification✅ All Critical Issues AddressedReviewing commit
All feedback properly addressed ✓ Documentation Quality✅ Good: Documentation PresentDocstrings:
However: CodeRabbit notes docstring coverage is 72.50% (below 80% threshold). Consider running Code Comments:
Potential Issues & RecommendationsMinor Observations
Performance Considerations✅ Good: No Performance RegressionsRefactoring Impact:
Validation Performance:
Security Considerations✅ Good: No Security IssuesInput Validation:
Error Handling:
Backward Compatibility✅ Excellent: 100% Backward CompatiblePydanticAdapter:
Breaking Changes:
Final RecommendationsMust Do (Before Merge)
Should Do (Nice to Have)
Optional (Future Work)
ConclusionThis PR demonstrates excellent software engineering practices: ✅ Architecture: Clean abstraction with proper inheritance The refactoring successfully achieves its goals:
Recommendation: ✅ APPROVE AND MERGE (after regenerating sync library) This is a high-quality PR that improves the codebase significantly. Great work! 🎉 |
|
Skipped: Cannot respond to another bot. |
1 similar comment
|
Skipped: Cannot respond to another bot. |
|
@coderabbitai please review |
|
@strawgate: I'll review the changes in this PR now. ✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py (1)
1-459: Critical: Changes made to generated file will be overwritten.This file is auto-generated (as stated in lines 1-3) and should never be edited directly. The coding guidelines explicitly state: "Never edit files in key-value/key-value-sync; the sync package is generated and changes will be overwritten."
While the changes themselves are valuable improvements (enhanced docstrings and error handling), they must be applied to the original async source file instead. The sync version will then be regenerated from that source.
Please revert these changes and apply them to the original async base.py file (likely at
key-value/key-value-aio/src/key_value/aio/stores/base.py), then regenerate the sync package.As per coding guidelines.
key-value/key-value-sync/src/key_value/sync/code_gen/wrappers/base.py (1)
1-75: Don't edit auto-generated files—modify the original source file and regenerate instead.The generated file
key-value/key-value-sync/src/key_value/sync/code_gen/wrappers/base.pywas modified directly (marked asMin the PR), but the corresponding original source filekey-value/key-value-aio/src/key_value/aio/wrappers/base.pywas not modified.This violates both the file header warning ("DO NOT CHANGE! Change the original file instead") and the coding guideline ("Never edit files in key-value/key-value-sync; the sync package is generated and changes will be overwritten").
Required action:
- Revert changes to
key-value/key-value-sync/src/key_value/sync/code_gen/wrappers/base.py- Apply the docstring changes to the original file:
key-value/key-value-aio/src/key_value/aio/wrappers/base.py- Run
python scripts/build_sync_library.pyto regenerate the sync package
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (14)
key-value/key-value-sync/src/key_value/sync/adapters/base/__init__.py(1 hunks)key-value/key-value-sync/src/key_value/sync/adapters/dataclass/__init__.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/__init__.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py(2 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py(2 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py(3 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.py(2 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/stores/redis/store.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/wrappers/base.py(1 hunks)key-value/key-value-sync/tests/code_gen/adapters/test_dataclass.py(1 hunks)key-value/key-value-sync/tests/code_gen/adapters/test_raise.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python code must adhere to Ruff formatting and linting with a maximum line length of 140 characters
Use Basedpyright in strict mode for type checking
Use Beartype for runtime type checking (can be disabled via PY_KEY_VALUE_DISABLE_BEARTYPE=true)
Code must be compatible with Python 3.10+ (codegen targets 3.10)
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.pykey-value/key-value-sync/src/key_value/sync/adapters/dataclass/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.pykey-value/key-value-sync/src/key_value/sync/code_gen/wrappers/base.pykey-value/key-value-sync/tests/code_gen/adapters/test_raise.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/__init__.pykey-value/key-value-sync/src/key_value/sync/adapters/base/__init__.pykey-value/key-value-sync/tests/code_gen/adapters/test_dataclass.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/redis/store.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/base.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py
key-value/key-value-sync/**
📄 CodeRabbit inference engine (AGENTS.md)
Never edit files in key-value/key-value-sync; the sync package is generated and changes will be overwritten
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.pykey-value/key-value-sync/src/key_value/sync/adapters/dataclass/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.pykey-value/key-value-sync/src/key_value/sync/code_gen/wrappers/base.pykey-value/key-value-sync/tests/code_gen/adapters/test_raise.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/__init__.pykey-value/key-value-sync/src/key_value/sync/adapters/base/__init__.pykey-value/key-value-sync/tests/code_gen/adapters/test_dataclass.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/redis/store.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/base.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py
{key-value/key-value-aio/src/key_value/aio/stores/**,key-value/key-value-sync/src/key_value/sync/code_gen/stores/**}
📄 CodeRabbit inference engine (AGENTS.md)
Store implementations must store ManagedEntry wrappers, not raw values
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/redis/store.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py
key-value/key-value-sync/src/key_value/sync/code_gen/stores/**
📄 CodeRabbit inference engine (AGENTS.md)
Sync store implementations must conform to the KeyValue protocol (generated mirror) from key_value/sync/code_gen/protocols/key_value.py
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/redis/store.pykey-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py
🧠 Learnings (1)
📚 Learning: 2025-10-27T00:52:38.578Z
Learnt from: CR
PR: strawgate/py-key-value#0
File: AGENTS.md:0-0
Timestamp: 2025-10-27T00:52:38.578Z
Learning: Applies to key-value/key-value-sync/src/key_value/sync/code_gen/stores/** : Sync store implementations must conform to the KeyValue protocol (generated mirror) from key_value/sync/code_gen/protocols/key_value.py
Applied to files:
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py
🧬 Code graph analysis (11)
key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.py (2)
key-value/key-value-shared/src/key_value/shared/utils/managed_entry.py (2)
ManagedEntry(14-119)from_dict(78-113)key-value/key-value-aio/src/key_value/aio/stores/mongodb/store.py (1)
managed_entry_to_document(51-69)
key-value/key-value-sync/src/key_value/sync/adapters/dataclass/__init__.py (1)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
DataclassAdapter(16-69)
key-value/key-value-sync/tests/code_gen/adapters/test_raise.py (5)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (2)
ttl(206-234)ttl_many(236-242)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (3)
ttl(29-40)ttl_many(78-89)keys(133-140)key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py (3)
ttl(215-224)ttl_many(228-238)keys(355-361)key-value/key-value-sync/src/key_value/sync/code_gen/wrappers/base.py (2)
ttl(47-48)ttl_many(52-53)key-value/key-value-shared/src/key_value/shared/errors/key_value.py (1)
MissingKeyError(18-25)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/__init__.py (1)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
DataclassAdapter(16-69)
key-value/key-value-sync/src/key_value/sync/adapters/base/__init__.py (1)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (1)
BasePydanticAdapter(18-242)
key-value/key-value-sync/tests/code_gen/adapters/test_dataclass.py (4)
key-value/key-value-shared/src/key_value/shared/errors/key_value.py (1)
DeserializationError(14-15)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
DataclassAdapter(16-69)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (12)
put(173-182)get(100-100)get(103-103)get(105-132)delete(194-198)get_many(135-135)get_many(138-138)get_many(140-171)ttl(206-234)put_many(184-192)delete_many(200-204)ttl_many(236-242)key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py (9)
put(270-279)get(181-202)delete(322-326)get_many(206-211)keys(355-361)ttl(215-224)put_many(282-302)delete_many(330-335)ttl_many(228-238)
key-value/key-value-sync/src/key_value/sync/code_gen/stores/redis/store.py (2)
key-value/key-value-shared/src/key_value/shared/utils/managed_entry.py (2)
to_json(65-75)ManagedEntry(14-119)key-value/key-value-aio/src/key_value/aio/stores/redis/store.py (1)
json_to_managed_entry(39-51)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.py (1)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (1)
BasePydanticAdapter(18-242)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (2)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (2)
BasePydanticAdapter(18-242)_get_model_type_name(33-39)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (2)
PydanticAdapter(14-47)_get_model_type_name(45-47)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (4)
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (10)
KeyValue(177-182)get(17-27)get_many(66-76)keys(133-140)put(42-52)ttl(29-40)put_many(91-104)delete(54-64)delete_many(106-116)ttl_many(78-89)key-value/key-value-shared/src/key_value/shared/errors/key_value.py (2)
DeserializationError(14-15)SerializationError(10-11)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
_get_model_type_name(67-69)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
_get_model_type_name(45-47)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (3)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (2)
BasePydanticAdapter(18-242)_get_model_type_name(33-39)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (1)
KeyValue(177-182)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
_get_model_type_name(45-47)
🔇 Additional comments (13)
key-value/key-value-sync/src/key_value/sync/code_gen/stores/mongodb/store.py (1)
1-3: The review comment is incorrect; the developer properly modified the source aio file and regenerated the sync file.The comprehensive docstrings you reference are already present in the aio source file (lines 36-47), and the sync file correctly reflects those changes through automatic regeneration. The commit "Syncronize the syncronous part of the library" indicates the sync file was properly regenerated from the updated aio source, which is the correct procedure. The developer did not manually edit the auto-generated sync file directly—they edited the aio source and regenerated.
Likely an incorrect or invalid review comment.
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (1)
61-63: LGTM: clearer delete() return contract.The explicit True/False semantics for delete() are helpful.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
23-66: Solid initialization and invariants.
- Correctly detects list models, extracts inner type, and validates with is_dataclass.
- Storing a single TypeAdapter and using BasePydanticAdapter wiring is clean.
key-value/key-value-sync/tests/code_gen/adapters/test_raise.py (1)
43-68: Good TTL coverage for raise-on-missing paths.Tests exercise ttl/ttl_many with positive and missing-key cases and avoid brittle TTL equality checks.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/__init__.py (1)
4-6: LGTM: public re-export is correct.key-value/key-value-sync/src/key_value/sync/adapters/dataclass/__init__.py (1)
4-6: LGTM: stable import path exposed.key-value/key-value-sync/src/key_value/sync/adapters/base/__init__.py (1)
4-6: LGTM: base adapter re-export matches codegen source.key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.py (1)
4-6: LGTM: generated package all is correct.key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (2)
17-44: Nice consolidation onto BasePydanticAdapter.Constructor wires the base correctly; surface area is much smaller and clearer. Based on learnings
38-41: Do not apply these changes to this file; it is generated and will be overwritten.This file is in the
key-value/key-value-sync/directory, which per the coding guidelines must never be edited directly. The sync package is auto-generated, and any modifications here will be lost on the next generation.To address the underlying concern about unifying list-model detection between adapters, the changes must be made to the source template or generator that produces this file, not to the generated output itself.
Likely an incorrect or invalid review comment.
key-value/key-value-sync/tests/code_gen/adapters/test_dataclass.py (1)
168-177: White-box assertion of storage shape looks good.Accessing MemoryStore internals to assert the list-wrapping contract is appropriate here and guarded with explicit pyright ignores. LGTM.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/adapter.py (2)
184-192: Guard put_many against keys/values length mismatch.Currently, a mismatch can cause silent misalignment or backend errors. Raise fast with a clear exception. Apply in the original adapter source.
Apply this diff:
def put_many( self, keys: Sequence[str], values: Sequence[T], *, collection: str | None = None, ttl: SupportsFloat | None = None ) -> None: """Serialize and store multiple models, preserving order alignment with keys.""" collection = collection or self._default_collection - - value_dicts: list[dict[str, Any]] = [self._serialize_model(value=value) for value in values] + if len(keys) != len(values): + raise ValueError("keys and values must have the same length") + value_dicts: list[dict[str, Any]] = [self._serialize_model(value=value) for value in values] self._key_value.put_many(keys=keys, values=value_dicts, collection=collection, ttl=ttl)Likely an incorrect or invalid review comment.
26-31: Expose a public knob for validation-error behavior.Tests currently flip a private flag. Provide a tiny public setter/property to avoid private access. Apply in the original adapter source.
Apply this diff:
class BasePydanticAdapter(Generic[T], ABC): @@ _raise_on_validation_error: bool @@ + @property + def raise_on_validation_error(self) -> bool: + return self._raise_on_validation_error + + @raise_on_validation_error.setter + def raise_on_validation_error(self, value: bool) -> None: + self._raise_on_validation_error = valueThis enables:
adapter.raise_on_validation_error = Truewithout pyright ignores and keeps API consistent across adapters.Likely an incorrect or invalid review comment.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py
Show resolved
Hide resolved
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py
Outdated
Show resolved
Hide resolved
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py
Show resolved
Hide resolved
key-value/key-value-sync/src/key_value/sync/code_gen/stores/redis/store.py
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 5
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
38-41: Unify list-model detection policy across adapters.Here you set list-model with issubclass(origin, Sequence), while DataclassAdapter uses origin is list. This divergence can surprise users and create storage-shape mismatches for typing.Sequence[...] in DataclassAdapter. Recommend standardizing on Sequence detection in both adapters (preferred), or document that both are list‑only and tighten this check accordingly. If you adopt Sequence, update DataclassAdapter to match and keep the {"items": ...} wrapping consistent. I’ve included a concrete diff on the DataclassAdapter comment.
♻️ Duplicate comments (1)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (1)
67-71: Do not include raw payloads in validation errors (privacy/compliance).Avoid logging the entire stored value in error messages.
Apply in the AIO base (source) and regenerate sync:
- except ValidationError as e: - if self._raise_on_validation_error: - msg = f"Invalid {self._get_model_type_name()}: {value}" - raise DeserializationError(msg) from e + except ValidationError as e: + if self._raise_on_validation_error: + # Do not include the input payload; keep details actionable. + details = e.errors(include_input=False) + msg = f"Invalid {self._get_model_type_name()}: {details}" + raise DeserializationError(msg) from e return None
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (10)
key-value/key-value-aio/src/key_value/aio/adapters/base/__init__.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py(2 hunks)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py(1 hunks)key-value/key-value-sync/src/key_value/sync/adapters/base/__init__.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py(2 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py(0 hunks)
💤 Files with no reviewable changes (1)
- key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python code must adhere to Ruff formatting and linting with a maximum line length of 140 characters
Use Basedpyright in strict mode for type checking
Use Beartype for runtime type checking (can be disabled via PY_KEY_VALUE_DISABLE_BEARTYPE=true)
Code must be compatible with Python 3.10+ (codegen targets 3.10)
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.pykey-value/key-value-sync/src/key_value/sync/adapters/base/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/base/__init__.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py
key-value/key-value-sync/**
📄 CodeRabbit inference engine (AGENTS.md)
Never edit files in key-value/key-value-sync; the sync package is generated and changes will be overwritten
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.pykey-value/key-value-sync/src/key_value/sync/adapters/base/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py
key-value/key-value-aio/**
📄 CodeRabbit inference engine (AGENTS.md)
Async-first: Make all code changes in the async package (key-value/key-value-aio) and regenerate sync
Files:
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/base/__init__.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py
🧬 Code graph analysis (9)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (2)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (2)
BasePydanticAdapter(18-242)_get_model_type_name(33-39)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (2)
PydanticAdapter(14-47)_get_model_type_name(45-47)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (2)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (2)
BasePydanticAdapter(15-239)_get_model_type_name(30-36)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (2)
PydanticAdapter(17-47)_get_model_type_name(45-47)
key-value/key-value-sync/src/key_value/sync/adapters/base/__init__.py (1)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (1)
BasePydanticAdapter(18-242)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.py (1)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (1)
BasePydanticAdapter(18-242)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (2)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (2)
BasePydanticAdapter(18-242)_get_model_type_name(33-39)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (1)
KeyValue(177-182)
key-value/key-value-aio/src/key_value/aio/adapters/base/__init__.py (1)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (1)
BasePydanticAdapter(15-239)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (2)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (2)
BasePydanticAdapter(15-239)_get_model_type_name(30-36)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (2)
DataclassAdapter(16-69)_get_model_type_name(67-69)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (5)
key-value/key-value-shared/src/key_value/shared/errors/key_value.py (2)
DeserializationError(14-15)SerializationError(10-11)key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (1)
AsyncKeyValue(184-189)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (16)
BasePydanticAdapter(18-242)_get_model_type_name(33-39)_validate_model(41-71)_serialize_model(73-97)get(100-100)get(103-103)get(105-132)get_many(135-135)get_many(138-138)get_many(140-171)put(173-182)ttl(206-234)put_many(184-192)delete(194-198)delete_many(200-204)ttl_many(236-242)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
_get_model_type_name(67-69)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
_get_model_type_name(45-47)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (4)
key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py (9)
get(181-202)get_many(206-211)keys(355-361)put(270-279)ttl(215-224)put_many(282-302)delete(322-326)delete_many(330-335)ttl_many(228-238)key-value/key-value-shared/src/key_value/shared/errors/key_value.py (2)
DeserializationError(14-15)SerializationError(10-11)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (1)
KeyValue(177-182)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (16)
BasePydanticAdapter(15-239)_get_model_type_name(30-36)_validate_model(38-68)_serialize_model(70-94)get(97-97)get(100-100)get(102-129)get_many(132-132)get_many(135-135)get_many(137-168)put(170-179)ttl(203-231)put_many(181-189)delete(191-195)delete_many(197-201)ttl_many(233-239)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: test_quick (3.10, windows-latest, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, windows-2022, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, windows-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, windows-2022, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, macos-latest, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, macos-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, ubuntu-22.04, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, ubuntu-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, macos-14, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, ubuntu-22.04, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, ubuntu-latest, key-value/key-value-sync)
🔇 Additional comments (10)
key-value/key-value-sync/src/key_value/sync/adapters/base/__init__.py (1)
1-6: No issues found; auto-generated file is correctly structured.Verification confirms:
- Source file exists with matching export pattern (BasePydanticAdapter)
- Code generation script is in place
- Target class definition exists in the imported module
- Import path and all declaration are correctly aligned with the code generation pattern (aio → sync.code_gen)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/base/__init__.py (1)
1-6: No changes needed—architectural pattern is intentional and auto-generated by design.The file is correctly auto-generated by
build_sync_library.py. For__init__.pyfiles in the aio package, the script intentionally outputs two copies: one incode_gen(the converted version) and one in the main namespace (for consistency with aio's structure). The re-export ofBasePydanticAdapterincode_gen/adapters/base/__init__.pymirrors the exact pattern used in aio'sadapters/base/__init__.pyand is part of the public API. The file header correctly marks it as auto-generated and warns against manual edits.key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (7)
1-10: LGTM! Imports and TypeVar are well-organized.All imports are necessary and used. The TypeVar definition is appropriate for the generic adapter pattern.
13-20: LGTM! Class structure follows the base adapter pattern correctly.The class properly inherits from
BasePydanticAdapter[T]and the docstring accurately describes support for both standard library and Pydantic dataclasses.
43-46: LGTM! List detection correctly restricts tolisttypes.The use of
origin is listproperly detectslist[dataclass]types without incorrectly matching other sequence types. This addresses the past review feedback.
48-56: LGTM! Inner type extraction handles both single and list dataclass types correctly.The error message now includes the type for better debugging (addressing past feedback). The
type: ignore[assignment]on line 56 is justified since the subsequent dataclass validation ensures type safety.
58-61: LGTM! Dataclass validation provides clear error handling.The validation ensures that both single and list inner types are actual dataclasses, providing a clear error message at initialization rather than during first use.
63-65: LGTM! TypeAdapter initialization is correct.Using the original
dataclass_type(rather than_inner_type) for the TypeAdapter ensures proper serialization/deserialization of both single dataclasses andlist[dataclass]types. All required base class attributes are properly initialized.
67-69: LGTM! Correctly implements the required abstract method.The implementation returns an appropriate model type name for error messages generated by the base class methods.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
17-43: LGTM; mirrors the AIO adapter and base.Constructor wiring and _get_model_type_name are correct; no further issues here.
Confirm the AIO source remains the single point of change and that code-gen stays in sync. As per coding guidelines.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (1)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
46-47: List model detection remains overly broad.This was flagged in a previous review: the current logic checks
issubclass(origin, Sequence), which matcheslist,tuple,Sequence, and other Sequence subclasses. According to the PR objectives, this adapter should support "list[dataclass] types" specifically, not all Sequence types.Additionally, the
isinstance(origin, type)check is redundant—issubclasswill raise TypeError if its first argument is not a type, so the explicit isinstance check adds no value.Apply this diff to restrict to
listspecifically and remove the redundant check:- origin = get_origin(dataclass_type) - self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence) + origin = get_origin(dataclass_type) + self._is_list_model = origin is list
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (4)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-aio/tests/adapters/test_pydantic.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py(1 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python code must adhere to Ruff formatting and linting with a maximum line length of 140 characters
Use Basedpyright in strict mode for type checking
Use Beartype for runtime type checking (can be disabled via PY_KEY_VALUE_DISABLE_BEARTYPE=true)
Code must be compatible with Python 3.10+ (codegen targets 3.10)
Files:
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.pykey-value/key-value-aio/tests/adapters/test_pydantic.py
key-value/key-value-sync/**
📄 CodeRabbit inference engine (AGENTS.md)
Never edit files in key-value/key-value-sync; the sync package is generated and changes will be overwritten
Files:
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py
key-value/key-value-aio/**
📄 CodeRabbit inference engine (AGENTS.md)
Async-first: Make all code changes in the async package (key-value/key-value-aio) and regenerate sync
Files:
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-aio/tests/adapters/test_pydantic.py
🧬 Code graph analysis (4)
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py (3)
key-value/key-value-aio/tests/adapters/test_pydantic.py (3)
test_simple_adapter_with_list(94-100)product_list_adapter(68-69)Product(22-26)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
PydanticAdapter(17-47)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (3)
put(42-52)get(17-27)delete(54-64)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (3)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (2)
BasePydanticAdapter(15-239)_get_model_type_name(30-36)key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (1)
AsyncKeyValue(184-189)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (2)
DataclassAdapter(17-70)_get_model_type_name(68-70)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (2)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (2)
BasePydanticAdapter(18-242)_get_model_type_name(33-39)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (2)
DataclassAdapter(14-70)_get_model_type_name(68-70)
key-value/key-value-aio/tests/adapters/test_pydantic.py (2)
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py (3)
test_simple_adapter_with_list(97-103)product_list_adapter(71-72)Product(25-29)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
PydanticAdapter(14-47)
🪛 GitHub Actions: Run Tests
key-value/key-value-aio/tests/adapters/test_pydantic.py
[error] 94-94: Type 'list[datetime]' cannot be assigned to type variable 'T@PydanticAdapter'. Pyright/type-checking error reported by 'uv run basedpyright .'
🪛 GitHub Check: static_analysis (key-value/key-value-aio)
key-value/key-value-aio/tests/adapters/test_pydantic.py
[failure] 96-96:
Type "list[datetime] | None" is not assignable to declared type "list[Product] | None"
Type "list[datetime] | None" is not assignable to type "list[Product] | None"
Type "list[datetime]" is not assignable to type "list[Product] | None"
"list[datetime]" is not assignable to "list[Product]"
Type parameter "_T@list" is invariant, but "datetime" is not the same as "Product"
Consider switching from "list" to "Sequence" which is covariant
"list[datetime]" is not assignable to "None" (reportAssignmentType)
[failure] 95-95:
Argument of type "list[Product]" cannot be assigned to parameter "value" of type "list[datetime]" in function "put"
"Product" is not assignable to "datetime"
"Product" is not assignable to "datetime" (reportArgumentType)
[failure] 94-94:
Type "list[datetime]" cannot be assigned to type variable "T@PydanticAdapter"
Type "list[datetime]" is not assignable to upper bound "BaseModel | Sequence[BaseModel]" for type variable "T@PydanticAdapter"
Type "list[datetime]" is not assignable to type "BaseModel | Sequence[BaseModel]"
"list[datetime]" is not assignable to "BaseModel"
"list[datetime]" is not assignable to "Sequence[BaseModel]"
Type parameter "_T_co@Sequence" is covariant, but "datetime" is not a subtype of "BaseModel"
"datetime" is not assignable to "BaseModel" (reportInvalidTypeArguments)
🔇 Additional comments (7)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (6)
1-11: LGTM: Imports are appropriate.All imports are correctly chosen for the DataclassAdapter implementation. The Sequence import on line 1 is currently used for list model detection.
14-19: LGTM: Class declaration and documentation are clear.The class correctly inherits from BasePydanticAdapter[T] and the docstring accurately describes its purpose and capabilities.
21-25: LGTM: Correct use of bear_spray with accurate explanation.The comment accurately explains why
@bear_sprayis necessary—beartype cannot handle the parameterized type annotationtype[T]used for this generic adapter.
26-43: LGTM: Method signature and documentation are correct.The
__init__method signature is appropriate, and the docstring accurately documents all parameters with terminology consistent with the parameter types.
50-66: LGTM: Inner type extraction and validation logic are correct.The implementation properly:
- Extracts the inner type from list models with a helpful error message that includes the actual type
- Validates that the inner type is a dataclass with a clear error message
- Initializes all required base class attributes
68-70: LGTM: Correct implementation of abstract method.The
_get_model_type_namemethod properly implements the abstract method fromBasePydanticAdapter, returning "dataclass" for use in error messages.key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
1-3: LGTM: Auto-generation warning is clear.The warning appropriately indicates this file is generated and directs developers to modify the source file instead, aligning with the project's code generation workflow.
| origin = get_origin(dataclass_type) | ||
| self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence) | ||
|
|
||
| # Extract the inner type for list models | ||
| if self._is_list_model: | ||
| args = get_args(dataclass_type) | ||
| if not args: | ||
| msg = f"List type {dataclass_type} must have a type argument" | ||
| raise TypeError(msg) | ||
| self._inner_type = args[0] | ||
| else: | ||
| self._inner_type = dataclass_type | ||
|
|
||
| # Validate that the inner type is a dataclass | ||
| if not is_dataclass(self._inner_type): | ||
| msg = f"{self._inner_type} is not a dataclass" | ||
| raise TypeError(msg) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Complete the Sequence detection fix by adding a guard for unsupported origins.
The Sequence-based detection on line 47 correctly handles list[Dataclass] and Sequence[Dataclass], but it also accepts unsupported Sequence types like tuple[Dataclass, ...]. Without an explicit guard, users will receive confusing "is not a dataclass" errors later instead of a clear TypeError at initialization.
Since this is an auto-generated file, apply the fix to the source file: key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py. After merging, regenerate the sync package.
Add this guard immediately after line 33 in the AIO source:
origin = get_origin(dataclass_type)
self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence)
+
+if origin is not None and not self._is_list_model:
+ msg = f"Unsupported generic type {dataclass_type!r}; only list[...] or Sequence[...] of dataclasses are supported"
+ raise TypeError(msg)
# Extract the inner type for list models
if self._is_list_model:Then add test coverage for rejection of tuple[Dataclass, ...] and other unsupported generic types.
🤖 Prompt for AI Agents
In key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py
(insert after line 33), add an explicit guard when detecting generic origins:
after calling get_origin(dataclass_type) and before treating it as a Sequence,
check whether the origin is one of the supported collection origins (e.g.,
built-in list or typing/collections.abc Sequence); if the origin is non-None and
not a supported origin, raise a clear TypeError stating the generic origin is
unsupported (mention examples like tuple[...]). This prevents later confusing
"is not a dataclass" errors; then mirror the same change in the sync generated
file and add tests that assert tuple[Dataclass, ...] and other unsupported
generic types are rejected at initialization.
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
♻️ Duplicate comments (5)
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (1)
12-14: Docstring wording fix looks correct.“async” wording removed; sync protocol description is now accurate and generator appears updated. No further action.
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (1)
64-69: Good: validation error message redacts input.Using e.errors(include_input=False) avoids leaking payload/PII and aligns with prior feedback. Keep this pattern.
key-value/key-value-aio/tests/adapters/test_pydantic.py (1)
94-101: Async list test fixed and aligned.Type annotation now matches fixture and usage; resolves prior pipeline/type-check failure.
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
46-48: Restrict list-model detection to list[...] only.Current issubclass(origin, Sequence) is too broad (matches tuple, deque, etc.). The adapter promises list[dataclass] support.
Apply this diff:
-from collections.abc import Sequence @@ - origin = get_origin(dataclass_type) - self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence) + origin = get_origin(dataclass_type) + self._is_list_model = origin is listkey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
46-62: Add guard to reject unsupported Sequence types (e.g., tuple).The current Sequence detection on line 47 accepts all Sequence subclasses, including
tuple[Dataclass, ...],deque[Dataclass], etc. Without an explicit guard, these unsupported types will pass initialization but cause unexpected behavior during serialization/deserialization, since the base adapter's list-model handling expects list-like semantics.Since this is auto-generated code, apply the fix to the source file:
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py. After merging, regenerate the sync package.Add this guard after line 33 in the AIO source (after the
_is_list_modelassignment):origin = get_origin(dataclass_type) self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence) + +# Reject unsupported generic types +if origin is not None and not self._is_list_model: + msg = f"Unsupported generic type {dataclass_type!r}; only list[...] or Sequence[...] of dataclasses are supported" + raise TypeError(msg) + +# Reject unsupported Sequence subclasses (e.g., tuple) +if self._is_list_model: + from collections.abc import Sequence as ABCSequence + if origin not in (list, ABCSequence): + msg = f"Unsupported Sequence type {dataclass_type!r}; only list[...] or Sequence[...] are supported, not {origin.__name__}" + raise TypeError(msg) # Extract the inner type for list models if self._is_list_model:Then add test coverage verifying that
tuple[Dataclass, ...],deque[Dataclass], and other unsupported generic/Sequence types are rejected at initialization.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (9)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py(1 hunks)key-value/key-value-aio/src/key_value/aio/protocols/key_value.py(1 hunks)key-value/key-value-aio/tests/adapters/test_pydantic.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py(0 hunks)key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py(1 hunks)
💤 Files with no reviewable changes (1)
- key-value/key-value-sync/src/key_value/sync/code_gen/stores/base.py
🧰 Additional context used
📓 Path-based instructions (3)
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python code must adhere to Ruff formatting and linting with a maximum line length of 140 characters
Use Basedpyright in strict mode for type checking
Use Beartype for runtime type checking (can be disabled via PY_KEY_VALUE_DISABLE_BEARTYPE=true)
Code must be compatible with Python 3.10+ (codegen targets 3.10)
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.pykey-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.pykey-value/key-value-aio/tests/adapters/test_pydantic.pykey-value/key-value-sync/tests/code_gen/adapters/test_pydantic.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/protocols/key_value.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py
key-value/key-value-sync/**
📄 CodeRabbit inference engine (AGENTS.md)
Never edit files in key-value/key-value-sync; the sync package is generated and changes will be overwritten
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.pykey-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.pykey-value/key-value-sync/tests/code_gen/adapters/test_pydantic.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py
key-value/key-value-aio/**
📄 CodeRabbit inference engine (AGENTS.md)
Async-first: Make all code changes in the async package (key-value/key-value-aio) and regenerate sync
Files:
key-value/key-value-aio/tests/adapters/test_pydantic.pykey-value/key-value-aio/src/key_value/aio/protocols/key_value.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py
🧠 Learnings (2)
📚 Learning: 2025-10-27T00:52:38.578Z
Learnt from: CR
PR: strawgate/py-key-value#0
File: AGENTS.md:0-0
Timestamp: 2025-10-27T00:52:38.578Z
Learning: Applies to key-value/key-value-sync/src/key_value/sync/code_gen/stores/** : Sync store implementations must conform to the KeyValue protocol (generated mirror) from key_value/sync/code_gen/protocols/key_value.py
Applied to files:
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py
📚 Learning: 2025-10-27T00:52:38.578Z
Learnt from: CR
PR: strawgate/py-key-value#0
File: AGENTS.md:0-0
Timestamp: 2025-10-27T00:52:38.578Z
Learning: Applies to key-value/key-value-aio/src/key_value/aio/stores/** : Async store implementations must conform to the AsyncKeyValue protocol from key_value/aio/protocols/key_value.py
Applied to files:
key-value/key-value-aio/src/key_value/aio/protocols/key_value.py
🧬 Code graph analysis (6)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (3)
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (10)
KeyValue(178-183)get(18-28)get_many(67-77)keys(134-141)put(43-53)ttl(30-41)put_many(92-105)delete(55-65)delete_many(107-117)ttl_many(79-90)key-value/key-value-shared/src/key_value/shared/errors/key_value.py (2)
DeserializationError(14-15)SerializationError(10-11)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
_get_model_type_name(45-47)
key-value/key-value-aio/tests/adapters/test_pydantic.py (3)
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py (3)
test_simple_adapter_with_list(97-103)product_list_adapter(71-72)Product(25-29)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
PydanticAdapter(14-47)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (5)
put(171-180)get(98-98)get(101-101)get(103-130)delete(192-196)
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py (3)
key-value/key-value-aio/tests/adapters/test_pydantic.py (3)
test_simple_adapter_with_list(94-100)product_list_adapter(68-69)Product(22-26)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
PydanticAdapter(17-47)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (5)
put(174-183)get(101-101)get(104-104)get(106-133)delete(195-199)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (2)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (2)
BasePydanticAdapter(18-243)_get_model_type_name(33-39)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (2)
DataclassAdapter(14-70)_get_model_type_name(68-70)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (2)
key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (1)
AsyncKeyValue(185-190)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (2)
BasePydanticAdapter(15-240)_get_model_type_name(30-36)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (4)
key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (10)
AsyncKeyValue(185-190)get(15-30)get_many(69-79)keys(141-148)put(45-55)ttl(32-43)put_many(94-112)delete(57-67)delete_many(114-124)ttl_many(81-92)key-value/key-value-shared/src/key_value/shared/errors/key_value.py (2)
DeserializationError(14-15)SerializationError(10-11)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
_get_model_type_name(68-70)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
_get_model_type_name(45-47)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (12)
- GitHub Check: test_quick (3.10, ubuntu-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, windows-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, windows-latest, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, windows-2022, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, macos-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, macos-latest, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, ubuntu-22.04, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, windows-2022, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, macos-14, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, macos-14, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, ubuntu-22.04, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, ubuntu-latest, key-value/key-value-sync)
🔇 Additional comments (11)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (1)
89-95: Serialization error path is precise.Catching PydanticSerializationError and mapping to SerializationError is appropriate; message doesn’t echo raw payloads. LGTM.
key-value/key-value-sync/tests/code_gen/adapters/test_pydantic.py (1)
97-104: List adapter test looks correct.Types align with fixture; validates put/get/delete for list[Product]. Matches storage “items” wrapper verified later in the file.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (2)
17-23: LGTM!The class docstring clearly documents that the adapter works with both standard library and Pydantic dataclasses using TypeAdapter for validation and serialization.
68-70: LGTM!The implementation correctly provides a descriptive model type name for error messages, following the pattern established by the base class.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (7)
18-39: LGTM!The abstract base class is well-designed with clear separation of concerns. The abstract method
_get_model_type_nameallows subclasses to customize error messages while the base provides all shared validation and serialization logic.
41-72: LGTM!The validation logic correctly handles both single and list models. Notably, line 69 properly prevents PII leakage by using
include_input=Falsewhen extracting validation error details, ensuring sensitive data isn't exposed in error messages or logs.
74-98: LGTM!The serialization logic correctly mirrors the validation logic, ensuring list models are wrapped in an
{"items": [...]}structure for consistent storage format. Error handling appropriately converts Pydantic serialization errors to the library's SerializationError type.
100-172: LGTM!Both
getandget_manymethods are well-implemented with proper type overloads for default value handling. The methods correctly validate retrieved data, handle missing keys gracefully, and respect theraise_on_validation_errorconfiguration. The batch operation preserves input order and length as documented.
174-193: LGTM!The put methods correctly serialize models before storage and properly support optional TTL. The batch operation maintains key-value alignment and will propagate SerializationError if any value fails to serialize.
195-205: LGTM!The delete methods correctly delegate to the underlying key-value store with appropriate collection handling. No validation or serialization is needed for deletion operations.
207-243: LGTM!The TTL methods correctly retrieve and validate models along with their expiration information. The batch operation on line 243 efficiently processes entries while properly handling missing keys and validation failures. Both methods respect the
raise_on_validation_errorconfiguration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
39-44: Restrict “list model” detection to list[…] and guard unsupported generics.Current check accepts tuple/Sequence origins; the contract and tests imply list[…] only. Add a guard to fail fast on unsupported generics.
Apply in aio source (then regenerate sync):
- origin = get_origin(pydantic_model) - self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence) + origin = get_origin(pydantic_model) + self._is_list_model = origin is list + if origin is not None and not self._is_list_model: + msg = f"Unsupported generic type {pydantic_model!r}; only list[...] of Pydantic models is supported" + raise TypeError(msg)
♻️ Duplicate comments (2)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
47-56: Restrict to list[…] and guard unsupported generics (align with docs).Current
issubclass(origin, Sequence)accepts tuple/Sequence; contract states list[dataclass]. Add a guard to error early.- origin = get_origin(dataclass_type) - self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence) + origin = get_origin(dataclass_type) + self._is_list_model = origin is list + if origin is not None and not self._is_list_model: + msg = f"Unsupported generic type {dataclass_type!r}; only list[...] of dataclasses is supported" + raise TypeError(msg)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
47-56: Fix list-model detection to accept onlylist; add unsupported-generic guard and tests.The issue is verified: both aio and sync adapters incorrectly accept any
Sequencesubclass (tuple, deque, etc.) when onlylist[dataclass]is supported per the docstring contract.Apply the fix to
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.pylines 47–56:- origin = get_origin(dataclass_type) - self._is_list_model = origin is not None and isinstance(origin, type) and issubclass(origin, Sequence) + origin = get_origin(dataclass_type) + self._is_list_model = origin is list + if origin is not None and not self._is_list_model: + msg = f"Unsupported generic type {dataclass_type!r}; only list[...] of dataclasses is supported" + raise TypeError(msg)Then regenerate the sync library. Add tests to
key-value/key-value-aio/tests/adapters/test_dataclass.pyto verifytuple[MyDc, ...]andSequence[MyDc]are rejected during initialization.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: ASSERTIVE
Plan: Pro
📒 Files selected for processing (5)
README.md(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py(3 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py(1 hunks)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py(3 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.md
📄 CodeRabbit inference engine (AGENTS.md)
**/*.md: Markdown must be linted with markdownlint as configured in .markdownlint.jsonc
Markdown line length should be 80 characters (excluding code blocks and tables)
Files:
README.md
**/*.py
📄 CodeRabbit inference engine (AGENTS.md)
**/*.py: Python code must adhere to Ruff formatting and linting with a maximum line length of 140 characters
Use Basedpyright in strict mode for type checking
Use Beartype for runtime type checking (can be disabled via PY_KEY_VALUE_DISABLE_BEARTYPE=true)
Code must be compatible with Python 3.10+ (codegen targets 3.10)
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py
key-value/key-value-sync/**
📄 CodeRabbit inference engine (AGENTS.md)
Never edit files in key-value/key-value-sync; the sync package is generated and changes will be overwritten
Files:
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.pykey-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py
key-value/key-value-aio/**
📄 CodeRabbit inference engine (AGENTS.md)
Async-first: Make all code changes in the async package (key-value/key-value-aio) and regenerate sync
Files:
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.pykey-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py
🧬 Code graph analysis (4)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (3)
key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (1)
KeyValue(178-183)key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (2)
BasePydanticAdapter(18-243)_get_model_type_name(33-39)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (2)
DataclassAdapter(14-71)_get_model_type_name(69-71)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (2)
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/base.py (2)
BasePydanticAdapter(18-243)_get_model_type_name(33-39)key-value/key-value-sync/src/key_value/sync/code_gen/protocols/key_value.py (1)
KeyValue(178-183)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (2)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (2)
BasePydanticAdapter(15-240)_get_model_type_name(30-36)key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (1)
AsyncKeyValue(185-190)
key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (2)
key-value/key-value-aio/src/key_value/aio/protocols/key_value.py (1)
AsyncKeyValue(185-190)key-value/key-value-aio/src/key_value/aio/adapters/pydantic/base.py (2)
BasePydanticAdapter(15-240)_get_model_type_name(30-36)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (11)
- GitHub Check: test_quick (3.10, windows-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, windows-latest, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, macos-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, macos-latest, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, ubuntu-latest, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, windows-2022, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, macos-14, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, windows-2022, key-value/key-value-sync)
- GitHub Check: test_quick (3.10, ubuntu-latest, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, ubuntu-22.04, key-value/key-value-aio)
- GitHub Check: test_quick (3.10, ubuntu-22.04, key-value/key-value-sync)
🔇 Additional comments (4)
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py (1)
20-26: Docstring already OK; no change required.Text matches behavior once list-only restriction is applied.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/dataclass/adapter.py (1)
60-67: Init-time dataclass validation looks good.Inner-type extraction and
is_dataclasscheck are correct.key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py (1)
60-67: Dataclass inner-type validation is solid.Clear TypeError with offending type included.
key-value/key-value-sync/src/key_value/sync/code_gen/adapters/pydantic/adapter.py (1)
39-44: Correct issue location: edit aio adapters, then regenerate sync.Do not edit the sync files; they are generated. Apply the restriction to the aio source adapters instead:
key-value/key-value-aio/src/key_value/aio/adapters/pydantic/adapter.py(line 40)key-value/key-value-aio/src/key_value/aio/adapters/dataclass/adapter.py(line 48)Replace the broad
issubclass(origin, Sequence)check to accept onlylisttype, then regenerate the sync adapters to apply the restriction there as well.
|


Summary
Implements a new DataclassAdapter that uses Pydantic's TypeAdapter for validation and serialization of standard library dataclasses.
Key Features
list[dataclass]typesraise_on_validation_errorflagTesting
Related Issue
Resolves #140
Generated with Claude Code
Summary by CodeRabbit
New Features
Behavior Changes
Tests
Docs