-
Notifications
You must be signed in to change notification settings - Fork 16
fix: unwrap AppModuleResolver to find activator in self-healing #70
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
The _invalidate_all_install_state() function was looking for _activator directly on the resolver, but when the resolver is an AppModuleResolver, the _activator lives on the wrapped BundleModuleResolver (stored as _bundle). This caused self-healing to always fail with 'No activator found' warning, meaning missing dependency issues would never actually self-heal even though detection worked correctly. Fix by unwrapping AppModuleResolver first, following the same pattern already used in session_spawner.py. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
PR Review: ✅ ApproveThe ProblemThe self-healing mechanism was detecting dependency issues correctly, but the actual fix (invalidating install state) was silently failing. The
The Fix# Before:
activator = getattr(resolver, "_activator", None)
# After:
bundle_resolver = getattr(resolver, "_bundle", resolver) # unwrap if wrapped
activator = getattr(bundle_resolver, "_activator", None)The
Assessment
Ship it. The fix is correct, minimal, and follows established patterns in the codebase. |
|
Is there a better way than accessing protected state in python, this is something easy to break since in foundation this could change even if we constrain to keeping the public surface locked. Maybe we review the proposed refactored change in foundation :microsoft/amplifier-foundation#41 |
Add new principle to PYTHON_BEST_PRACTICES.md addressing the anti-pattern of accessing private attributes via getattr(obj, '_internal', ...). This documents: - Why accessing private state is dangerous - Real example from PR #70 showing the anti-pattern - Correct approaches (expose public API, redesign) - Litmus test for detecting the problem Motivated by microsoft/amplifier-app-cli#70 which demonstrates the anti-pattern spreading across the codebase. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Add 'Accessing Private State (Critical Anti-Pattern)' subsection to DESIGN_PHILOSOPHY.md under Module Development anti-patterns. Documents: - Why getattr(obj, '_private', ...) is dangerous - Code example showing the anti-pattern vs correct approach - Guidance on when internal access seems needed - Litmus test for detecting protocol violations Motivated by microsoft/amplifier-app-cli#70 which shows this anti-pattern spreading with 'Pattern from X' comments. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Add 'Violating Encapsulation' subsection to Module Development anti-patterns in DESIGN_PHILOSOPHY.md. Documents the general principle that accessing internal state instead of using public contracts creates fragile code. The fix for missing APIs is to extend the interface, not bypass it. Motivated by microsoft/amplifier-app-cli#70. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Add bullet to 'In Module Development' anti-patterns about bypassing public interfaces to access internal state. Motivated by microsoft/amplifier-app-cli#70. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Add principle to PYTHON_BEST_PRACTICES.md about not accessing private attributes from outside a class. Includes table of anti-patterns and code example. Motivated by microsoft/amplifier-app-cli#70. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
|
Changes here have been brought into #71 . Closing this PR. |
Add good/bad examples showing why bypassing public interfaces creates fragile code. The fix for missing APIs is to extend the interface, not reach into internal state. Motivated by microsoft/amplifier-app-cli#70. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Add good/bad examples for bypassing vs using public interfaces. Motivated by microsoft/amplifier-app-cli#70. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-Authored-By: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Add good/bad examples for bypassing vs using public interfaces. Motivated by microsoft/amplifier-app-cli#70. 🤖 Generated with [Amplifier](https://github.com/microsoft/amplifier) Co-authored-by: Amplifier <240397093+microsoft-amplifier@users.noreply.github.com>
Summary
_invalidate_all_install_state()to correctly find the activator when the resolver is wrapped in anAppModuleResolverProblem
The self-healing detection was working, but the actual fix (invalidating install state) was always failing silently with "No activator found" warning. This meant missing dependency issues like "tool-web module has a missing aiohttp dependency" would never self-heal.
Root Cause
At line 248-251 of
session_runner.py, the resolver gets wrapped withAppModuleResolver, which stores the originalBundleModuleResolverasself._bundle. However,_invalidate_all_install_state()was looking for_activatordirectly on theAppModuleResolverinstead of unwrapping to get theBundleModuleResolverfirst.Fix
Unwrap
AppModuleResolverto getBundleModuleResolverbefore accessing_activator. This follows the same pattern already used insession_spawner.py.Test plan
AppModuleResolver) and direct (BundleModuleResolver) cases🤖 Generated with Amplifier