File size: 5,536 Bytes
f56729d ff73b92 f56729d ff73b92 f56729d ff73b92 f56729d 45ac00a ff73b92 f56729d ff73b92 f56729d 45ac00a f56729d ff73b92 f56729d ff73b92 f56729d 45ac00a ff73b92 f56729d ff73b92 45ac00a f56729d ff73b92 f56729d ff73b92 f56729d 45ac00a f56729d 45ac00a f56729d |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 |
"""
Interactive shell for the Folio CLI.
This module provides the main entry point for the Folio CLI interactive shell.
"""
import argparse
import os
import traceback
from prompt_toolkit import PromptSession
from prompt_toolkit.completion import NestedCompleter
from prompt_toolkit.history import FileHistory
from prompt_toolkit.shortcuts import confirm
from rich.console import Console
from src.focli.commands import execute_command, get_command_registry
from src.focli.commands.simulate import simulate_command
from src.focli.utils import load_portfolio
def create_completer():
"""Create a nested completer for command auto-completion.
Returns:
NestedCompleter for command auto-completion
"""
# Build a nested completer from the command registry
commands = get_command_registry()
# Create completion dictionary with subcommands and parameters
completion_dict = {}
for cmd_name, cmd_info in commands.items():
if cmd_info.get("subcommands"):
completion_dict[cmd_name] = {
subcmd: None for subcmd in cmd_info["subcommands"]
}
else:
completion_dict[cmd_name] = None
return NestedCompleter.from_nested_dict(completion_dict)
def initialize_state():
"""Initialize the application state.
Returns:
Dictionary containing the initial application state
"""
return {
"portfolio_groups": None,
"portfolio_summary": None,
"loaded_portfolio": None,
"last_simulation": None,
"simulation_history": [],
"last_position": None,
"position_simulations": {},
"filtered_groups": None,
"simulation_presets": {
"default": {"range": 20.0, "steps": 13},
"detailed": {"range": 20.0, "steps": 21, "detailed": True},
"quick": {"range": 10.0, "steps": 5},
},
"command_history": [],
}
def load_default_portfolio(state, console):
"""Try to load the default portfolio.
Args:
state: Application state
console: Rich console for output
Returns:
True if portfolio was loaded successfully, False otherwise
"""
default_portfolio = "private-data/portfolio-private.csv"
try:
load_portfolio(default_portfolio, state, console)
return True
except Exception as e:
console.print(f"[yellow]Could not load default portfolio: {e}[/yellow]")
console.print(
"[yellow]Use 'portfolio load <path>' to load a portfolio.[/yellow]"
)
return False
def main():
"""Main entry point for the Folio CLI."""
# Parse command-line arguments
parser = argparse.ArgumentParser(description="Folio CLI")
parser.add_argument(
"--simulate", action="store_true", help="Run simulation directly"
)
parser.add_argument(
"--preset", type=str, help="Simulation preset to use (default, quick, detailed)"
)
args = parser.parse_args()
console = Console()
# Initialize application state
state = initialize_state()
# If direct simulation is requested
if args.simulate:
console.print("[bold cyan]Folio CLI - Direct Simulation[/bold cyan]")
# Try to load default portfolio
if load_default_portfolio(state, console):
# Run simulation with optional preset
sim_args = []
if args.preset:
sim_args = ["-p", args.preset]
# Execute simulation command
simulate_command(sim_args, state, console)
return
else:
console.print(
"[bold red]Error:[/bold red] Cannot run simulation without a portfolio."
)
console.print(
"Please run the CLI without --simulate to load a portfolio first."
)
return
# Regular interactive mode
console.print("[bold cyan]Folio Interactive Shell[/bold cyan]")
console.print("Type 'help' for available commands.")
# Create history file in user's home directory
history_file = os.path.expanduser("~/.folio_history")
# Create session with auto-completion and history
session = PromptSession(
completer=create_completer(), history=FileHistory(history_file)
)
# Try to load default portfolio
load_default_portfolio(state, console)
# Main REPL loop
while True:
try:
# Get user input
text = session.prompt("folio> ")
if not text.strip():
continue
# Handle exit command directly
if text.strip().lower() == "exit":
if confirm_exit():
break
continue
# Execute the command
execute_command(text, state, console)
# Add to command history
state["command_history"].append(text)
except KeyboardInterrupt:
# Handle Ctrl+C
console.print("[yellow]Use 'exit' to exit the application.[/yellow]")
except EOFError:
# Handle Ctrl+D
break
except Exception as e:
# Handle other exceptions
console.print(f"[bold red]Error:[/bold red] {e}")
console.print(traceback.format_exc())
def confirm_exit():
"""Confirm exit with the user.
Returns:
True if the user confirms, False otherwise
"""
return confirm("Are you sure you want to exit?")
if __name__ == "__main__":
main()
|