Skip to content

Commit 94aa172

Browse files
committed
feat: Add rules and more Documentation
1 parent 63dadf8 commit 94aa172

4 files changed

Lines changed: 1792 additions & 0 deletions

File tree

.cursorrules

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Cursor Rules for SQLAlchemy-serializer
2+
3+
## Project Context
4+
This is a SQLAlchemy serialization library that provides a mixin for easy model serialization.
5+
6+
**Project Structure:**
7+
- Main package: `sqlalchemy_serializer/`
8+
- Core: `serializer.py` (SerializerMixin, Serializer classes)
9+
- Library: `lib/schema.py` (Schema/Tree/Rule), `lib/fields.py` (field extraction), `lib/serializable/` (type serializers)
10+
- Tests: `tests/` directory mirrors source structure
11+
12+
## Core Development Principles
13+
14+
**Code Quality & Standards**
15+
16+
Python code must follow PEP 8 strictly. Use type hints on all functions and class methods. Prefer explicit over implicit. Enforce minimal complexity—keep functions under 20 lines when feasible. Use descriptive variable names that reflect intent.
17+
18+
**Architecture Principles**
19+
- Beautiful is better than ugly
20+
- Explicit is better than implicit
21+
- Simple is better than complex
22+
- Complex is better than complicated
23+
- Flat is better than nested
24+
- Sparse is better than dense
25+
- Keep functions small and elegant
26+
- Readability counts
27+
28+
**Testing Requirements**
29+
30+
Write tests before or immediately after implementation. Use pytest for unit tests and pytest-cov for coverage tracking. Maintain minimum 80% code coverage on all modules. Test both success and failure paths. Mock external dependencies consistently.
31+
32+
**Project Testing Setup:**
33+
- Run tests: `make test` (uses docker-compose)
34+
- Run specific test: `make test file=tests/path/to/test.py`
35+
- Test configuration: `pytest.ini_options` in `pyproject.toml` (verbose, color, log_cli)
36+
- Coverage: `--cov=sqlalchemy_serializer --cov-report term-missing`
37+
- Database: PostgreSQL container via docker-compose (session-scoped fixture)
38+
- Test models: Defined in `tests/models.py` (FlatModel, NestedModel, RecursiveModel, CustomSerializerModel)
39+
40+
## Project-Specific Rules
41+
42+
**File Organization**
43+
44+
Keep modules focused—one responsibility per file. Create a `tests/` directory mirroring source structure. Store configuration in environment variables or dedicated config modules. Never commit `.env` files or secrets.
45+
46+
**Project Structure:**
47+
- `sqlalchemy_serializer/serializer.py` - Core SerializerMixin and Serializer classes
48+
- `sqlalchemy_serializer/lib/schema.py` - Schema tree for rule handling (greedy/strict modes)
49+
- `sqlalchemy_serializer/lib/fields.py` - Field extraction utilities (SQL fields, properties)
50+
- `sqlalchemy_serializer/lib/serializable/` - Type-specific serializers (datetime, date, time, decimal, enum, uuid, bytes)
51+
- Each serializable type has its own module following the Base class pattern
52+
53+
**Dependencies & Imports**
54+
55+
Pin exact versions in `pyproject.toml`. Use virtual environments exclusively—no global installs. Review dependency licenses. Import standard library first, then third-party, then local modules. Remove unused imports automatically.
56+
57+
**Error Handling & Logging**
58+
59+
Never use bare `except:`. Log at appropriate levels (DEBUG, INFO, WARNING, ERROR). Include context in error messages. Use custom exceptions for domain-specific errors. Validate inputs at function entry points.
60+
61+
**Project-Specific:**
62+
- Logger: `logging.getLogger("serializer")` with WARN level by default
63+
- Custom exception: `IsNotSerializable` for unserializable types
64+
- Iterable serialization swallows `IsNotSerializable` exceptions (see FIXME in code)
65+
- Use debug logging for serialization flow: `logger.debug("Serialize key:%s type:%s", ...)`
66+
67+
## Code Practices
68+
69+
**Performance & Maintenance**
70+
71+
Avoid mutable default arguments. Use generators for large datasets. Prefer built-in functions over loops. Document non-obvious logic inline. Keep functions pure when possible. Use pathlib for file operations instead of os.path.
72+
73+
**Database & Queries**
74+
75+
Use parameterized queries exclusively—no string concatenation. Leverage ORM features (SQLAlchemy) for common operations. Index frequently-queried columns. Document query intent with comments. Use database transactions appropriately.
76+
77+
**SQLAlchemy-Specific:**
78+
- Use `sqlalchemy.inspect()` for model introspection (get mapper attributes)
79+
- Access model fields via `getattr(model, key)` after validation
80+
- Support SQLAlchemy 2.0+ patterns (declarative_base, relationship)
81+
- Handle relationships automatically through SerializerMixin
82+
- Test models use PostgreSQL with docker-compose for integration testing
83+
84+
**AI Assistance Configuration**
85+
86+
Provide complete file context using `@` mentions before multi-file refactors. Keep conversations focused—start fresh if conversation exceeds 15 exchanges. Use Agent mode for complex multi-step changes. Reference commit history for reverting problematic changes quickly.
87+
88+
## Workflow Rules
89+
90+
**Version Control**
91+
92+
Make atomic commits with clear messages. Keep working directory clean—commit before AI changes. Use branches for features and fixes. Squash commits before merging. Document rationale in PR descriptions, not just code.
93+
94+
**Review & Validation**
95+
96+
Review all AI-generated code—especially complex logic and security-sensitive code. Run full test suite locally before pushing. Use linting tools (ruff, pylint) on every change. Verify imports resolve correctly. Check for accidental debugging code or temporary variables.
97+
98+
## Common Patterns
99+
100+
**Serializer Architecture:**
101+
- Callback chain pattern: `serialize_types` tuple checked in order (most specific first)
102+
- Fork pattern: Create new Serializer instances for nested structures (dicts, iterables, models)
103+
- Schema tree: Tree structure for nested rule handling with greedy/strict modes
104+
- Options pattern: Use `namedtuple` (Options) for configuration passing
105+
106+
**Type Serialization:**
107+
- All serializable types inherit from `lib.serializable.base.Base`
108+
- Implement `__call__` method that takes value and returns serialized form
109+
- Order matters: atomic types first, then specific types (time before datetime), then generic (dict before Iterable)
110+
- Custom types: Add to `serialize_types` tuple as `(Type, callable)` pairs
111+
112+
**Schema & Rules:**
113+
- Greedy mode (default): Include all fields unless excluded
114+
- Strict mode (`serialize_only`): Include only specified fields
115+
- Nested rules: Use dot notation (`'relation.field'`)
116+
- Negative rules: Prefix with `-` (`'-field'`, `'-relation.field'`)
117+
- Schema forks for nested structures maintain parent context
118+
119+
**Model Mixin:**
120+
- `SerializerMixin` adds `to_dict()` method to SQLAlchemy models
121+
- Class attributes: `serialize_only`, `serialize_rules`, `serialize_types`, `serializable_keys`
122+
- Format attributes: `date_format`, `datetime_format`, `time_format`, `decimal_format`
123+
- Override `get_tzinfo()` for timezone-aware serialization
124+
- Set `auto_serialize_properties=True` to include `@property` fields automatically
125+
126+
**Testing Patterns:**
127+
- Use `conftest.py` fixtures: `session` (database), `get_instance`, `get_serializer`
128+
- Test files mirror source structure: `test_*.py` for modules, `test_*_function.py` for specific functions
129+
- Integration tests use PostgreSQL via docker-compose
130+
- Test both greedy and strict modes, nested structures, recursive models
131+
- Never delete or change already committed tests. Treat old tests like immutable legacy.
132+
133+
## Known Issues & Workarounds
134+
135+
**Known Issues:**
136+
1. Iterable serialization swallows `IsNotSerializable` exceptions (see FIXME in `serialize_iter`)
137+
2. Schema checks can be optimized: TODO comments about skipping checks when not greedy
138+
3. Custom serializers cannot access format or tzinfo (documented limitation in README)
139+
140+
**Common Pitfalls:**
141+
- One-element tuples must have trailing comma: `serialize_only = ('field',)` not `('field')`
142+
- Recursive models need explicit exclusion rules to prevent max recursion: `serialize_rules = ('-relation.backref',)`
143+
- Controversial rules: `('-prop', 'prop.id')` will include `prop` despite negative rule
144+
- Negative rules in `serialize_only`: `serialize_only = ('-model.id',)` returns nothing (must include parent: `('model', '-model.id')`)
145+
146+
**Performance Considerations:**
147+
- `get_serializable_keys` uses `@functools.lru_cache` for field name caching
148+
- Schema tree operations are O(n) where n is rule depth
149+
- Fork creates new Serializer instances (intentional for isolation)
150+
- Type checking uses `isinstance()` with tuple of types for efficiency
151+

0 commit comments

Comments
 (0)