capcat.core.source_system.remove_source_command
File: Application/capcat/core/source_system/remove_source_command.py
Description
Base classes and implementations for the remove-source command.
Provides the command pattern foundation used by EnhancedRemoveCommand and RemoveSourceService:
- SourceRemovalInfo: value object describing a source to be removed
- RemovalUserInterface: ABC for UI interactions (enables testability)
- RemoveSourceCommand: orchestrates selection → confirmation → removal
- RegistrySourceLister: queries SourceRegistry for available sources
- RegistrySourceInfoProvider: builds SourceRemovalInfo from config files
- FileSystemConfigRemover: deletes YAML config files from disk
- BundleManagerUpdater: removes the source from bundles.yml
Classes
SourceRemovalInfo
Describes a source that is about to be removed.
Attributes:
source_id: Machine-readable source identifier (e.g. "hn").
display_name: Human-readable name shown in the UI.
config_path: Filesystem path to the source YAML config file.
bundles: List of bundle names that include this source.
RemovalUserInterface
Inherits from: abc.ABC
Abstract interface for all UI interactions during source removal.
Concrete implementations live in removal_ui.py (questionary-based) and tests/fixtures (mock-based).
Methods
select_sources_to_remove
def select_sources_to_remove(self, sources: List[tuple]) -> List[str]
Prompt the user to select one or more sources.
Args:
sources: List of (source_id, display_name) tuples.
Returns: List of selected source IDs.
Parameters:
selfsources(List[tuple])
Returns: List[str]
show_removal_summary
def show_removal_summary(self, sources_info: List[SourceRemovalInfo]) -> None
Show a summary of what will be removed before confirming.
Args: sources_info: Sources scheduled for removal.
Parameters:
selfsources_info(List[SourceRemovalInfo])
Returns: None
confirm_removal
def confirm_removal(self, sources_info: List[SourceRemovalInfo]) -> bool
Ask the user to confirm the removal.
Args: sources_info: Sources scheduled for removal.
Returns:
True if the user confirmed, False to abort.
Parameters:
selfsources_info(List[SourceRemovalInfo])
Returns: bool
show_success
def show_success(self, message: str) -> None
Display a success notification.
Parameters:
selfmessage(str)
Returns: None
show_error
def show_error(self, message: str) -> None
Display an error notification.
Parameters:
selfmessage(str)
Returns: None
show_info
def show_info(self, message: str) -> None
Display an informational message.
Parameters:
selfmessage(str)
Returns: None
SourceLister
Inherits from: abc.ABC
Returns the list of removable sources as (source_id, display_name) pairs.
Methods
list_sources
def list_sources(self) -> List[tuple]
Return [(source_id, display_name), ...] for every available source.
Parameters:
self
Returns: List[tuple]
SourceInfoProvider
Inherits from: abc.ABC
Builds SourceRemovalInfo objects for a list of source IDs.
Methods
get_sources_info
def get_sources_info(self, source_ids: List[str]) -> List[SourceRemovalInfo]
Return removal metadata for each requested source_id.
Args: source_ids: IDs returned by the UI selection step.
Returns: One SourceRemovalInfo per valid source ID.
Parameters:
selfsource_ids(List[str])
Returns: List[SourceRemovalInfo]
ConfigRemover
Inherits from: abc.ABC
Deletes a source’s config file from disk.
Methods
remove_config
def remove_config(self, source_info: SourceRemovalInfo) -> None
Remove the config file described by source_info.
Args: source_info: Removal metadata including the config path.
Parameters:
selfsource_info(SourceRemovalInfo)
Returns: None
BundleUpdater
Inherits from: abc.ABC
Removes a source ID from all bundles in bundles.yml.
Methods
remove_from_bundles
def remove_from_bundles(self, source_id: str) -> None
Strip source_id from every bundle that contains it.
Args: source_id: The source to remove from bundles.
Parameters:
selfsource_id(str)
Returns: None
RemoveSourceCommand
Orchestrates the interactive remove-source workflow.
Steps:
- List available sources via source_lister.
- Let the user select via ui.
- Gather removal info via source_info_provider.
- Show summary and ask for confirmation via ui.
- Delete config via config_remover.
- Update bundles via bundle_updater.
Args:
source_lister: Provides [(source_id, display_name)].
source_info_provider: Builds SourceRemovalInfo objects.
ui: User-facing interaction layer.
config_remover: Removes YAML config files.
bundle_updater: Keeps bundles.yml consistent.
logger: Optional logger; defaults to module logger.
Methods
init
def __init__(self, source_lister: SourceLister, source_info_provider: SourceInfoProvider, ui: RemovalUserInterface, config_remover: ConfigRemover, bundle_updater: BundleUpdater, logger: Optional[Any] = None) -> None
Parameters:
selfsource_lister(SourceLister)source_info_provider(SourceInfoProvider)ui(RemovalUserInterface)config_remover(ConfigRemover)bundle_updater(BundleUpdater)logger(Optional[Any]) optional
Returns: None
execute
def execute(self) -> None
Run the full interactive removal workflow.
Parameters:
self
Returns: None
_remove_sources
def _remove_sources(self, sources_info: List['SourceRemovalInfo']) -> None
Remove a list of sources (config + bundles) without UI interaction.
Used by EnhancedRemoveCommand after confirmation has been handled.
Args: sources_info: Sources to remove.
Parameters:
selfsources_info(List[‘SourceRemovalInfo’])
Returns: None
_refresh_registry
def _refresh_registry(self) -> None
Reset the global source registry so it reflects removed sources.
Parameters:
self
Returns: None
RegistrySourceLister
Inherits from: SourceLister
Lists sources via SourceRegistry - reads the live registry at call time.
Methods
list_sources
def list_sources(self) -> List[tuple]
Return [(source_id, display_name)] for every user-removable source.
Builtin sources (shipped with the application) are excluded - they cannot be removed and would silently no-op if the removal were attempted.
Parameters:
self
Returns: List[tuple]
RegistrySourceInfoProvider
Inherits from: SourceInfoProvider
Builds SourceRemovalInfo by inspecting config files and bundles.yml.
Args: config_path: Directory containing per-source YAML config files. bundles_path: Path to bundles.yml.
Methods
init
def __init__(self, config_path: Path, bundles_path: Path) -> None
Parameters:
selfconfig_path(Path)bundles_path(Path)
Returns: None
get_sources_info
def get_sources_info(self, source_ids: List[str]) -> List[SourceRemovalInfo]
Build SourceRemovalInfo for each source_id.
Args: source_ids: IDs selected for removal.
Returns: List of SourceRemovalInfo, one per valid ID.
Parameters:
selfsource_ids(List[str])
Returns: List[SourceRemovalInfo]
_load_bundles_map
def _load_bundles_map(self) -> dict
Return {source_id: [bundle_name, ...]} from bundles.yml.
Parameters:
self
Returns: dict
FileSystemConfigRemover
Inherits from: ConfigRemover
Deletes the YAML config file for a source from disk.
Methods
remove_config
def remove_config(self, source_info: SourceRemovalInfo) -> None
Delete the config file if it exists.
Args: source_info: Contains the config_path to delete.
Parameters:
selfsource_info(SourceRemovalInfo)
Returns: None
BundleManagerUpdater
Inherits from: BundleUpdater
Removes a source from all entries in bundles.yml.
Args: bundles_path: Path to bundles.yml.
Methods
init
def __init__(self, bundles_path: Path) -> None
Parameters:
selfbundles_path(Path)
Returns: None
remove_from_bundles
def remove_from_bundles(self, source_id: str) -> None
Strip source_id from every bundle in bundles.yml.
Args: source_id: Source to remove from all bundles.
Parameters:
selfsource_id(str)
Returns: None