Source code for paper_firehose.core.command_context
"""
Command context for shared initialization across CLI commands.
Provides a unified way to initialize config, database, and common parameters
to reduce boilerplate code in command implementations.
"""
from __future__ import annotations
import logging
from typing import Optional, Dict, Any
from .config import ConfigManager
from .database import DatabaseManager
logger = logging.getLogger(__name__)
[docs]
class CommandContext:
"""Encapsulates shared initialization logic for CLI commands.
Handles config loading, validation, database connection, and topic resolution
in a single reusable class to reduce boilerplate across commands.
Example:
```python
ctx = CommandContext(config_path)
for topic in ctx.get_topics(topic_arg):
# Access ctx.config_manager, ctx.config, ctx.db
entries = ctx.db.get_current_entries(topic=topic)
```
"""
def __init__(self, config_path: Optional[str] = None):
"""Initialize command context with config and database.
Args:
config_path: Path to main config file (None = use default)
Raises:
ValueError: If configuration is invalid
"""
self.config_manager = ConfigManager(config_path)
# Validate configuration
if not self.config_manager.validate_config():
raise ValueError("Invalid configuration. Run 'paper-firehose status' for details.")
self.config = self.config_manager.load_config()
self.db = DatabaseManager(self.config)
logger.debug(f"CommandContext initialized with config from {self.config_manager.config_path}")
[docs]
def get_topics(self, topic: Optional[str] = None) -> list[str]:
"""Resolve topic argument to list of topics to process.
Args:
topic: Optional single topic name, or None for all topics
Returns:
List of topic names (single topic or all available topics)
"""
if topic:
return [topic]
return self.config_manager.get_available_topics()
[docs]
def load_topic_config(self, topic: str) -> Dict[str, Any]:
"""Load configuration for a specific topic.
Args:
topic: Topic name
Returns:
Topic configuration dictionary
Raises:
FileNotFoundError: If topic config file doesn't exist
"""
return self.config_manager.load_topic_config(topic)
[docs]
def get_default(self, key: str, default: Any = None) -> Any:
"""Get value from defaults section of config.
Args:
key: Config key (e.g., 'rank_threshold')
default: Fallback value if key not found
Returns:
Config value or default
"""
defaults = self.config.get('defaults') or {}
return defaults.get(key, default)
[docs]
def get_nested_default(self, *keys: str, default: Any = None) -> Any:
"""Get nested value from defaults section.
Args:
*keys: Nested keys (e.g., 'abstracts', 'mailto')
default: Fallback value if path not found
Returns:
Config value or default
Example:
```python
# Get config.defaults.abstracts.mailto
mailto = ctx.get_nested_default('abstracts', 'mailto', default='noreply@example.com')
```
"""
defaults = self.config.get('defaults') or {}
value = defaults
for key in keys:
if isinstance(value, dict):
value = value.get(key)
else:
return default
return value if value is not None else default
def __enter__(self):
"""Context manager entry."""
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Context manager exit - cleanup resources."""
# DatabaseManager handles its own cleanup via context managers
# No explicit cleanup needed here
pass