Added ProxyObject + CLAUDE.md

This commit is contained in:
2025-12-03 21:20:20 +01:00
parent 0ef9523e6b
commit d8b1ef2e2c
5 changed files with 628 additions and 6 deletions

154
CLAUDE.md Normal file
View File

@@ -0,0 +1,154 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project Overview
MyUtils is a Python utility library providing dynamic object manipulation capabilities. The library focuses on three core utilities: Observable (attribute change tracking), Expando (dynamic dictionary wrapper), and Dummy (null-safe mock object).
## Development Workflow & Collaboration Guidelines
### Developer Profile
You are an experienced Python developer producing Python 3.12 code.
### Development Process
- All proposed code must be testable
- **Before writing any code**, explain the possible implementation options
- Wait for mutual validation and understanding of requirements before producing code
### Collaboration Style
- Ask clarifying questions to refine understanding or suggest alternative approaches
- **Ask questions one at a time** - wait for complete understanding of the answer before asking the next
- When you have multiple questions, indicate progress (e.g., "Question 1/5")
### Communication
- Conversations can be in French or English
- **All code, documentation, and comments must be exclusively in English**
### Code Standards
- Follow PEP 8 conventions for Python code style
- Use Google or NumPy format for docstrings
- Variable and function names must be explicit and in snake_case
- Never use emojis in code
### Dependency Management
- List all external dependencies required for installation
- When possible, propose alternatives using only Python standard library
### Unit Testing
- Use pytest library for unit tests
- **Before writing tests**, list the tests you plan to implement with explanations of why they're important
- Wait for validation before implementing tests
- Unless explicitly needed (e.g., inheritance), write tests as functions, not classes
- Test function naming patterns:
- `test_i_can_xxx` for tests that should pass
- `test_i_cannot_xxx` for edge cases that should raise errors/exceptions
### File Management
- When adding or modifying a file, always provide the complete file path
### Error Handling
- When presented with a code execution error:
1. First provide a clear explanation of the problem
2. **Do not** immediately propose a new version
3. Wait for validation of the bug analysis before proposing solutions
## Development Commands
### Testing
```bash
# Run all tests
pytest
# Run specific test file
pytest tests/test_observable.py
pytest tests/test_expando.py
pytest tests/test_dummy.py
# Run with verbose output
pytest -v
# Run specific test
pytest tests/test_observable.py::test_i_can_make_an_object_observable
```
### Package Management
```bash
# Install development dependencies
pip install -r requirements.txt
# Install package in development mode
pip install -e .
# Install with development extras
pip install -e ".[dev]"
```
## Architecture
### Observable Pattern Implementation
The Observable module uses **dynamic class substitution** to add observability to any Python object:
1. `make_observable(obj)` replaces the object's class with a dynamically created subclass
2. The subclass overrides `__setattr__` to intercept attribute changes
3. Listeners are stored in a `_listeners` dict on the object instance
4. Two types of callbacks exist:
- **Attribute callbacks**: Triggered on specific attribute changes via `bind()`
- **Event listeners**: Triggered via `add_event_listener()` for events like `AFTER_PROPERTY_CHANGE`
#### Event System
- `AFTER_PROPERTY_CHANGE` event fires after all attribute callbacks execute
- Event listeners receive: `(attr_name, old_value, new_value, callback_results)`
- Empty string `""` as attr_name registers listener for ALL attributes
- Return values from attribute callbacks are collected and passed to event listeners
### Expando Pattern
Expando wraps dictionaries to enable dot-notation access:
- Uses `__getattr__` to intercept attribute access and delegate to internal dict
- Nested dicts are automatically wrapped in Expando instances
- `get(path)` method supports path notation (`"a.b.c"`) and list traversal
- When path encounters a list, it collects values from all list items
### ProxyObject
ProxyObject (currently in development) maps object attributes to a different structure using a mappings dictionary. Note: Implementation appears incomplete - `_create_props()` is defined but never called.
## Testing Conventions
- Test files use pytest fixtures (`data`, `observable_data`)
- Test names follow pattern: `test_i_can_<action>` or `test_<behavior>`
- Tests are organized by feature with separator comments
- Each test is self-contained with its own callbacks/data
## Package Structure
```
src/myutils/ # Main package code
├── __init__.py # Package exports (currently empty)
├── observable.py # Observable implementation
├── Expando.py # Expando wrapper
├── Dummy.py # Dummy mock object
└── ProxyObject.py # ProxyObject (incomplete)
tests/ # Test suite
├── test_observable.py
├── test_expando.py
└── test_dummy.py
```
## Important Patterns
### Observable Callback Semantics
- Callbacks must be the **same object** to unbind (not just equivalent lambdas)
- `unbind()` and `unbind_all()` fail silently if callback/attribute doesn't exist
- Multiple instances have independent listeners
- `make_observable()` is idempotent (safe to call multiple times)
### Naming Convention
- Use English for persona names (per global CLAUDE.md)
- Class names use PascalCase
- Functions use snake_case
- Private attributes prefixed with `_`