ARCHIVED: This technical design was archived on 2025-12-22 when the reflection module was removed from the codebase. The DI module now uses Python standard library introspection (
inspect,typing.get_type_hints()) instead of the custom reflection system.
📘 Technical Design 027 — ReflectionContext API Simplification¶
| Field | Value |
|---|---|
| ID | TD-027 |
| Title | ReflectionContext API Simplification |
| Status | Implemented |
| Owner | Reflection maintainers |
| Audience | Reflection and DI contributors, documentation team, test maintainers |
| Created | 2025-10-22 |
| Last Updated | 2025-10-24 |
| Reviewers | TBD |
| Approvers | TBD |
| Tracking | Roadmap 002 • Phase A (API Simplification) |
| Related | Reflection Modernization Roadmap, Reflection Architecture |
1. 🧭 Summary¶
ReflectionContext currently exposes six facade objects (resolve, scan, hints, annotations, invoke, create) that together surface sixteen callable entry points. The indirection obscures the public contract, complicates documentation, and makes it difficult to ensure tests cover the intended workflows. This design collapses the facade layer and promotes the supported operations to first-class ReflectionContext methods with explicit names (resolve_module, scan_package, invoke, construct, etc.), paired with refreshed documentation and test coverage for the canonical use cases. While we remove the facade layer we also collapse the constructor seams: ReflectionContext instantiates its own scanner, type resolver, invoker, instance factory, type hints, annotations, and type checker—only cache, metrics, and the optional TypeHintConfig remain constructor parameters.
The change reduces accidental API surface, clarifies how strict mode is applied, and makes it trivial for downstream code (DI runtime, fixtures, user guides) to discover supported behaviors. Because the project remains in alpha, the plan removes the facades without a transitional alias while providing migration guidance and automated tests that lock in the captured workflows. The plan retires or reworks tests that previously exercised configurability through builder overrides so they rely on component-level fixtures. As of Phase 2A, the constructor directly owns all core collaborators; scan_types remains available as a compatibility helper for existing callers and is now complemented by dedicated TypeInfo utilities.
At a glance:
- Replace facade properties with explicit
ReflectionContextmethods and update the globalreflectionsingleton accordingly. - Internalize collaborator construction inside
ReflectionContext(scanner, resolver, invoker, instance factory, type hints, annotations, type checker) while keeping cache, metrics, and type-hint configuration configurable. - Document and test the core workflows (resolution, scanning, type hints, annotations, invocation, instance creation) against the new API.
- Produce contributor-facing guidance and changelog entries so downstream consumers can migrate confidently; the plan removes or reworks builder-oriented tests in favor of component fixtures.
- Introduce dedicated, public TypeInfo utilities so contributors can enumerate metadata without relying on facade internals.
scan_typesremains available for compatibility but is no longer required by project tests.
2. 📚 Context and Problem Statement¶
ReflectionContext is designed with nested facade classes to group related operations. Those facades continue to expand organically, now providing sixteen total callable methods. The grouping no longer aligns with how contributors reason about the API:
- Facade boundaries (e.g.,
resolvevs.scan) duplicate responsibilities—developers must remember whether dotted-path resolution is available viaresolve.dottedorscan.dotted. - Documentation and architecture references list the facade names, but downstream code (tests, DI runtime) often requires deeper knowledge of the hierarchy.
- Each facade creates a new object per context instance, increasing boilerplate and subtly complicating strict-mode handling.
Roadmap 002 Phase A calls for narrowing the public surface and documenting supported workflows. Removing facades in favor of named methods directly on ReflectionContext accomplishes this while keeping the underlying provider wiring intact.
2.1 Goals¶
- Promote the supported reflection operations to explicit, well-described methods on
ReflectionContext. - Ensure every described use case (resolution, scanning, type hints, annotations, invocation, instance creation) has dedicated integration coverage that exercises the new methods.
- Provide migration guidance for downstream consumers, including DI runtime modules, fixtures, and user guides.
- Simplify testing by replacing builder-driven configurability suites with focused component fixtures or removing redundant coverage.
2.2 Out of Scope¶
- Metrics redesign (captured in Phase B of the roadmap).
- Reworking provider seams (annotations, type hints, parsing, invocation) beyond changing the call sites.
- Type checker redesign or exposure changes (Phase F).
- Performance tuning beyond the minimal improvements gained from removing facade objects.
2.3 Assumptions and Constraints¶
- The project remains in alpha; breaking API changes can ship without a deprecation period, provided migration steps remain clearly described.
- Downstream consumers rely on the behaviors covered by existing integration tests (
tests/integration/reflection/test_context_api.py,test_context_ergonomics.py, etc.), which this plan updates as part of the work. - We must maintain existing strict-mode semantics (
strictdefaults toTrueunless overridden) and cache behavior. - The global
reflectionsingleton inloryth.common.reflection.__init__must use the new API without exposing the old facades.
3. 🧩 Design Overview¶
Phase A removes the facade layer and exposes a curated set of methods on ReflectionContext. Each method name clearly signals the operation it performs (resolve_module, scan_package, hints_for_callable, invoke, construct, etc.). Internally, the context constructs its own collaborators—package scanner, runtime type resolver, callable invoker, instance factory, type-hint provider, annotation provider, and type checker—leaving only cache and metrics as optional constructor inputs. Tests that previously relied on bespoke injections through the builder convert to component-level fixtures or retire when redundant.
The migration unfolds in staged increments:
- Introduce explicit methods that wrap the existing provider interactions, mirroring the logic currently living inside the facade classes.
- Internalize collaborator construction and eliminate the seams from
ReflectionContext.__init__, while providing targeted fixtures for tests that still need custom behavior. - Delete facade classes and properties, ensuring no dormant API remnants remain.
- Update documentation and tests to reference the new methods, adding a contributor guide section that lists the supported workflows and expectations.
The result is a discoverable, self-contained API surface that matches the architecture reference and accelerates upcoming metrics and seam rationalization work.
3.1 Key Components¶
| Component | Purpose |
|---|---|
ReflectionContext | Hosts the new first-class methods for resolution, scanning, metadata retrieval, and invocation. |
| Internal collaborator factories | Default constructors for scanner, invoker, instance factory, type resolver, type hints, annotations, and type checker sit under ReflectionContext control. |
Test fixtures (tests/integration/reflection/test_context_api.py, component-specific fixtures) | Exercise the renamed APIs and validate supported use cases without relying on builder overrides. |
Documentation (reflection-architecture.md, roadmap phase A section) | Receives updates outlining the new public surface and migration steps. |
3.2 Architecture¶
graph TD
RC[ReflectionContext] --> IR[ImportResolver]
RC --> TH[TypeHintProvider]
RC --> AP[AnnotationProvider]
RC -.internal.-> SC[PackageScanner]
RC -.internal.-> TR[RuntimeTypeResolver]
RC -.internal.-> INV[SafeCallableInvoker]
RC -.internal.-> IF[TypeInstanceFactory]
RC -.internal.-> TC[TypeChecker]
classDef primary fill:none,stroke:#1976D2,stroke-width:2px,color:#1976D2;
class RC primary; Solid edges denote externally swappable dependencies (import resolver, type-hint provider, annotation provider). Dashed edges represent internally constructed components that are no longer passed through the constructor.
3.3 Interface Impact (Before → After)¶
| Area | Before (legacy state) | After (use instead) |
|---|---|---|
| Module resolution | ctx.resolve.module("pkg.mod") | ctx.resolve_module("pkg.mod") |
| Package resolution | ctx.resolve.package("pkg") | ctx.resolve_package("pkg") |
| Type resolution | ctx.resolve.type("pkg.Type") | ctx.resolve_type("pkg.Type") |
| Function resolution | ctx.resolve.function("pkg.fn") | ctx.resolve_function("pkg.fn") |
| Dotted lookup (strict) | ctx.resolve.dotted("pkg.symbol", strict=True) | ctx.resolve("pkg.symbol", strict=True) |
| Package scanning | ctx.scan.package("pkg", recursive=True) | ctx.scan_package("pkg", recursive=True) |
| Module scanning | ctx.scan.module("pkg.mod") | ctx.scan_module("pkg.mod") |
| Type scanning | ctx.scan.types([Type]) | Removed — prefer TypeInfo utilities |
| Dotted scanning | ctx.scan.dotted("pkg") | Use resolve(...) + scan_package/scan_module |
| Callable hints | ctx.hints.for_callable(callable_info) | ctx.hints_for_callable(callable_info) |
| Type hints | ctx.hints.for_type(type_info) | ctx.hints_for_type(type_info) |
| Field hints | ctx.hints.for_field(field_info) | ctx.hints_for_field(field_info) |
| Property hints | ctx.hints.for_property(prop_info) | ctx.hints_for_property(prop_info) |
| Annotation retrieval | ctx.annotations.for_member(member_info) | ctx.annotations(member_info) |
| Callable invocation | ctx.invoke(callable_info, *args, **kwargs) or ctx.invoke.call(...) | ctx.invoke(callable_info, *args, strict=None, **kwargs) |
| Constructor invocation | ctx.invoke.construct(type_info, *args, **kwargs) | ctx.construct(type_info, *args, strict=None, **kwargs) |
| Instance creation | ctx.create.instance(type_info, *args, **kwargs) | ctx.create_instance(type_info, *args, strict=None, **kwargs) |
3.4 Removal/Deprecation Policy¶
- Alpha removal: The plan removes facade classes and properties immediately with no deprecation window.
- Migration support: Documentation, changelog entries, and test updates provide clear guidance. Any external modules in this repository (DI bootstrap, fixtures) will be migrated in the same PR.
- Future compatibility: Downstream packages outside the repo must adopt the new method names; no shim layer will be provided.
4. 📐 Implementation Details¶
4.1 Resolution API¶
ReflectionContext will surface direct resolution helpers while preserving strict-mode semantics. Callers can pass dotted strings or native Python objects where indicated.
class ReflectionContext:
def resolve_module(self, module_path: str) -> ModuleInfo: ...
def resolve_package(self, package_path: str) -> PackageInfo: ...
def resolve_type(self, target: str | type[Any]) -> TypeInfo: ...
def resolve_function(self, dotted_path: str) -> FunctionInfo: ...
def resolve(self, path: str, *, strict: bool | None = None) -> MemberInfo | None: ...
Key behaviors:
resolvereplacesresolve.dotted; strict defaults continue to follow_effective_strict.resolve_typeaccepts either a dotted path or a concretetype[Any], returning the cachedTypeInfo.
4.2 Scanning API¶
Scanning entry points remain focused on packages and modules, accepting either dotted strings or pre-built MemberInfo instances. Dotted scanning use cases are satisfied by calling resolve followed by the appropriate scan. The legacy scan_types helper continues to ship temporarily while we introduce focused TypeInfo utilities and migrate downstream callers; its removal is tracked under the Remaining Work section and earmarked for Phase 2B.
class ReflectionContext:
def scan_package(
self,
package: str | PackageInfo,
*,
recursive: bool = True,
public_only: bool = True,
kinds: set[str] | None = None,
name_contains: str | None = None,
) -> Iterable[MemberInfo]: ...
def scan_module(
self,
module: str | ModuleInfo,
*,
public_only: bool = True,
kinds: set[str] | None = None,
name_contains: str | None = None,
) -> Iterable[MemberInfo]: ...
The design ultimately drops scan_types and scan_dotted. During the transition, contributors should:
- Prefer
scan_module/scan_packagefor module-level enumeration (accepting strings orMemberInfoinstances, so dotted inputs remain ergonomic) and treatscan_typesas a compatibility shim slated for removal. - Introduce lightweight utilities in
loryth.common.reflection(outsideReflectionContext) to extract common views fromTypeInfo(e.g.,iter_methods(type_info)), keeping reflection context focused on discovery rather than convenience iteration once we retirescan_types.
4.3 Metadata API (Type Hints & Annotations)¶
Metadata retrieval remains accessible but is renamed for clarity.
class ReflectionContext:
def hints_for_callable(self, callable_info: CallableInfo) -> Mapping[str, Any]: ...
def hints_for_type(self, type_info: TypeInfo) -> Mapping[str, Any]: ...
def hints_for_field(self, field: FieldInfo) -> Any | None: ...
def hints_for_property(self, prop: PropertyInfo) -> Any | None: ...
def annotations(self, member: MemberInfo) -> Sequence[AnnotationInfo]: ...
annotations consolidates former annotations.for_member. The method delegates to the configured annotation provider and maintains strict mode behavior (none).
4.4 Invocation & Instance Construction¶
Invocation entry points are renamed to align with their purpose while retaining strict-mode support and cache priming.
class ReflectionContext:
def invoke(
self,
callable_info: CallableInfo,
*args: Any,
strict: bool | None = None,
**kwargs: Any,
) -> Any: ...
def construct(
self,
type_info: TypeInfo,
*args: Any,
strict: bool | None = None,
**kwargs: Any,
) -> Any: ...
def create_instance(
self,
type_info: TypeInfo,
*args: Any,
strict: bool | None = None,
**kwargs: Any,
) -> Any: ...
Notes:
invokeprepares type-hint caches when strict mode is true before delegating to the invoker.constructandcreate_instanceremain aliases;create_instancecontinues to call intoconstructfor ergonomics.
4.5 Internal Restructuring¶
Core steps
- Remove nested facade classes (
_ResolveFacade,_ScanFacade,_HintsFacade,_AnnotationsFacade,_InvokeFacade,_CreateFacade) and inline their logic into the new top-level methods. - Reuse
_prepare_type_hints_cache,_effective_strict, and the extracted_build_scan_predicatehelper to keep strict-mode and filtering behaviour intact. - Simplify
ReflectionContext.__init__so the context instantiates scanner, type resolver, callable invoker, instance factory, type-hint provider, annotation provider, and type checker internally while still honoring optional cache, metrics, and type-hint configuration parameters.
class ReflectionContext:
def __init__(
self,
*,
cache: HierarchicalCache | None = None,
metrics: ReflectionMetrics | None = None,
type_hint_config: TypeHintConfig | None = None,
) -> None:
cache = cache or ConcurrentHierarchicalCache()
metrics = metrics or ReflectionMetricsCollector()
type_hint_config = type_hint_config or TypeHintConfig()
self._cache = cache
self._metrics = metrics
self._member_builder = MemberBuilder(cache=self._cache)
self._import_resolver = PythonImportResolver(
builder=self._member_builder,
metrics=self._metrics,
)
self._type_resolver = RuntimeTypeResolver(builder=self._member_builder)
self._scanner = PackageScanner(
imports=self._import_resolver,
builder=self._member_builder,
metrics=self._metrics,
)
self._invoker = SafeCallableInvoker(metrics=self._metrics)
self._instance_factory = TypeInstanceFactory(invoker=self._invoker)
self._type_hints = HighFidelityTypeHintProvider(
import_resolver=self._import_resolver,
metrics=self._metrics,
cache=self._cache,
strict=type_hint_config.strict,
allow_module_level_fallback=type_hint_config.allow_module_level_fallback,
cache_key_strategy=type_hint_config.cache_key_strategy,
)
self._annotations = DecoratorAnnotationProvider()
self._type_checker = TypeChecker()
Next actions
- Remove
ReflectionContextBuilder(andReflectionContext.derive()where applicable), migrating remaining tests and documentation to constructor + fixture-based customization. - Update the
reflectionsingleton inloryth.common.reflection.__init__to construct the context directly without involving the builder. - Deliver the new TypeInfo utility helpers that replace
scan_types, then delete the legacy helper and migrate dependent call sites.
4.6 Documentation and Test Updates¶
- Integration tests: Update
tests/integration/reflection/test_context_api.py,test_context_ergonomics.py,test_context_workflows.py, and related fixtures to call the new methods. As part of the update, audit each builder-dependent test and categorize it as delete (pure configurability coverage) or refactor (replace with component-specific fixtures). See §7 for guidance. - Regression tests: Ensure strict-mode scenarios (
test_api_conformance_flags.py,test_error_payloads_schema.py) cover bothresolveandinvokesemantics using the new constructor-driven context. - Documentation: Refresh
docs/contributors/reflection-architecture.mdand the roadmap Phase A section to list the new method names and explain the constructor simplification. Add a contributor guide snippet demonstrating side-by-side migration examples. - Changelog: Add an entry describing the API change (facade removal, constructor seam removal, new methods).
4.7 TypeInfo Utilities¶
New helper functions live in loryth.common.reflection.type_info_utils and are re-exported from the reflection package:
from loryth.common.reflection import (
iter_public_fields,
iter_public_methods,
iter_public_properties,
)
The helpers provide ergonomic, public-API alternatives to scan_types, enabling tests and consumers to enumerate TypeInfo metadata without reintroducing facade seams. Dedicated unit coverage lives in tests/unit/reflection/type_info/test_type_info_utils.py.
5. ⚙️ Semantics¶
- Strict mode:
resolvedefaults to strict (True) and raisesResolveErrorwhen a symbol cannot be located; passingstrict=FalsereturnsNone. Invocation APIs follow the same_effective_strictsemantics as today. - Idempotent metadata: Methods returning
ModuleInfo,TypeInfo, or annotations reuse cached instances; repeated calls do not rebuild metadata. - Scanning inputs:
scan_packageandscan_moduleaccept either dotted strings or pre-resolvedPackageInfo/ModuleInfo. When provided a string, the context resolves and then scans, ensuring metrics and caching behave identically to the original facade logic. - Invocation defaults:
invoke,construct, andcreate_instanceall pre-hydrate type-hint caches when strict mode applies and emit the same metrics events (scheduled for Phase B refinement). - Utility expectations: Any new TypeInfo helpers we introduce outside
ReflectionContextmust be pure functions that operate on existingMemberInfoobjects without mutating them.
6. 🧪 Examples¶
6.1 Quickstart: Resolve and Scan¶
Start with the constructor-only API to resolve symbols, scan packages, and access metadata without using facades.
from loryth.common.reflection import ReflectionContext
ctx = ReflectionContext()
type_info = ctx.resolve_type("tests.resources.sample_module.ExampleClass")
module_info = ctx.resolve_module("tests.resources.sample_module")
maybe_member = ctx.resolve("tests.resources.sample_module.MISSING", strict=False)
for member in ctx.scan_package("tests.resources", recursive=False):
print(member.qualname)
package_info = ctx.resolve_package("tests.resources")
public_classes = [
m for m in ctx.scan_package(package_info, public_only=True) if m.kind.value == "class"
]
hints = ctx.hints_for_callable(type_info.ctor) # type: ignore[arg-type]
annotations = ctx.annotations(type_info)
result = ctx.invoke(type_info.methods[0], 1, 2, strict=True)
instance = ctx.create_instance(type_info, 5, strict=False)
6.2 Inspect Metadata with TypeInfo Utilities¶
Pair the new TypeInfo helpers with the simplified context to enumerate public methods while reusing cached metadata.
from loryth.common.reflection import iter_public_methods
type_info = ctx.resolve_type("tests.resources.sample_module.ExampleClass")
method_names = [method.name for method in iter_public_methods(type_info)]
6.3 Strict Resolution with Targeted Scans¶
Show how strict resolution feeds directly into module-level scans for service discovery workflows.
service_info = ctx.resolve_type("myapp.services.UserService")
module_members = list(ctx.scan_module(service_info.module, name_contains="Service"))
assert any(member.qualname == service_info.qualname for member in module_members)
7. 🧪 Test Matrix¶
7.1 Scenarios¶
7.1.1 Resolve module/type/package/function via dotted string¶
Methods - resolve_module - resolve_type - resolve_package - resolve_function - resolve
Current Coverage - tests/integration/reflection/context/test_context_resolution.py - tests/integration/reflection/context/test_context_workflows.py - tests/integration/reflection/test_dotted_path_resolution.py
Actions - Refactor to fixture-driven tests with AAA structure and tolerant/strict assertions. - Validate identity reuse for concrete types and tolerant fallbacks in test_context_resolution.py.
7.1.2 Resolve using concrete type[Any]¶
Methods - resolve_type
Current Coverage - tests/integration/reflection/context/test_context_resolution.py
Actions - Maintain identity reuse assertions (is comparisons) alongside descriptive docstrings.
7.1.3 Package/module scanning (string and MemberInfo inputs)¶
Methods - scan_package - scan_module
Current Coverage - tests/integration/reflection/context/test_context_scanning.py - tests/integration/reflection/test_scan_filters_conformance.py
Actions - Ensure test_context_scanning.py covers PackageInfo reuse, AAA structure, and fixture-driven inputs. - Keep legacy scanning conformance tests aligned with public APIs while avoiding scan_types.
7.1.4 Type member introspection helpers¶
Methods - Utilities operating on TypeInfo (planned as needed)
Current Coverage - tests/unit/reflection/type_info/test_type_info_utils.py
Actions - Ship helper utilities with dedicated fixtures under tests/unit/reflection/type_info/ in line with TD-024.
7.1.5 Type-hint retrieval (callable/type/field/property)¶
Methods - hints_for_callable - hints_for_type - hints_for_field - hints_for_property
Current Coverage - tests/integration/reflection/context/test_context_metadata.py - tests/integration/reflection/context/test_context_workflows.py - tests/regression/reflection/test_property_precedence_rules.py
Actions - Supply callable/type metadata through shared fixtures; document strict/tolerant expectations in the context suite. - Keep regression coverage compatible without relying on builder overrides.
7.1.6 Annotation retrieval¶
Methods - annotations
Current Coverage - tests/integration/reflection/context/test_context_metadata.py
Actions - Rely on shared fixtures and public APIs for annotation assertions.
7.1.7 Strict invocation success/failure¶
Methods - invoke - construct - create_instance
Current Coverage - tests/integration/reflection/context/test_context_invocation.py - tests/integration/reflection/context/test_strict_invocation_behavior.py - tests/integration/reflection/test_api_conformance_flags.py - tests/unit/reflection/test_error_payloads_schema.py
Actions - Assert strict/tolerant behavior with shared fixtures while routing invocation payload tests through factory-driven contexts.
7.1.8 Cache clearing and context overrides¶
Methods - clear_caches - Test-only fixtures providing collaborator overrides
Current Coverage - tests/integration/reflection/context/test_cache_management.py
Actions - Validate cache clearing through public metrics and namespace eviction without touching internals.
7.2 Coverage Tracker (Phase 2E Target)¶
| Scenario (§7.1.x) | Primary Suites | Notes |
|---|---|---|
| 7.1.1 Resolve dotted inputs | tests/integration/reflection/context/test_context_resolution.py, tests/integration/reflection/context/test_context_workflows.py, tests/integration/reflection/test_dotted_path_resolution.py | Fixture-driven AAA tests with tolerant/strict assertions |
| 7.1.2 Resolve concrete types | tests/integration/reflection/context/test_context_resolution.py | Identity reuse stays covered via is assertions |
| 7.1.3 Package/module scanning | tests/integration/reflection/context/test_context_scanning.py, tests/integration/reflection/test_scan_filters_conformance.py | String vs MemberInfo inputs remain covered without scan_types |
| 7.1.4 TypeInfo utilities | tests/unit/reflection/type_info/test_type_info_utils.py | Pure helpers operating on TypeInfo with localized fixtures |
| 7.1.5 Type-hint retrieval | tests/integration/reflection/context/test_context_metadata.py, tests/integration/reflection/context/test_context_workflows.py, tests/regression/reflection/test_property_precedence_rules.py | Behavioral assertions only; uses reflection_context_factory |
| 7.1.6 Annotation retrieval | tests/integration/reflection/context/test_context_metadata.py | Sequence assertions built on shared fixtures |
| 7.1.7 Strict invocation paths | tests/integration/reflection/context/test_context_invocation.py, tests/integration/reflection/context/test_strict_invocation_behavior.py, tests/integration/reflection/test_api_conformance_flags.py, tests/unit/reflection/test_error_payloads_schema.py | Strict failure and tolerant success scenarios stay backed by shared fixtures |
| 7.1.8 Cache management | tests/integration/reflection/context/test_cache_management.py, tests/fixtures/reflection_common.py fixtures | Cache eviction exercises public calls and metrics hooks |
7.3 Phase 2E Additions Delivered¶
- Strict/Tolerant Invocation Matrix —
tests/integration/reflection/context/test_strict_invocation_behavior.pyexercises failure and tolerant success paths via shared fixtures. - Cache Management Coverage —
tests/integration/reflection/context/test_cache_management.pyvalidatesclear_caches()(global and namespaced) using public metrics hooks. - TypeInfo Utilities Suite —
src/loryth/common/reflection/type_info_utils.pyships with unit coverage undertests/unit/reflection/type_info/test_type_info_utils.py. - Refactored Context API Tests — Context-focused suites reside in
tests/integration/reflection/context/, each targeting a specific workflow (resolution, scanning, metadata, invocation, end-to-end). - Annotation & Metrics Enhancements — Metadata workflows employ shared fixtures and document tolerant semantics; cache suite covers metrics behavior.
- Regression Alignment — Existing regression suites remain green and no longer rely on builder overrides; further clean-up is deferred to Phase 3.
7.4 Fixture & Utility Consolidation Plan¶
- Promote
reflection_context_factoryto the canonical entry for integration suites while avoiding bespoke constructors in favour of shared fixtures. - Extend
tests/fixtures/reflection_common.pywith helper factories (for example,type_info_for) wrapping the new TypeInfo utilities while keeping behaviour observable and public. - Introduce
tests/unit/reflection/type_info/conftest.pyto share reusableTypeInfosamples across the utility tests without reintroducing builder seams. - Keep context-focused tests under
tests/integration/reflection/context/to eliminate inline fixture duplication.
7.5 Naming & Structure Improvements¶
- File Reorganization — Keep context-specific suites in
tests/integration/reflection/context/, including the strict/tolerant and cache-management modules. - Test Naming — Maintain the
test_<component>_<behavior>convention with descriptive docstrings across the reorganized files. - Unit Suite Layout — Use
tests/unit/reflection/type_info/for the utility helpers; additional metrics-specific moves remain optional Phase 3 clean-up. - Shared Resources — Draw integration tests from centralized fixtures while tracking broader resource consolidation in Phase 3.
8. ✅ Acceptance Criteria¶
- The plan removes all facade properties from
ReflectionContext; the new method set matches §4 without omissions. - Existing integration/regression tests compile and pass with the renamed methods, augmented per the test matrix.
- Contributor documentation (
reflection-architecture.md, roadmap Phase A entry) reflects the revised API. - Changelog entry highlights the breaking change and new method names.
- No remaining repository references to
ctx.resolve.*,ctx.scan.*,ctx.hints.*,ctx.annotations.*,ctx.invoke.*, orctx.create.*facade patterns (verified via repository search). - The plan removes
ReflectionContextBuilderandReflectionContext.derive(); all usages migrate to direct constructor + fixtures. - Test coverage aligns with the scenarios captured in the test matrix (§7).
9. 🚀 Rollout and Migration¶
9.1 Implementation Phases¶
Phase 1: API Surface Extraction¶
- Add direct
ReflectionContexthelpers (resolve_module,scan_package, etc.) and document preferred usage. - Result: Downstream code adopts the simplified API without breaking existing paths.
Phase 2A: Resolver Defaults¶
- Internalize default import/type resolver creation when constructor overrides are not provided and accept optional cache/metrics parameters on the constructor.
- Introduce a shared
reflection_context_factory/build_reflection_contexthelper so regression and integration suites no longer depend on builder overrides. - Outcome:
ReflectionContextowns import/type resolution while cache/metrics remain optional parameters and tests cover the default-construction path.
Phase 2B: Scanner & Invocation Defaults¶
- Internalize package scanner, callable invoker, and instance factory creation; replace builder overrides with explicit fixtures and validate strict-mode metrics behaviour across the integration suites.
Phase 2C: Metadata Provider Defaults¶
- Ensure the constructor owns type-hint and annotation providers plus type checker; guide tests to use constructor knobs and fixtures, leaving no mutable collaborator seams.
Phase 2D: Builder Retirement & Constructor Simplification¶
- Remove
ReflectionContextBuilder, update the global singleton to instantiateReflectionContextdirectly, and finish all active documentation/examples to highlight the constructor-only API (historical docs remain preserved).
Phase 2E: Test Matrix Coverage¶
- Implement or update the test suites enumerated in §7 so each scenario in the matrix has focused coverage against the simplified API, including strict/tolerant paths and cache behavior.
- Outcome: The defined behaviors stay protected by automated tests aligned with the new method surface before repository-wide cleanup.
Phase 3: Repository Adoption & Cleanup¶
- Sweep the repository for legacy facade/builder usage, delete or refactor configurability tests per §7, and update changelog/documentation.
9.3 Migration Strategy¶
For Framework Maintainers 1. Execute Phase 2 sub-phases sequentially: internalize each collaborator default, migrate affected tests to component fixtures, complete the dedicated §7 test-matrix coverage pass, and drop redundant configurability suites. 2. Once the constructor owns the collaborators, remove ReflectionContextBuilder, limit ReflectionContext.__init__ to cache/metrics overrides, and update documentation/examples. 3. Finish with a repository sweep (Phase 3) replacing facade/builder usage, updating changelog entries, and confirming the test matrix is fully exercised.
For Downstream Consumers - Replace facade calls with the new method names listed in §3.3. - Instantiate ReflectionContext directly, optionally passing cache/metrics; other collaborators are no longer overridable. - Use component-level fixtures (e.g., stub scanners) when custom behaviour is required for tests.
9.4 Remaining Work Summary¶
- Phase A scope is complete. Repository-wide adoption tasks (changelog updates, downstream migration guidance, optional
scan_typesdeprecation) are tracked as part of Roadmap 002 Phase A.3.
9.5 Phase 2E Verification Plan¶
- Run
.venv/bin/python3.13 -m pytest tests/integration/reflection tests/unit/reflectionto validate the refreshed suites; add targeted-k strictruns where helpful. - Execute
rg "scan_types"after migrations to confirm remaining references are either transitional (noted) or ready for Phase 3 cleanup. - Re-read TD-027 §7–§9 post-implementation to ensure test coverage notes, fixtures, and rollout text remain synchronized with the codebase.
- Capture any lingering historical-doc references or downstream adoption tasks and funnel them into Phase 3 tracking before closing Phase 2E.
- Track
.venv/bin/python3.13 -m pytest tests/integration/reflection tests/unit/reflectionas the verification run and record results once executed.
10. 📊 Progress Status¶
- Phase A.1 — API Surface Extraction: ✅ Completed (2025-10-20); direct helpers shipped and docs highlight new entry points.
- Phase A.2A — Resolver Defaults: ✅ Completed (2025-10-22); constructor now owns resolver defaults and tests use the shared factory fixtures.
- Phase A.2B — Scanner & Invocation Defaults: ✅ Completed (2025-10-23); constructor wires scanner/invoker/instance factory and tests rely on fixture helpers instead of builder overrides.
- Phase A.2C — Metadata Provider Defaults: ✅ Completed (2025-10-23); constructor owns type-hint/annotation providers and type checker, leaving no mutable seams.
- Phase A.2D — Builder Retirement: ✅ Completed (2025-10-24); constructor-only API and documentation updates landed, with historical docs intentionally preserved.
- Phase A.2E — Test Matrix Coverage: ✅ Completed (2025-10-24); scenario-focused suites, strict/tolerant workflows, cache coverage, and TypeInfo utilities now guard the simplified API.
- Phase A.3 — Adoption & Cleanup: 🔄 Not Started; repository sweep, test deletions/refactors, and changelog/documentation updates outstanding.
11. ⚠️ Risks and Mitigations¶
| Risk | Impact | Mitigation |
|---|---|---|
| External consumers (outside repo) still rely on facades | Runtime failures after upgrade | Publish migration guide in changelog and release notes |
| Missed facade method during migration | Incomplete API, broken tests | Use interface impact table as checklist; run rg "ctx\.(resolve|scan|hints|annotations|invoke|create)\." to ensure no residual usage |
| Naming confusion for invocation methods | Developer friction | Clearly document invoke, construct, create_instance semantics; include examples for strict mode |
| Potential regressions in strict-mode handling | Incorrect type checking behavior | Add focused tests for strict vs. tolerant invocation paths; reuse regression suites |
| Builder removal breaks configurability-only tests | False failures / lost coverage | Classify tests per §7 (delete vs refactor) and add component fixtures where behaviour remains valuable |
12. Open Questions¶
- Should
resolve_typesupport call sites that already have aTypeInfoinstance (pass-through), or should those callers rely on existing helpers (of_type)? Pending review feedback. - When we re-introduce extension seams post Phase A, what is the minimal public configuration surface we want to expose (e.g., explicit factories vs. lightweight hooks)?
13. 📖 Glossary¶
- ReflectionContext — Core orchestration class that wires import resolution, scanning, metadata providers, and invocation helpers.
- Strict mode — Default behavior where resolution/invocation raises errors on mismatches; configurable per call via the
strictparameter. - MemberInfo — Base metadata type describing reflected modules, packages, types, callables, and fields.
- TypeInfo utilities — Planned helper functions that operate on
TypeInfoinstances (e.g., iterating methods) without requiring facade support.
14. 📎 Appendix¶
14.1 Quality Audit Findings (TD-024 Alignment)¶
- Context-level integration tests now live under
tests/integration/reflection/context/with AAA structure, shared fixtures, and noscan_typesdependencies. - Strict/tolerant invocation scenarios are covered in
test_strict_invocation_behavior.py, while payload schemas reuse thereflection_context_factoryfixture. - Cache management behavior is validated through the new integration suite, ensuring no private cache state checks remain.