Date: 2026-02-15 Feature: Symbol provenance matching in rule engine Status: ✅ Complete
Added support for distinguishing between different import types in the rule engine through SymbolProvenance matching. This enables rules to make decisions based on whether a symbol is defined locally, imported, or imported with an alias (likely re-export).
File: src/exportify/common/types.py
Added provenance field to RuleMatchCriteria:
@dataclass(frozen=True)
class RuleMatchCriteria:
"""Criteria for matching exports."""
name_exact: str | None = None
name_pattern: str | None = None # Regex
module_exact: str | None = None
module_pattern: str | None = None # Regex
member_type: MemberType | None = None
provenance: SymbolProvenance | None = None # NEW
any_of: list[RuleMatchCriteria] | None = None # OR conditions
all_of: list[RuleMatchCriteria] | None = None # AND conditionsFile: src/exportify/export_manager/rules.py
def _matches_criteria(
self, symbol: DetectedSymbol, module_path: str, criteria: RuleMatchCriteria
) -> bool:
# ... existing checks ...
if criteria.provenance and symbol.provenance != criteria.provenance:
return False
return TrueModified _parse_rule() and _parse_criteria() to parse provenance from YAML:
provenance=SymbolProvenance(match_data["provenance"]) if "provenance" in match_data else Nonefrom exportify.common.types import (
# ... existing imports ...
SymbolProvenance, # NEW
)File: src/exportify/rules/README.md
match:
name_exact: "SymbolName"
name_pattern: "^public_.*"
module_exact: "mypackage.core"
module_pattern: ".*\\.types$"
member_type: class
provenance: imported # NEWDocuments the four provenance types:
defined_here: Defined in the current moduleimported: Regular imports (from x import y)alias_imported: Imports with aliases (from x import y as z)unknown: Provenance cannot be determined
The section about provenance not being supported was removed as this feature is now implemented.
rules:
- name: "ExportAliasedImportsOnly"
priority: 600
description: "Export imports with aliases (likely intentional re-exports)"
match:
all_of:
- member_type: imported
- provenance: alias_imported
action: include
propagate: parentFile: tests/test_rules.py
Created test suite with 10 test cases covering:
test_match_defined_here_provenance- Match symbols defined locallytest_match_imported_provenance- Match regular importstest_match_alias_imported_provenance- Match aliased imports (re-exports)test_provenance_with_other_criteria- Combine provenance with member_typetest_provenance_with_any_of- Provenance in OR conditionstest_provenance_priority_order- Verify priority system works with provenancetest_no_provenance_criteria_matches_all- Rules without provenance match all
test_load_rule_with_provenance- Load rules with provenance from YAMLtest_invalid_provenance_value- Reject invalid provenance valuestest_nested_provenance_in_any_of- Nested provenance in any_of/all_of
All tests pass ✅
- name: "ExcludeRegularImports"
priority: 500
match:
provenance: imported # Regular imports without aliases
action: exclude- name: "ExportAliasedImports"
priority: 600
match:
provenance: alias_imported # Imports with aliases
action: include
propagate: parent- name: "ExportDefinedClasses"
priority: 700
match:
all_of:
- member_type: class
- provenance: defined_here # Only locally defined
action: include
propagate: parent- name: "ExportDefinedOrAliased"
priority: 650
match:
any_of:
- provenance: defined_here
- provenance: alias_imported
action: include
propagate: parent- Precise Control: Rules can now distinguish between different import types
- Re-export Detection: Identify likely re-exports (aliased imports) automatically
- Backward Compatible: Existing rules without provenance continue to work
- Composable: Provenance works with all other criteria (member_type, patterns, etc.)
- Well-Tested: Comprehensive test suite ensures correctness
The AST parser (src/exportify/analysis/ast_parser.py) already sets provenance correctly:
- DEFINED_HERE: Classes, functions, constants, variables defined in the file
- IMPORTED: Regular imports (
from x import y) - ALIAS_IMPORTED: Aliased imports (
from x import y as z)
The rule engine now uses this information for matching.
✅ Type checking passes ✅ All 10 tests pass ✅ Documentation updated ✅ Example rules provided ✅ Backward compatible
While the core feature is complete, potential future enhancements:
- Stdlib Detection: Add provenance filter for stdlib vs third-party imports
- Provenance Patterns: Support regex patterns for original_source matching
- Composite Filters: Combine provenance with import path patterns
- Default Rule Updates: Update default_rules.yaml to leverage provenance
src/exportify/common/types.py- Type definitionssrc/exportify/export_manager/rules.py- Rule enginesrc/exportify/analysis/ast_parser.py- AST parser (sets provenance)src/exportify/rules/README.md- Rule schema documentationtests/test_rules.py- Test suite
From exportify-requirements-v1.0.md Section 4:
Rules need to check
SymbolProvenanceto distinguish:
DEFINED_HERE- symbols defined in the moduleIMPORTED- regular importsALIASED_IMPORT- imports with aliases (likely re-exports)
✅ All requirements met. The rule engine can now check provenance to distinguish between these three types.