fix: instrument attrs classes in __init__ capture (crash fix + monkey-patch wrapper)#1860
fix: instrument attrs classes in __init__ capture (crash fix + monkey-patch wrapper)#1860
Conversation
…t to code_context_extractor - instrument_codeflash_capture: detect @attrs.define / @attr.s / etc. in the 'no explicit __init__' branch and return early, same as dataclass/NamedTuple. Prevents a TypeError caused by attrs(slots=True) creating a new class whose __class__ cell no longer matches the injected super().__init__ wrapper. - code_context_extractor: add _get_attrs_config() helper; update _collect_synthetic_constructor_type_names, _build_synthetic_init_stub, and _extract_synthetic_init_parameters to handle attrs field conventions (factory= keyword, init=False, kw_only). - tests: add 3 exact-output tests for instrumentation skip behaviour and 3 exact-output tests for attrs stub generation. Co-Authored-By: Oz <oz-agent@warp.dev>
Instead of skipping attrs classes entirely (previous approach), emit a
module-level patch block immediately after the class definition:
_codeflash_orig_ClassName_init = ClassName.__init__
def _codeflash_patched_ClassName_init(self, *args, **kwargs):
return _codeflash_orig_ClassName_init(self, *args, **kwargs)
ClassName.__init__ = codeflash_capture(...)(_codeflash_patched_ClassName_init)
This sidesteps the __class__ cell TypeError that attrs(slots=True) triggers
when a synthetic super().__init__() body is injected into the original class,
because the patched wrapper is a plain module-level function with no __class__
cell.
Changes:
- InitDecorator.__init__: add _attrs_classes_to_patch dict
- visit_ClassDef: for attrs classes, record (name -> decorator) instead of
returning immediately; set inserted_decorator=True
- visit_Module: splice patch block statements after each attrs ClassDef
- _build_attrs_patch_block: new helper that builds the 3-statement AST block
- Tests: rename *_no_init_skipped -> *_patched_via_module_wrapper and update
expected strings to assert the exact generated patch block
Co-Authored-By: Oz <oz-agent@warp.dev>
|
Claude finished @KRRT7's task in 9m 36s —— View job PR Review Summary
Prek Checks✅ MypyOne new type error introduced in This is the same class of error as the pre-existing one at line 244 ( Code Review1. Naming convention violation — 2. Cross-module import of private constants — 3. 4. 5. Logic correctness of 6. Duplicate Detection
Test Coverage
All 6 new tests pass. Gaps in Overall: The crash fix is correct and the approach (module-level monkey-patch instead of class-body injection) is sound. The main actionable issues are the naming convention (#1) and the cross-module constant import (#2). Items #3 and #4 are pre-existing limitation gaps worth tracking but not blockers for this PR. |
Co-authored-by: Kevin Turcios <undefined@users.noreply.github.com>
⚡️ Codeflash found optimizations for this PR📄 19,597% (195.97x) speedup for
|
The optimization pre-allocates reusable AST node fragments in `__init__` (such as `ast.Load()`, `ast.Store()`, `ast.Name(id="self")`, and `ast.Starred`) that previously were reconstructed on every call to `_build_attrs_patch_block`. Because AST nodes are immutable value objects that Python interns, referencing the same instances avoids repeated allocation overhead—profiler data shows lines constructing `ast.Name`, `ast.arg`, and `ast.Starred` nodes dropped from ~1–3 µs each to ~0.1–0.4 µs. Across 2868 invocations (per profiler), this yields the observed 40% runtime reduction from 22.7 ms to 16.2 ms with no correctness regressions.
⚡️ Codeflash found optimizations for this PR📄 40% (0.40x) speedup for
|
⚡️ Speed up method `InitDecorator._build_attrs_patch_block` by 40% in PR #1860 (`fix/attrs-init-instrumentation`)
|
This PR is now faster! 🚀 @KRRT7 accepted my optimizations from: |
⚡️ Codeflash found optimizations for this PR📄 33% (0.33x) speedup for
|
- Fix bug: skip attrs classes with init=False (no __init__ to patch) - Deduplicate attrs namespace/name sets into shared constants - Fix _get_attrs_config to resolve import aliases properly - Add test for init=False case with exact expected output
…ormat Co-Authored-By: Kevin Turcios <undefined@users.noreply.github.com> Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: codeflash-ai[bot] <148906541+codeflash-ai[bot]@users.noreply.github.com>
|
This PR is now faster! 🚀 Kevin Turcios accepted my code suggestion above. |
The optimization pre-parses the `codeflash_capture` import statement once in `__init__` and stores it in `self._import_stmt`, eliminating the repeated `ast.parse` call inside `visit_Module`. Line profiler confirms the original code spent ~186 µs (1% of runtime) parsing the import on every module visit (11 hits × 16.9 µs each), which is now reduced to a one-time ~8 µs insertion cost. This reduces total `visit_Module` time by ~2.6% (17.87 ms → 17.41 ms) with no correctness trade-offs, preserving all AST structure and behavior across diverse test scenarios including large modules with 100+ classes.
⚡️ Codeflash found optimizations for this PR📄 12% (0.12x) speedup for
|
⚡️ Codeflash found optimizations for this PR📄 10% (0.10x) speedup for
|
…2026-03-18T10.30.36 ⚡️ Speed up method `InitDecorator.visit_Module` by 12% in PR #1860 (`fix/attrs-init-instrumentation`)
|
This PR is now faster! 🚀 @claude[bot] accepted my optimizations from: |
Problem
When codeflash tried to optimize a method on an
@attrs.define(or@attr.s) class, it crashed with:Root cause:
attrs.define(slots=True)(the default) replaces the original class with a brand-new slots class at import time. The syntheticdef __init__(self, *args, **kwargs): super().__init__(...)that codeflash injects into the class body bakes a__class__cell pointing to the original class, butselfis already an instance of the new slots class — so thesuper()call explodes.Additionally, the test-generation context extractor had no knowledge of attrs field conventions, so it couldn't build correct
__init__stubs for attrs classes.Changes
1. Crash fix → module-level monkey-patch wrapper (
instrument_codeflash_capture.py)Instead of injecting a synthetic
__init__body into the class (which triggers the crash), we now emit a module-level patch block immediately after the class definition:The wrapper is a plain module-level function with no
__class__cell, so it's immune to the slot-class replacement. Covers@attrs.define,@attrs.define(frozen=True),@attrs.mutable,@attrs.frozen,@attr.s,@attr.attrs.2. Test-gen context: attrs support (
code_context_extractor.py)_get_attrs_config()helper (parallel to_get_dataclass_config)_collect_synthetic_constructor_type_names: attrs classes now included_build_synthetic_init_stub: generates correct stub withkw_onlysupport_extract_synthetic_init_parameters: handles attrsfactory=keyword (equivalent to dataclassdefault_factory=)3. Tests
6 new tests, all with exact expected-output assertions:
test_instrument_codeflash_capture.py— verify the monkey-patch block is emitted correctly for@attrs.define,@attrs.define(frozen=True), and@attr.stest_code_context_extractor.py— verify__init__stub generation for attrs classes (required fields,factory=defaults,init=False)Co-Authored-By: Oz oz-agent@warp.dev