gabriel / muse public
feat minor feat/framework-entry-points #1 / 1
AI Agent gabriel · 171 days ago · Apr 10, 2026 · Diff

feat: add framework entry-point detection system

Introduces an abstract FrameworkPlugin protocol and built-in plugins for FastAPI, Flask, and Celery. Framework-wired symbols (route handlers, tasks, lifecycle hooks) are now treated as implicit entry points rather than dead code.

Changes: - muse/plugins/code/_framework.py (new): ImplicitEntryEdge dataclass, FrameworkPlugin protocol, _FastAPIPlugin / _FlaskPlugin / _CeleryPlugin, _CustomPatternPlugin (user-defined decorator rules), build_implicit_edge_graph, load_framework_config (.muse/code_config.toml support) - muse/plugins/code/_callgraph.py: re-exports ImplicitEntryEdge, ImplicitEdgeGraph, build_implicit_edge_graph for caller convenience - muse/cli/commands/impact.py: surfaces entry-point annotations in text and JSON output when a symbol has no explicit callers but is framework-wired - muse/cli/commands/dead.py: excludes framework entry-point addresses from dead-code candidates; imports Manifest from canonical muse.core._types - tests/test_framework_plugins.py (new): 65 unit + integration tests covering all three built-in plugins, _CustomPatternPlugin, build_implicit_edge_graph, load_framework_config, and dead-code exclusion integration

Zero mypy errors in changed files. Zero typing_audit violations.

sha256:1b06c30c3c1ef33d54b84efed3ab027d0ddc2cb89af9d46b2bdd79687a038c56 sha
+156 ~10 symbols
sha256:146791ee76bbd0922f73ff18dc2c7215d801e7cdd91793c6103766f25fe07c82 snapshot
+156
symbols added
~10
symbols modified
0
dead code introduced
Semantic Changes 166 symbols
~ muse/plugins/code/_framework.py .py 44 symbols added
+ BUILTIN_PLUGINS variable variable BUILTIN_PLUGINS L550–554
+ FrameworkConfig class class FrameworkConfig L168–177
+ FrameworkPlugin class class FrameworkPlugin L106–150
+ detect_entry_points method method detect_entry_points L128–150
+ framework_id method method framework_id L124–126
+ language method method language L119–121
+ ImplicitEdgeGraph variable variable ImplicitEdgeGraph L97–97
+ ImplicitEntryEdge class class ImplicitEntryEdge L74–91
+ _CeleryPlugin class class _CeleryPlugin L454–546
+ _edge_from_decorator method method _edge_from_decorator L495–546
+ detect_entry_points method method detect_entry_points L472–493
+ _CustomEntryPointRule class class _CustomEntryPointRule L159–164
+ _CustomPatternPlugin class class _CustomPatternPlugin L225–270
+ __init__ method method __init__ L231–232
+ detect_entry_points method method detect_entry_points L234–270
+ _FastAPIPlugin class class _FastAPIPlugin L278–354
+ _edge_from_decorator method method _edge_from_decorator L322–354
+ detect_entry_points method method detect_entry_points L299–320
+ _FlaskPlugin class class _FlaskPlugin L357–451
+ _edge_from_decorator method method _edge_from_decorator L402–451
+ detect_entry_points method method detect_entry_points L379–400
+ _Metadata variable variable _Metadata L70–70
+ _PY_SUFFIXES variable variable _PY_SUFFIXES L562–562
+ _decorator_bare_name function function _decorator_bare_name L669–679
+ _extract_methods_kwarg method function _extract_methods_kwarg L682–691
+ _resolve_address function function _resolve_address L647–666
+ build_implicit_edge_graph function function build_implicit_edge_graph L565–639
+ MAX_AST_BYTES import import MAX_AST_BYTES L59–59
+ Manifest import import Manifest L56–56
+ Protocol import import Protocol L54–54
+ SymbolCache import import SymbolCache L58–58
+ SymbolTree import import SymbolTree L60–60
+ annotations import import annotations L47–47
+ ast import import ast L49–49
+ dataclass class import dataclass L53–53
+ field import import field L53–53
+ logging import import logging L50–50
+ parse_symbols import import parse_symbols L60–60
+ pathlib import import pathlib L51–51
+ read_object import import read_object L57–57
+ runtime_checkable import import runtime_checkable L54–54
+ tomllib import import tomllib L52–52
+ load_framework_config function function load_framework_config L180–217
+ logger variable variable logger L62–62
~ tests/test_framework_plugins.py .py 102 symbols added
+ TestBuildImplicitEdgeGraph class class TestBuildImplicitEdgeGraph L602–732
+ test_all_disabled_returns_empty method method test_all_disabled_returns_empty L710–718
+ test_auto_detect_false_returns_empty method method test_auto_detect_false_returns_empty L653–661
+ test_celery_task_detected method method test_celery_task_detected L618–626
+ test_custom_rule_in_config method method test_custom_rule_in_config L694–708
+ test_disabled_plugins_excluded method method test_disabled_plugins_excluded L663–673
+ test_empty_manifest_returns_empty method method test_empty_manifest_returns_empty L603–606
+ test_fastapi_handler_detected method method test_fastapi_handler_detected L608–616
+ test_flask_handler_detected method method test_flask_handler_detected L628–636
+ test_graph_maps_address_to_edges method method test_graph_maps_address_to_edges L720–732
+ test_multiple_files_multiple_plugins method method test_multiple_files_multiple_plugins L675–692
+ test_non_python_file_ignored method method test_non_python_file_ignored L638–643
+ test_syntax_error_file_skipped_gracefully method method test_syntax_error_file_skipped_gracefully L645–651
+ TestCeleryPlugin class class TestCeleryPlugin L415–495
+ _edges method method _edges L418–420
+ test_app_task_bare_attribute method method test_app_task_bare_attribute L441–448
+ test_app_task_called_attribute method method test_app_task_called_attribute L450–457
+ test_framework_id_is_celery method method test_framework_id_is_celery L489–495
+ test_periodic_task_bare method method test_periodic_task_bare L468–475
+ test_periodic_task_called method method test_periodic_task_called L459–466
+ test_shared_task_bare_name method method test_shared_task_bare_name L422–430
+ test_shared_task_called method method test_shared_task_called L432–439
+ test_syntax_error_returns_empty method method test_syntax_error_returns_empty L485–487
+ test_unrelated_decorator_ignored method method test_unrelated_decorator_ignored L477–483
+ TestCustomPatternPlugin class class TestCustomPatternPlugin L503–594
+ _make_plugin method method _make_plugin L504–505
+ test_called_form_also_recognised method method test_called_form_also_recognised L524–538
+ test_custom_decorator_produces_edge method method test_custom_decorator_produces_edge L507–522
+ test_empty_rules_returns_empty method method test_empty_rules_returns_empty L571–579
+ test_metadata_includes_decorator_name method method test_metadata_includes_decorator_name L581–594
+ test_non_matching_decorator_ignored method method test_non_matching_decorator_ignored L540–553
+ test_non_python_rules_excluded method method test_non_python_rules_excluded L555–569
+ TestDeadCodeIntegration class class TestDeadCodeIntegration L832–872
+ test_celery_task_excluded_from_dead method method test_celery_task_excluded_from_dead L849–859
+ test_fastapi_handler_excluded_from_dead method method test_fastapi_handler_excluded_from_dead L835–847
+ test_plain_function_not_mistaken_for_entry_point method method test_plain_function_not_mistaken_for_entry_point L861–872
+ TestFastAPIPlugin class class TestFastAPIPlugin L174–309
+ _edges method method _edges L177–179
+ test_address_uses_file_path method method test_address_uses_file_path L303–309
+ test_bare_decorator_not_a_call_ignored method method test_bare_decorator_not_a_call_ignored L261–268
+ test_get_route method method test_get_route L181–192
+ test_head_options_trace method method test_head_options_trace L219–234
+ test_method_stored_uppercase method method test_method_stored_uppercase L295–301
+ test_multiple_routes_in_one_file method method test_multiple_routes_in_one_file L284–293
+ test_no_path_arg_gives_empty_string method method test_no_path_arg_gives_empty_string L244–251
+ test_non_http_attribute_ignored method method test_non_http_attribute_ignored L253–259
+ test_path_extracted_from_first_arg method method test_path_extracted_from_first_arg L236–242
+ test_post_route method method test_post_route L194–202
+ test_put_delete_patch_routes method method test_put_delete_patch_routes L204–217
+ test_symbol_not_in_tree_returns_no_edge method method test_symbol_not_in_tree_returns_no_edge L275–282
+ test_syntax_error_returns_empty method method test_syntax_error_returns_empty L270–273
+ TestFlaskPlugin class class TestFlaskPlugin L317–407
+ _edges method method _edges L320–322
+ test_after_request method method test_after_request L371–377
+ test_before_request_bare method method test_before_request_bare L352–360
+ test_before_request_called method method test_before_request_called L362–369
+ test_errorhandler method method test_errorhandler L379–386
+ test_framework_id_is_flask method method test_framework_id_is_flask L401–407
+ test_non_flask_attribute_ignored method method test_non_flask_attribute_ignored L388–394
+ test_route_no_methods_defaults_to_get method method test_route_no_methods_defaults_to_get L335–342
+ test_route_path_extracted method method test_route_path_extracted L344–350
+ test_route_with_methods_kwarg method method test_route_with_methods_kwarg L324–333
+ test_syntax_error_returns_empty method method test_syntax_error_returns_empty L396–399
+ TestFrameworkPluginProtocol class class TestFrameworkPluginProtocol L157–166
+ test_builtin_plugins_satisfy_protocol method method test_builtin_plugins_satisfy_protocol L158–162
+ test_custom_pattern_plugin_satisfies_protocol method method test_custom_pattern_plugin_satisfies_protocol L164–166
+ TestImplicitEntryEdge class class TestImplicitEntryEdge L125–149
+ test_default_metadata_is_empty_dict method method test_default_metadata_is_empty_dict L143–149
+ test_equality_uses_all_fields method method test_equality_uses_all_fields L136–141
+ test_is_frozen method method test_is_frozen L126–134
+ TestLoadFrameworkConfig class class TestLoadFrameworkConfig L740–824
+ test_auto_detect_false_parsed method method test_auto_detect_false_parsed L768–775
+ test_disabled_plugins_stored_as_frozenset method method test_disabled_plugins_stored_as_frozenset L793–802
+ test_malformed_toml_returns_defaults method method test_malformed_toml_returns_defaults L785–791
+ test_missing_file_returns_defaults method method test_missing_file_returns_defaults L741–745
+ test_missing_section_returns_defaults method method test_missing_section_returns_defaults L777–783
+ test_multiple_custom_rules method method test_multiple_custom_rules L804–824
+ test_valid_toml_parsed method method test_valid_toml_parsed L747–766
+ _SourceMap variable variable _SourceMap L101–101
+ _sym_tree function function _sym_tree L115–117
+ _write_snapshot function function _write_snapshot L104–112
+ BUILTIN_PLUGINS import import BUILTIN_PLUGINS L81–81
+ FrameworkConfig import import FrameworkConfig L81–81
+ FrameworkPlugin import import FrameworkPlugin L81–81
+ ImplicitEntryEdge import import ImplicitEntryEdge L81–81
+ Manifest import import Manifest L79–79
+ SymbolTree import import SymbolTree L80–80
+ _CeleryPlugin import import _CeleryPlugin L81–81
+ _CustomEntryPointRule import import _CustomEntryPointRule L81–81
+ _CustomPatternPlugin import import _CustomPatternPlugin L81–81
+ _FastAPIPlugin import import _FastAPIPlugin L81–81
+ _FlaskPlugin import import _FlaskPlugin L81–81
+ annotations import import annotations L69–69
+ build_implicit_edge_graph import import build_implicit_edge_graph L81–81
+ dataclasses class import dataclasses L71–71
+ hashlib import import hashlib L72–72
+ load_framework_config import import load_framework_config L81–81
+ parse_symbols import import parse_symbols L80–80
+ pathlib import import pathlib L73–73
+ pytest import import pytest L76–76
+ textwrap import import textwrap L74–74
+ write_object import import write_object L78–78
~ muse/cli/commands/dead.py .py 2 symbols added, 7 symbols modified
+ ImplicitEdgeGraph import import ImplicitEdgeGraph L151–151
+ build_implicit_edge_graph import import build_implicit_edge_graph L151–151
~ run
~ muse/cli/commands/impact.py .py 5 symbols added, 3 symbols modified
+ _EntryPointJson class class _EntryPointJson L122–127
+ _StrMeta variable variable _StrMeta L118–118
+ ImplicitEdgeGraph import import ImplicitEdgeGraph L108–108
+ ImplicitEntryEdge import import ImplicitEntryEdge L108–108
+ build_implicit_edge_graph import import build_implicit_edge_graph L108–108
~ run
~ muse/plugins/code/_callgraph.py .py 3 symbols added
+ ImplicitEdgeGraph import import ImplicitEdgeGraph L43–43
+ ImplicitEntryEdge import import ImplicitEntryEdge L43–43
+ build_implicit_edge_graph import import build_implicit_edge_graph L43–43
← Older Oldest on feat/framework-entry-points
All commits
Newer → Latest on feat/framework-entry-points

0 comments

No comments yet. Be the first to start the discussion.

To add a comment, use the Muse CLI: muse hub commit comment sha256:1b06c30c3c1ef33d54b84efed3ab027d0ddc2cb89af9d46b2bdd79687a038c56 --body "your comment"