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 030 โ Reflection Handle Hierarchy Rationalization¶
| Field | Value |
|---|---|
| ID | TD-030 |
| Title | Reflection Handle Hierarchy Rationalization |
| Status | โ Complete (Phase 1 โ , Phase 2 โ , Phase 3 โ , Phase 4 โ , Phase 5 โ ) |
| Owner | Reflection maintainers |
| Audience | Reflection/DI contributors, type system maintainers |
| Created | 2025-10-25 |
| Last Updated | 2025-10-26 (Phase 5 Complete) |
| Reviewers | TBD |
| Approvers | TBD |
| Tracking | Phase I of 002-reflection-modernization-roadmap.md |
| Related | TD-033 โ ReflectionEngine & HandleRegistry (depends on this), TD-031 โ Handle Generation Strategy Protocol (depends on this), TD-027 โ API Simplification, TD-028 โ Metrics Architecture |
๐งญ 1. Summary¶
The current reflection system uses passive "Info" classes that obscure their purpose as active references and navigation tools. This design establishes a clearer five-phase refactoring that renames all *Info classes to *Handle classes and reorganizes the codebase for better maintainability. Phase 1 (complete) established a clean type hierarchy (ReflectionInfo โ ModuleInfo/MemberInfo) with explicit ownership semantics. Phase 2 (planned) completes the architectural vision by renaming to Handles and reorganizing into individual files within the handles package. Subsequent phases add ownership properties, consolidate APIs, and enable Phase C (Member Parsing Seam) implementation.
At a glance:
- Phase 1 โ Complete: ReflectionInfo/MemberInfo hierarchy with explicit module/type properties (791 tests passing)
- Phase 2 โ
Complete: Rename *Info โ *Handle; reorganize into
handles/package (one class per file) - Phase 3 โ Complete: Ownership properties added (module, type) to all member types
- Phase 4 โ Complete: Unified modules model (PackageHandle removed, merged into ModuleHandle with is_package property)
- Phase 5 โ Complete: Scanner integration unified (scan_package merged into scan_module with recursive parameter)
- Handles terminology: Better semantics for "active references" vs passive data containers
- One class per file: Improved discoverability, maintainability, and independent evolution
- Unified modules: Consolidate PackageInfo into ModuleInfo with
is_packageproperty - Unified scanning: Single scan_module() API with recursive parameter replaces separate scan_package()
- Clear ownership: Explicit module and type properties enable straightforward navigation
- Foundation for Phase C: Handles semantics align with Member Parsing Seam design
- Zero consumer impact: Internal refactor; public APIs and DI code unchanged
๐ 2. Context and Problem Statement¶
The current type hierarchy has MemberInfo as the root abstraction for all introspectable entities: modules, packages, types, methods, fields, and parameters. Ownership semantics are implicit and scattered across code:
- TypeInfo has
moduleproperty (it belongs to a module) - MethodInfo/FieldInfo/PropertyInfo have no explicit
typeproperty (relationship to class is implicit) - ParameterInfo has no clear parent linkage
This design creates navigation challenges and makes the conceptual model harder to reason about.
Pain points:
- Implicit ownership: No direct way to navigate from method โ type โ module ownership chain
- Redundant classes:
PackageInfoduplicatesModuleInfo; Python treats packages as modules with__path__. - Unclear scope: Hard to answer "what scope contains this member?" without special cases
- Complicates Phase C: The Member Parsing Seam design needs explicit "member belongs to X" semantics for clean strategy composition.
Prior work: TD-027 (API Simplification) narrowed the public surface of ReflectionContext; TD-028 (Metrics Architecture) introduced strongly-typed metrics. This TD completes the foundational cleanup.
2.1 Goals¶
- Separate containers from members conceptually and in code
- Consolidate PackageInfo into ModuleInfo (eliminate redundancy)
- Provide a clear type hierarchy that reflects Python's object model
- Foundation for Phase C: Enable parsing strategies to clearly distinguish discovery (members) from orchestration (containers)
- Maintain zero breaking changes for DI consumers
2.2 Out of Scope¶
- Parsing strategy design (Phase C)
- Type checking redesign (Phase F)
- Annotation and type-hint provider consolidation (Phase E)
- Performance optimization
- Scanning algorithm unification (Phase D)
2.3 Assumptions and Constraints¶
- Python 3.13+ with strict mypy and PEP 695 generics
- Alpha stage: Breaking changes allowed with migration guidance (no deprecation period)
- Must complete before Phase C implementation (though alphabetically later)
- Zero breaking changes for DI consumers โ this is internal refactoring
- Existing test coverage maintained โ all tests pass with new hierarchy
2.4 Terminology: Handle vs Info¶
Decision: Rename all *Info classes to *Handle to better convey their purpose as lightweight references to introspectable entities, not passive data containers.
Rationale:
The term "handle" better communicates that these are: - Active references to runtime entities (modules, types, methods) - Navigation tools for traversing the reflection graph - Lazy accessors that may trigger introspection on demand - Lightweight proxies that don't duplicate the underlying entity
This aligns with established patterns in other reflection systems (Java's MethodHandle, C#'s unsafe handles).
Alternatives considered:
| Term | Pros | Cons | Status |
|---|---|---|---|
| Handle | Clear intent (reference/accessor), established pattern (Java), concise | None significant | โ Selected |
| Info | Familiar (current), simple | Suggests passive data; doesn't convey navigation/reference semantics | โ Rejected |
| Ref | Very concise, common in systems programming | Ambiguous (reference vs referent); conflicts with Python's weakref | โ Rejected |
| Proxy | Clearly indicates delegation pattern | Heavier semantics; suggests interception/wrapping over introspection | โ Rejected |
| Descriptor | Precise reflection terminology | Conflicts with Python's descriptor protocol; too specific | โ Rejected |
| Meta | Concise, conveys metadata | Overloaded term (metaclass); unclear in context | โ Rejected |
Naming convention: All classes follow *Handle pattern: - ReflectionInfo โ ReflectionHandle (base) - MemberInfo โ MemberHandle (owned entities) - ModuleInfo โ ModuleHandle (scopes) - TypeInfo โ TypeHandle (class introspection) - FunctionInfo โ FunctionHandle (function introspection) - MethodInfo โ MethodHandle (method introspection) - And so on for all member types
๐งฉ 3. Design Overview¶
The design introduces a simplified ReflectionInfo abstract base that branches into two concrete categories:
ModuleInfoโ Scopes that contain members (modules and packages unified)MemberInfoโ Entities that belong to a scope (types, functions, methods, fields, parameters)
Ownership is explicit through properties: - All MemberInfo subclasses have a module: ModuleInfo property - Class-level MemberInfo subclasses (MethodInfo, FieldInfo, PropertyInfo) also have a type: TypeInfo property - TypeInfo occupies a special dual role: it is a member of a module (extends MemberInfo) but also a container for its own members (methods, fields, properties)
This explicit ownership model eliminates ambiguity and enables clear navigation of the member hierarchy.
3.1 Key Components¶
| Component | Purpose |
|---|---|
ReflectionInfo | Abstract base for all introspectable entities; common properties (qualname, name, doc, annotations) |
ModuleInfo | Concrete scope for Python modules and packages (unified); includes is_package property and submodules iterator |
MemberInfo | Abstract base for owned members (types, functions, methods, fields, parameters); defines module property |
TypeInfo | Dual role: member of module (has module property) + container for class members (has methods, fields iterators) |
3.2 Type Hierarchy Overview¶
graph TD
RI["ReflectionInfo<br/>(abstract)"]
MOD["ModuleInfo<br/>(scope)"]
MI["MemberInfo<br/>(owned)"]
RI --> MOD
RI --> MI
MOD -. contains .-> MI
classDef base fill:none,stroke:#8E24AA,stroke-width:2px,color:#8E24AA
classDef scope fill:none,stroke:#00C853,stroke-width:2px,color:#00C853
classDef member fill:none,stroke:#1976D2,stroke-width:2px,color:#1976D2
class RI base
class MOD scope
class MI member 3.2a Complete MemberInfo Hierarchy¶
graph TD
RI["ReflectionInfo"]
MOD["ModuleInfo<br/>is_package: bool<br/>classes: Iterator<br/>functions: Iterator<br/>submodules: Iterator"]
MI["MemberInfo<br/>module: ModuleInfo"]
TI["TypeInfo<br/>module: ModuleInfo<br/>methods: Iterator<br/>fields: Iterator"]
FI["FunctionInfo<br/>module: ModuleInfo"]
MTH["MethodInfo<br/>module: ModuleInfo<br/>type: TypeInfo"]
FLD["FieldInfo<br/>module: ModuleInfo<br/>type: TypeInfo"]
PRP["PropertyInfo<br/>module: ModuleInfo<br/>type: TypeInfo"]
PARAM["ParameterInfo<br/>module: ModuleInfo<br/>type: TypeInfo"]
RI --> MOD
RI --> MI
MI --> TI
MI --> FI
MI --> MTH
MI --> FLD
MI --> PRP
MI --> PARAM
MOD -. "contains" .-> TI
MOD -. "contains" .-> FI
TI -. "contains" .-> MTH
TI -. "contains" .-> FLD
TI -. "contains" .-> PRP
MTH -. "contains" .-> PARAM
classDef base fill:none,stroke:#8E24AA,stroke-width:2px,color:#8E24AA
classDef scope fill:none,stroke:#00C853,stroke-width:2px,color:#00C853
classDef member fill:none,stroke:#1976D2,stroke-width:2px,color:#1976D2
classDef dual fill:none,stroke:#FFB300,stroke-width:2px,color:#FFB300
class RI base
class MOD scope
class MI member
class TI dual 3.3 Interface Impact (Before โ After)¶
| Area | Before | After |
|---|---|---|
| Type hierarchy | MemberInfo (root) for all entities | ReflectionInfo (root) โ ModuleInfo / MemberInfo |
| Member ownership | Implicit (scattered across code) | Explicit: module and type properties on MemberInfo |
| Package representation | PackageInfo (separate class) | ModuleInfo.is_package property |
| MemberBuilder | build_package(module) โ PackageInfo | build_module(module) โ ModuleInfo (consolidated) |
| ReflectionContext | resolve_package(path) โ PackageInfo | resolve_module(path) โ ModuleInfo (consolidated) |
| PackageScanner | scan_package(pkg, recursive=True) | scan_module(mod, recursive=True) |
| Enums | MemberKind.MODULE, MemberKind.PACKAGE | Removed; optionally add ContainerKind.MODULE |
| Cache keys | ("builder", "package", qualname) | ("module", qualname) for modules |
| Terminology | *Info classes (MemberInfo, TypeInfo, etc.) | *Handle classes (MemberHandle, TypeHandle, etc.) |
| Package location | member_types/ (base.py, structural.py, callables.py, containers.py) | handles/ (one file per class) |
| Enum location | reflection/enums.py | handles/enums.py |
| Import path | from .member_info import TypeInfo | from .handles import TypeHandle |
3.4 Package Restructuring: handles¶
New package structure: loryth.common.reflection.handles
Organization: - One class per file: Each handle class gets its own module for clarity and maintainability - Enums included: Move reflection enums into handles package - Flat namespace: Direct imports from handles package for ergonomics
File structure:
src/loryth/common/reflection/handles/
โโโ __init__.py # Re-exports all handles and enums
โโโ reflection_handle.py # ReflectionHandle ABC
โโโ member_handle.py # MemberHandle ABC
โโโ module_handle.py # ModuleHandle (scope)
โโโ package_handle.py # PackageHandle (to be removed in Phase 4)
โโโ type_handle.py # TypeHandle (dual role: member + container)
โโโ callable_handle.py # CallableHandle ABC
โโโ function_handle.py # FunctionHandle
โโโ method_handle.py # MethodHandle
โโโ constructor_handle.py # ConstructorHandle
โโโ field_handle.py # FieldHandle
โโโ property_handle.py # PropertyHandle
โโโ parameter_handle.py # ParameterHandle
โโโ enums.py # Visibility, MemberKind, MethodKind, Variance
Import patterns:
# Before
from loryth.common.reflection.member_info import TypeInfo, MemberInfo
from loryth.common.reflection.enums import MemberKind
# After
from loryth.common.reflection.handles import TypeHandle, MemberHandle, MemberKind
Rationale for one-class-per-file approach: - Clarity: Each class is independently discoverable and testable - Maintainability: Smaller focused modules reduce cognitive load - Discoverability: File names match class names exactly (type_handle.py for TypeHandle) - Growth: Easier to extend individual classes without touching unrelated code - Dependencies: Clear module boundaries make import dependencies explicit
3.5 Removal/Deprecation Policy¶
Alpha stage: Clean break.
PackageInfoclass removed entirely (not available)build_package(),resolve_package(),scan_package()methods removedMemberKind.MODULEandMemberKind.PACKAGEremoved from enums- All changes synchronized in single update (all tests, all code updated together)
- No deprecation period; no aliases
- Tests and migration examples provided in documentation
๐ 4. Implementation Details¶
4.1 Type Hierarchy & MemberInfo Abstraction¶
ReflectionInfo (abstract base):
from abc import ABC, abstractmethod
from collections.abc import Iterator
class ReflectionInfo(ABC):
"""Abstract base for all introspectable entities.
Common properties for modules and members.
"""
@property
@abstractmethod
def qualname(self) -> str:
"""Fully qualified name (e.g., 'loryth.common.reflection.MethodInfo')."""
...
@property
def name(self) -> str:
"""Short name (last component of qualname)."""
return self.qualname.split(".")[-1]
@property
@abstractmethod
def doc(self) -> str | None:
"""Docstring if available."""
...
@property
@abstractmethod
def annotations(self) -> Iterator['AnnotationInfo']:
"""Annotations attached to this entity."""
...
MemberInfo (abstract, with explicit ownership):
class MemberInfo(ReflectionInfo, ABC):
"""Abstract base for owned members.
All members belong to a ModuleInfo. Class-level members additionally
belong to a TypeInfo.
"""
@property
@abstractmethod
def module(self) -> 'ModuleInfo':
"""Module that owns this member."""
...
# For class-level members only (MethodInfo, FieldInfo, PropertyInfo)
# Subclasses will override to provide TypeInfo
See source code for concrete implementations (ModuleInfo, TypeInfo, FunctionInfo, MethodInfo, etc.).
4.2 ModuleInfo: Unified Container¶
ModuleInfo (scope for modules and packages):
class ModuleInfo(ReflectionInfo):
"""Scope for Python modules and packages (unified).
Packages (modules with __path__) and regular modules both use
ModuleInfo; distinguish via is_package property.
"""
@property
def is_package(self) -> bool:
"""True if this module is a package (has __path__)."""
...
@property
def classes(self) -> Iterator['TypeInfo']:
"""Types defined directly in this module."""
...
@property
def functions(self) -> Iterator['FunctionInfo']:
"""Functions defined directly in this module."""
...
@property
def submodules(self) -> Iterator['ModuleInfo']:
"""Submodules (empty if not a package)."""
...
4.3 API Changes in MemberBuilder, ReflectionContext, and PackageScanner¶
MemberBuilder updates:
- Remove
build_package(module: ModuleType) -> PackageInfomethod - Update
build_module()to transparently handle packages (hasattr(module, '__path__')) - Cache key changes:
("module", qualname)for all modules build_type(),build_function()signatures and behavior unchanged
ReflectionContext updates:
- Remove
resolve_package(path: str) -> PackageInfomethod resolve_module(path: str) -> ModuleInfohandles both modules and packagesscan_module(module, predicate, recursive: bool)replacesscan_package()
PackageScanner updates:
- Remove
scan_package()method entirely scan_module(module, predicate, recursive)single entry point- Internally: check
module.is_package and recursivefor recursion logic
4.4 Member Container Relationships¶
TypeInfo (dual role):
TypeInfo extends MemberInfo with module property (it's a member of a module) AND acts as container for class members:
class TypeInfo(MemberInfo):
"""Type (class) member with dual role.
1. As member: has module property (belongs to ModuleInfo)
2. As container: has methods/fields/properties iterators
"""
@property
def module(self) -> ModuleInfo:
"""Module containing this type."""
...
@property
def methods(self) -> Iterator['MethodInfo']:
"""Methods defined in this type."""
...
@property
def fields(self) -> Iterator['FieldInfo']:
"""Fields defined in this type."""
...
FunctionInfo (module-level function):
Functions belong only to a module (not a class):
class FunctionInfo(MemberInfo):
"""Function member."""
@property
def module(self) -> ModuleInfo:
"""Module containing this function."""
...
Class members (MethodInfo, FieldInfo, PropertyInfo):
Class members belong to both a module (transitively) and a type:
class MethodInfo(MemberInfo):
"""Method member."""
@property
def module(self) -> ModuleInfo:
"""Module containing the class that owns this method."""
...
@property
def type(self) -> TypeInfo:
"""Type (class) that owns this method."""
...
Same pattern for FieldInfo, PropertyInfo.
4.5 Backward Compatibility & Breaking Changes¶
Removed entirely: - PackageInfo class (no compatibility shim) - build_package(), resolve_package(), scan_package() methods - MemberKind.MODULE and MemberKind.PACKAGE enum values
Changed behavior: - build_module() now returns ModuleInfo for packages (was PackageInfo) - resolve_module() now returns ModuleInfo for packages (was PackageInfo) - Cache keys simplified: ("module", qualname) instead of ("builder", "module/package", ...)
Preserved behavior: - DI bootstrap unchanged (uses ReflectionContext, unaware of internals) - Type resolution semantics identical - Member discovery semantics identical - Public API method names and signatures mostly unchanged (only removals)
โ๏ธ 5. Semantics¶
Ownership model:
- Modules (
ModuleInfo): Top-level scopes. Do not belong to anything. - Members (
MemberInfo): All havemodule: ModuleInfoproperty. Everything introspectable except modules is a member. - Class members:
MethodInfo,FieldInfo,PropertyInfoadditionally havetype: TypeInfoproperty. - Parameters:
ParameterInfoalso havemoduleandtype(if parameter of a method) properties.
TypeInfo dual role:
- As member of module:
type_info.modulereturns the containingModuleInfo - As container for class members:
type_info.methods,type_info.fieldsprovide access to class members - Reflects Python semantics: classes are module members that also contain members
Navigation semantics:
module โ type โ method โ parameter
โ โ โ
(owns) (owns) (owns)
Every entity can navigate "upward" to its owning container: - method.type โ containing class - method.module โ containing module - type.module โ containing module - function.module โ containing module
Package handling:
- Packages are modules with
__path__attribute (Python's native model) ModuleInfo.is_packageboolean property distinguishes packages from regular modulesModuleInfo.submodulesiterator is empty for non-packages- Caller controls recursion via
scan_module(..., recursive=bool)parameter
๐งช 6. Examples¶
6.1 Basic Usage¶
from loryth.common.reflection import ReflectionContext
context = ReflectionContext()
# Resolve module (works for packages too)
module = context.resolve_module("loryth.common.reflection")
print(module.is_package) # True if package, False otherwise
# Access and navigate members
for cls in module.classes:
print(f"Type: {cls.qualname}")
print(f"Module: {cls.module.qualname}") # Navigate back to owning module
6.2 Advanced Patterns¶
# Scan module recursively, access ownership chain
from loryth.common.reflection import TypeInfo, MethodInfo
for member in context.scan_module("loryth.common", recursive=True):
if isinstance(member, TypeInfo):
# Navigate: type โ module
print(f"{member.qualname} in module {member.module.qualname}")
for method in member.methods:
# Navigate: method โ type โ module
print(f" Method: {method.name} in {method.type.qualname}")
6.3 Integration Example¶
# MemberBuilder (internal usage)
from loryth.common.reflection.member_builder import MemberBuilder
from loryth.common.collections.concurrent import ConcurrentHierarchicalCache
builder = MemberBuilder(cache=ConcurrentHierarchicalCache())
import loryth.common.reflection as ref_module
module_info = builder.build_module(ref_module)
# Navigate package structure
if module_info.is_package:
for submodule in module_info.submodules:
for type_info in submodule.classes:
for method in type_info.methods:
# Full ownership chain: module โ type โ method
print(f"{method.module.qualname}.{method.type.name}.{method.name}")
๐งช 7. Test Matrix¶
Unit coverage (80%+): ReflectionInfo common properties; MemberInfo.module property and navigation; ModuleInfo.is_package property and submodules iterator; TypeInfo dual role (module property + members/fields iterators); MethodInfo/FieldInfo with both module and type properties; enum changes (removal of MemberKind.MODULE/PACKAGE).
Integration coverage (comprehensive): MemberBuilder.build_module() for both modules and packages (is_package = True/False); ReflectionContext.resolve_module() for package paths; PackageScanner.scan_module(recursive=True) with submodule iteration and ownership verification; ownership chain navigation (module โ type โ method); cache key correctness (simplified ("module", qualname)); DI bootstrap unchanged.
Regression coverage (all existing workflows): All existing module/package scanning behavior preserved; type resolution unchanged; metrics emission behavior unchanged; DI container initialization and autowiring unaffected; member access and ownership semantics identical.
Testing notes: Unit tests focus on ownership properties (module, type); integration tests verify full ownership chains via actual modules; regression tests ensure consumer APIs (especially DI) see zero behavior changes.
โ 8. Acceptance Criteria¶
- โณ
ReflectionInfoabstract base defined with common properties (qualname, name, doc, annotations) - โณ
MemberInforedefined to extendReflectionInfowithmodule: ModuleInfoproperty - โณ
ModuleInfoextendsReflectionInfowithis_packageproperty andsubmodulesiterator - โณ
PackageInfoclass fully removed from codebase - โณ
build_package()method removed fromMemberBuilder - โณ
resolve_package()method removed fromReflectionContext - โณ
scan_package()method removed fromPackageScanner - โณ
MemberKind.MODULEandMemberKind.PACKAGEenum values removed - โณ All member types (MethodInfo, FieldInfo, PropertyInfo, ParameterInfo) have explicit
module: ModuleInfoproperty - โณ Class-level members (MethodInfo, FieldInfo, PropertyInfo) have explicit
type: TypeInfoproperty - โณ Cache keys simplified to
("module", qualname)for module resolution - โณ All existing tests pass with new hierarchy (no test failures)
- โณ Integration tests verify ownership chains: module โ type โ method navigation
- โณ Integration tests verify
scan_module(recursive=True)withis_packagechecks - โณ Complexity per component โค10 cyclomatic, โค50 lines method length
- โณ Zero breaking changes to DI consumer APIs (DI bootstrap unchanged)
- โณ Architecture documentation updated with new type hierarchy and diagrams
- โณ Phase C (TD-029) unblocked and ready for implementation
๐ 9. Rollout and Migration¶
9.1 Implementation Phases¶
Phase 1 โ Core Hierarchy Simplification: โ Completed
- Create
ReflectionInfoabstract base with common properties (qualname, name, doc, annotations) - Redefine
MemberInfoto extendReflectionInfowithmodule: ModuleInfoproperty - Ensure all
MemberInfosubclasses preserve existing signatures and behavior - Update module-level imports to reflect new hierarchy (no API surface changes)
- Outcome: Simplified two-level hierarchy in place (ReflectionInfo โ ModuleInfo / MemberInfo)
Phase 2 โ Terminology Change & Package Restructuring:
- Create new
handles/package directory underloryth/common/reflection/ - Create individual files for each handle class (reflection_handle.py, member_handle.py, module_handle.py, type_handle.py, etc.)
- Move enums.py into
handles/package; consolidate all reflection enums there - Rename all
*Infoclasses to*Handle(find/replace: ReflectionInfo โ ReflectionHandle, MemberInfo โ MemberHandle, etc.) - Update all internal imports to use new
handlespackage structure - Remove old
member_types/package andmember_info.pyre-export layer - Update all test code to use new handle class names and package paths
- Run full test suite; verify all 791+ tests passing with new terminology
- Outcome: Clean "handle" semantics with one class per file; improved code organization
Phase 3 โ ModuleHandle Ownership Properties:
- Add
is_package: boolproperty toModuleHandle(True if module has__path__) - Add
module: ModuleHandleproperty to allMemberHandlesubclasses - Add
type: TypeHandleproperty to class-level members (MethodHandle, FieldHandle, PropertyHandle) - Update member builders to populate ownership properties during construction
- Outcome: Explicit ownership model in place, all properties accessible
Phase 4 โ PackageHandle Removal & API Consolidation:
- Remove
PackageHandleclass entirely (search for all references) - Remove
build_package()fromMemberBuilder;build_module()handles packages transparently - Remove
resolve_package()fromReflectionContext;resolve_module()handles packages transparently - Update
MemberKindenum (removeMODULE,PACKAGEvalues if present) - Update cache key generation to use
("module", qualname)uniformly - Outcome: Single ModuleHandle API, all package/module handling unified
Phase 5 โ Scanner Integration & Full Test Coverage:
- Remove
scan_package()fromPackageScanner;scan_module(..., recursive=True)serves both cases - Update
scan_module()to checkis_package and recursivefor recursion logic - Update all test code: replace
PackageHandleโModuleHandle,build_package()โbuild_module(),scan_package()โscan_module(..., recursive=True) - Verify all ownership chains work (module โ type โ method, function โ module)
- Run full test suite; verify zero breaking changes to DI APIs
- Outcome: Integration complete, all tests passing, Phase C unblocked
9.2 Migration Strategy¶
For internal codebase (synchronized across all 5 phases):
Phase 2 terminology change: - Search/replace from loryth.common.reflection.member_info import โ from loryth.common.reflection.handles import - Search/replace ReflectionInfo โ ReflectionHandle globally - Search/replace MemberInfo โ MemberHandle globally - Search/replace ModuleInfo โ ModuleHandle globally - Search/replace TypeInfo โ TypeHandle globally - Search/replace *Info โ *Handle for all info classes globally
Phases 4-5 package consolidation: - Search/replace from loryth.common.reflection import PackageHandle โ Remove import (no longer exported) - Search/replace PackageHandle( โ Becomes ModuleHandle( (consolidation) - Search/replace build_package( โ becomes build_module( (MemberBuilder) - Search/replace resolve_package( โ becomes resolve_module( (ReflectionContext) - Search/replace scan_package( โ becomes scan_module(..., recursive=True) (PackageScanner) - Update type annotations: ModuleHandle | PackageHandle โ just ModuleHandle
For DI consumers:
No migration needed. Internal refactor with zero public API changes: - ReflectionContext method signatures unchanged (except removals) - DI bootstrap behavior unchanged - Autowiring behavior unchanged - Type introspection behavior unchanged
โ ๏ธ 10. Risks and Mitigations¶
| Risk | Mitigation |
|---|---|
| Phase C blocked on Phase I completion | Phase I complete (Oct 26); Phase II (terminology) unblocks Phase III (ownership); Phase V unblocks Phase C |
| Test coverage gaps during refactor | Maintain regression test suite; run full pytest (791+ tests) after each phase completion |
| Incomplete *Handle migration across codebase | Systematic search for "*Info" class references; automated test coverage ensures missed renames surface quickly |
| TypeHandle dual role creates confusion | Clear docstrings explaining member-of-module vs container-for-members aspects; include examples in architecture docs |
| Package reorganization impacts dependencies | Centralized handles/ package; clear init.py exports; no consumer-facing imports change |
| Migration complexity across 5 phases | Each phase has clear, focused outcome; systematic phase progression minimizes coordination overhead |
๐ 11. Progress¶
Legend: โณ Pending ยท ๐ง In Progress ยท โ Completed
- Phase 1 โ Core Hierarchy Simplification
- โ ReflectionInfo abstract base created with common properties
- โ MemberInfo redefined to extend ReflectionInfo (not be root)
- โ All member types updated to extend MemberInfo
- โ Module-level imports updated (backward-compatible)
- โ 791 tests passing with new hierarchy
- Phase 2 โ Terminology Change & Package Restructuring
- โณ handles/ package created with individual files per class
- โณ All *Info classes renamed to *Handle
- โณ Enums moved into handles/ package
- โณ All imports updated across codebase
- โณ member_types/ and member_info.py removed
- โณ 791+ tests passing with new terminology
- Phase 3 โ ModuleHandle Ownership Properties
- โ ModuleHandle.is_package property added
- โ MemberHandle.module property already present (from Phase 1-2)
- โ MethodHandle/FieldHandle/PropertyHandle.type property added
- โ ConstructorHandle.type property alias added
- โ Member builders updated to populate ownership properties
- โ 791 tests passing with ownership properties
- Phase 4 โ PackageHandle Removal & API Consolidation
- โ PackageHandle class removed completely (deleted package_handle.py)
- โ build_package() removed from MemberBuilder, resolve_package() returns ModuleHandle
- โ MemberKind.MODULE and MemberKind.PACKAGE removed from enum
- โ Cache keys updated to ("module", qualname) for unified module/package handling
- โ PackageScanner refactored to discover submodules dynamically
- โ ReflectionContext.scan() checks is_package instead of isinstance(PackageHandle)
- โ All 791 tests passing with unified module/package model
- โ All linting checks pass (ruff, mypy, complexity)
- Phase 5 โ Scanner Integration & Full Test Coverage
- โณ scan_package() method removed from PackageScanner
- โณ scan_module(recursive=True) handles both packages and modules
- โณ All test code migrated (no PackageHandle references)
- โณ Full test suite passing, DI APIs unchanged
๐ 12. Changelog¶
- 2025-10-26 โ Phase 4 completed: PackageHandle removal & API consolidation
- Removed PackageHandle class entirely (deleted handles/package_handle.py)
- Removed build_package() method from MemberBuilder
- Updated resolve_package() to return ModuleHandle (validates path for package check)
- Unified cache keys to ("module", qualname) pattern for all modules/packages
- Removed MemberKind.MODULE and MemberKind.PACKAGE enum values
- Refactored PackageScanner to dynamically discover submodules using pkgutil.iter_modules()
- Updated ReflectionContext to check is_package property instead of PackageHandle type
- Unified Python ImportResolver to use build_module() for both packages and modules
- Updated all protocol definitions to use ModuleHandle instead of PackageHandle
- All 791 tests passing, all linting checks pass
- Unified module/package model simplifies reflection API
- Phase 4 unblocks Phase 5 (scanner integration improvements)
- 2025-10-26 โ Phase 3 completed: Ownership properties implemented
- Added
is_package: boolproperty to ModuleHandle (checks for__path__attribute) - Added
type: TypeHandleproperty to MethodHandle, FieldHandle, PropertyHandle, ConstructorHandle - Updated MemberBuilder to populate ownership properties during construction:
- PropertyHandle receives declaring_type in _process_property_member()
- FieldHandle receives declaring_type in _add_field_to_type() and _process_annotation_only_fields()
- All 791 tests passing with ownership model in place
- Explicit ownership chains now work: module โ type โ method/field/property
- Phase 3 unblocks Phase C (Member Parsing Seam) implementation
- 2025-10-26 โ Phase 1 completed; added Phase 2 terminology & restructuring
- Phase 1 completed: ReflectionInfo/MemberInfo hierarchy established, 791 tests passing
- Added section 2.4: Terminology decision (Handle vs Info/Ref/Proxy) with alternatives table
- Added section 3.4: Package restructuring (handles package, one class per file)
- Inserted Phase 2: Terminology change (*Info โ *Handle) and package restructuring
- Renumbered phases: ownership properties now Phase 3, removal now Phase 4, integration now Phase 5
- Updated progress tracking to show Phase 1 completion
- Updated migration strategy for 5-phase approach
- 2025-10-25 โ Initial draft created (status ๐ต Draft)
- Simplified two-level hierarchy: ReflectionInfo โ ModuleInfo / MemberInfo
- Explicit ownership semantics: module and type properties on MemberInfo
- PackageInfo consolidated into ModuleInfo with is_package property
- Four-phase implementation plan with clear phase boundaries defined
- Integration strategy documented for Phase C unblocking