Skip to content

refactor(sync): inline make_default_block_processor as method#731

Merged
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/inline-default-block-processor
May 18, 2026
Merged

refactor(sync): inline make_default_block_processor as method#731
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/inline-default-block-processor

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Summary

The make_default_block_processor(spec) factory existed only to
capture spec in a closure for the SyncService.process_block
field. SyncService already has self.spec, so the closure is
unnecessary indirection. Inlines the body as
_default_process_block(self, store, block) and binds it as the
default in __post_init__.

Net: -8 lines, zero behavior change, no test updates needed.

What changed

service.py

Removed the module-level make_default_block_processor factory
(~32 lines with docstring).

Added _default_process_block method on SyncService (~22
lines, same body). Reads self.spec directly instead of a
captured closure variable.

Updated __post_init__:

# before
self.process_block = make_default_block_processor(self.spec)

# after
self.process_block = self._default_process_block

What stayed the same

  • The process_block: Callable | None field stays. It is a
    real test-injection point used by two paths in the test suite:
    • create_mock_sync_service passes
      lambda s, b: s.on_block(b) so tests run against
      MockForkchoiceStore.on_block rather than a real spec.
    • _persist_process_block(...) in TestBlockPersistence
      substitutes a custom processor that exercises the
      database-persistence wrapper without needing a real spec or
      state transition.
  • Behavior is identical. Same metrics emitted, same reorg-depth
    computation, same _process_block_wrapper flow.
  • No test changes — the injection contract is unchanged.

Correction on the original estimate

The earlier scan claimed "every call site uses the default" — that
was wrong. Tests genuinely override process_block. The factory
itself is still worth removing (a closure capturing self.spec when
the method has self is pure indirection), but the metrics body has
to live somewhere, so savings are modest (~8 lines instead of ~30).

The architectural win is small but real: _default_process_block
and _process_block_wrapper now sit next to each other on
SyncService, both reading self.spec and self.database. One
indirection layer fewer for a reader tracing the block-processing
path.

Test plan

  • ruff check, ruff format --check, ty check all pass
  • pytest tests/lean_spec/subspecs/sync -> 156 passed

Out of scope

The last queued item from the sync-layer review:

  • SyncService marketing-tone docstring rewrite (Reactive/Simple/Resilient/Observable)

🤖 Generated with Claude Code

The factory existed only to capture `spec` in a closure for the
SyncService.process_block field. SyncService already has self.spec,
so the closure is unnecessary indirection. Inlines the body as
_default_process_block(self, store, block) and binds it as the
default in __post_init__.

The process_block field stays as a legitimate test-injection point.
Tests use it to substitute mock processors that exercise the
metrics and database-persistence wrapper without requiring a full
spec or state transition.

Net: -8 lines, zero behavior change, no test updates needed.
A reader of SyncService now finds the block-processing logic as a
method on the class instead of a module-level factory closure.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 08868e7 into leanEthereum:main May 18, 2026
13 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant