Files
MyUtils/CLAUDE.md

5.5 KiB

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

# 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

# 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 _