Hadron / Hadron/carthage

Emit lifecycle events when async_become_ready() follows not-ready instantiation

Open
#147 0 comments 0 reactions 1 assignee Claimed by @hartmans View on GitHub
Dominant language
Python
Stars
11
Forks
8
Avg merge
9h 26m
Merged PRs (30d)
6

Description

## Summary

When an `AsyncInjectable` is first instantiated with a not-ready key and later brought to ready with `async_become_ready()`, the second transition does not emit an injector lifecycle event. This leaves event-driven instrumentation—particularly `carthage.entanglement.instrumentation`—without a notification that the provider has moved from `not_ready` to `ready`.

## Current behavior

A typical sequence is:

1. Resolve `InjectionKey(SomeType, _ready=False)`.
2. The original `InstantiationContext` emits `dependency_final`; entanglement records the provider as `InstantiationProgress.not_ready`.
3. Later, call `obj.async_become_ready()` directly.
4. `AsyncInjectable.async_become_ready()` enters an `AsyncBecomeReadyContext`, runs the dependency/ready protocol, and reaches `ReadyState.READY`.
5. When this is a root-level call, `AsyncBecomeReadyContext` has no parent to notify and does not emit `dependency_progress` or `dependency_final` through the injector.

`CarthageRegistry.instrument_injector()` listens for `dependency_progress` and `dependency_final` to update `ProviderInfo` and invoke registered entanglement instrumentation callbacks. Because neither event is emitted for this path, the synchronized state can remain `not_ready` indefinitely.

## Expected behavior

An observable ready transition initiated by `async_become_ready()` should produce the same useful lifecycle notification as a ready transition performed during dependency instantiation:

- instrumentation can observe that work has started;
- successful completion publishes the provider as ready;
- the event is dispatched to the provider's applicable injection keys and the `InjectionKey(Injector)` wildcard;
- callbacks receive an inspector/context from which the existing provider identity and current value can be recovered.

The implementation should avoid duplicate terminal events when `async_become_ready()` is already nested inside an `InstantiationContext`, and concurrent callers waiting on the same `_ready_future` should not report multiple independent transitions.

## Suggested regression test

1. Register an `AsyncInjectable` whose `async_ready()` is nontrivial.
2. Instantiate it using `InjectionKey(Type, _ready=False)`.
3. Attach listeners for `dependency_progress` and `dependency_final` (or instrument a `CarthageRegistry`).
4. Call `obj.async_become_ready()` outside the original instantiation context.
5. Assert that the ready transition is observed and `ProviderInfo.state` becomes `InstantiationProgress.ready`.
6. Also cover the normal ready-at-instantiation path and two concurrent `async_become_ready()` callers to ensure events are not duplicated.

Failure/cancellation should leave the object in its existing retryable state and should not publish a false ready state.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.