Resolution and injection details

  • dependency.descriptor(...) resolves when the attribute is read, not when the object is created. Before the dependency manager exists, current clients raise an attribute error; after that, a missing service is a dependency-resolution failure.
  • dependency.instance(...) resolves immediately at the call site and should be treated separately from the complete feature behavior; resolution can also invoke a lazy creator.
  • replace_none_kwargs(...) injects only when the named keyword is absent or explicitly None. Use it for dependency parameters with a None default and pass those parameters by keyword or omit them.
  • If None is a meaningful caller-provided value, do not use replace_none_kwargs for that parameter.
  • Runtime service factories can be lazy. Importing an interface or defining a descriptor does not prove the concrete service has been constructed.
  • Do not retain a context-owned service past account, battle, app, or feature teardown. Re-resolve at the next lifecycle boundary instead of keeping stale globals.

Interfaces and implementations

An interface describes the contract used by callers. The concrete implementation can differ by context, mode, or client build. Find the registration site before assuming which implementation is active.

Illustrative. This authored fragment is not an extracted client caller. IVerifiedService is undefined: no registration, refresh implementation, signature, readiness condition or lifetime is supplied. Replace the placeholder interface and operation with verified APIs, and call only at the actual owner’s ready lifecycle boundary.

from helpers import dependency


def refresh_feature():
    service = dependency.instance(IVerifiedService)
    service.refresh()

Each call resolves the current dependency.instance and then the interface name. An undefined placeholder fails before lookup. Only after resolution returns does the function read and call service.refresh with no arguments. It retains no cached service and provides no configuration, readiness check, retry, fallback, subscription or cleanup.

In the current dependency implementation, an absent manager raises DependencyError. The manager retrieves the registered item’s value() inside a try block: a KeyError there, including one from a lazy creator, becomes DependencyError; other creator failures can escape. Lookup is not a passive availability test or an optional operation that always returns None when unavailable.

Resolution can construct a lazy service, with the creator marked as invoked before it runs. If it fails, later lookups need not retry and can return the still-empty None value. A registered object or successful creator result can also be None: registration validates the interface class, not whether the result implements refresh. Normal lookup return therefore does not prove that the attribute lookup or zero-argument call succeeds.

The fragment catches no failures and supplies no rollback for lookup or refresh side effects. An unexpected creator failure must remain diagnosable; catching every exception and returning None hides it as apparent absence. Treat missing registration as optional only under the verified feature’s own availability contract. A shipped adaptation needs its real interface, version attribution and traceback-bearing diagnostics for caught unexpected failures, while keeping optional reporting infrastructure optional.

refresh is called once only if the earlier steps succeed. Its result is discarded; refresh_feature implicitly returns None on normal completion. There is no awaiting, callback handling or receipt check: normal return establishes only that the immediate call returned, not synchronized data, finished background work or an accepted action. Those meanings require the real method contract. Later replacement cannot redirect the already resolved local object; its owner still defines lifetime and teardown. This is source/control-flow inspection, not execution of this fragment, a service factory or the game client.

Optional infrastructure provenance

An importable API may belong to optional mod infrastructure rather than the base client. Establish provenance before treating a package as a World of Tanks service, and keep the feature that uses it behind a narrow availability gate.

Readiness is system-specific

  • A lobby controller may require account GUI readiness.
  • An inventory-facing service may be meaningless before cache synchronization.
  • A battle controller may not exist until the battle GUI is constructed.
  • A mode service may be registered only while its feature package is active.
  • A view-scoped service may disappear when its window closes.

Service availability and operation completion are different gates

A registered service, its availability flag, a completed login continuation and a current credential are separate facts. In the inspected IWebController implementation, AvailableState.isAvailable() is true independently of login state. Its loginAsync wrapper reports true after the inner login continuation; the outer WebController.login also reports true without forwarding the state's boolean result. That completion value alone does not establish authentication success.

Check the service's current login state and the returned data needed for the operation. Token retrieval uses a retained payload and expiry; a non-null payload is not by itself proof of a successful fresh refresh. Carry the original account/state generation across asynchronous work, and reject stale results independently of whether the service object still exists. Local freshness does not prove server acceptance.

These are version-sensitive Python contracts from web service states and the controller's forwarding methods in the latest public source. They establish caller-side state and completion semantics, not a live authentication failure or permission to reuse credentials across accounts.

Descriptor cautions

A descriptor on a module-global singleton can look convenient, but the first attribute access may occur during import or in the wrong context. Prefer a small accessor when availability is expected to change.

Configuration and lazy services

The current client builds one process-level dependency manager, then composes root client, helper, GUI, controller, and optional feature configurations into it. Adding a configuration function is eager composition. A runtime service is the lazy part: its creator runs on first lookup and a successful result is retained.

  • A lazy creator is marked invoked before it runs. If it raises, later lookups do not retry it automatically and can resolve to None.
  • Creation order is assigned after successful lazy creation. Shutdown snapshots all registered item objects, including uncreated lazy entries; see the shutdown boundaries below.
  • A descriptor resolves through the current manager on every access, but storing the returned object defeats replacement awareness.
  • Container configuration is process-scoped. Account, avatar, feature, and view readiness are separate lifecycles layered on top.