mirror of
https://github.com/Benexl/FastAnime.git
synced 2025-12-12 15:50:01 -08:00
feat: single source of app level constants
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import importlib.metadata
|
||||
import sys
|
||||
|
||||
if sys.version_info < (3, 10):
|
||||
@@ -7,14 +6,6 @@ if sys.version_info < (3, 10):
|
||||
)
|
||||
|
||||
|
||||
__version__ = importlib.metadata.version("FastAnime")
|
||||
|
||||
APP_NAME = "FastAnime"
|
||||
AUTHOR = "Benexl"
|
||||
GIT_REPO = "github.com"
|
||||
REPO = f"{GIT_REPO}/{AUTHOR}/{APP_NAME}"
|
||||
|
||||
|
||||
def FastAnime():
|
||||
from .cli import run_cli
|
||||
|
||||
|
||||
@@ -3,9 +3,8 @@ from typing import TYPE_CHECKING
|
||||
import click
|
||||
from click.core import ParameterSource
|
||||
|
||||
from .. import __version__
|
||||
from ..core.config import AppConfig
|
||||
from ..core.constants import PROJECT_NAME, USER_CONFIG_PATH
|
||||
from ..core.constants import PROJECT_NAME, USER_CONFIG_PATH, __version__
|
||||
from .config import ConfigLoader
|
||||
from .options import options_from_model
|
||||
from .utils.exceptions import setup_exceptions_handler
|
||||
@@ -24,6 +23,7 @@ if TYPE_CHECKING:
|
||||
dev: bool | None
|
||||
log: bool | None
|
||||
rich_traceback: bool | None
|
||||
rich_traceback_theme: str
|
||||
|
||||
|
||||
commands = {
|
||||
@@ -39,38 +39,22 @@ commands = {
|
||||
context_settings=dict(auto_envvar_prefix=PROJECT_NAME),
|
||||
)
|
||||
@click.version_option(__version__, "--version")
|
||||
@click.option("--no-config", is_flag=True, help="Don't load the user config file.")
|
||||
@click.option(
|
||||
"--no-config",
|
||||
is_flag=True,
|
||||
help="Don't load the user config file.",
|
||||
envvar=f"{PROJECT_NAME}_NO_CONFIG",
|
||||
)
|
||||
@click.option(
|
||||
"--trace",
|
||||
is_flag=True,
|
||||
help="Controls Whether to display tracebacks or not",
|
||||
envvar=f"{PROJECT_NAME}_TRACE",
|
||||
)
|
||||
@click.option(
|
||||
"--dev",
|
||||
is_flag=True,
|
||||
help="Controls Whether the app is in dev mode",
|
||||
envvar=f"{PROJECT_NAME}_DEV",
|
||||
)
|
||||
@click.option(
|
||||
"--log", is_flag=True, help="Controls Whether to log", envvar=f"{PROJECT_NAME}_LOG"
|
||||
)
|
||||
@click.option(
|
||||
"--log-to-file",
|
||||
is_flag=True,
|
||||
help="Controls Whether to log to a file",
|
||||
envvar=f"{PROJECT_NAME}_LOG_TO_FILE",
|
||||
"--trace", is_flag=True, help="Controls Whether to display tracebacks or not"
|
||||
)
|
||||
@click.option("--dev", is_flag=True, help="Controls Whether the app is in dev mode")
|
||||
@click.option("--log", is_flag=True, help="Controls Whether to log")
|
||||
@click.option("--log-to-file", is_flag=True, help="Controls Whether to log to a file")
|
||||
@click.option(
|
||||
"--rich-traceback",
|
||||
is_flag=True,
|
||||
help="Controls Whether to display a rich traceback",
|
||||
envvar=f"{PROJECT_NAME}_LOG_TO_FILE",
|
||||
)
|
||||
@click.option(
|
||||
"--rich-traceback-theme",
|
||||
default="github-dark",
|
||||
help="Controls Whether to display a rich traceback",
|
||||
)
|
||||
@options_from_model(AppConfig)
|
||||
@click.pass_context
|
||||
@@ -78,12 +62,13 @@ def cli(ctx: click.Context, **options: "Unpack[Options]"):
|
||||
"""
|
||||
The main entry point for the FastAnime CLI.
|
||||
"""
|
||||
setup_logging(
|
||||
options["log"],
|
||||
options["log_to_file"],
|
||||
setup_logging(options["log"], options["log_to_file"])
|
||||
setup_exceptions_handler(
|
||||
options["trace"],
|
||||
options["dev"],
|
||||
options["rich_traceback"],
|
||||
options["rich_traceback_theme"],
|
||||
)
|
||||
setup_exceptions_handler(options["trace"], options["dev"])
|
||||
|
||||
loader = ConfigLoader(config_path=USER_CONFIG_PATH)
|
||||
config = AppConfig.model_validate({}) if options["no_config"] else loader.load()
|
||||
|
||||
@@ -113,8 +113,13 @@ def _generate_desktop_entry():
|
||||
from rich import print
|
||||
from rich.prompt import Confirm
|
||||
|
||||
from ... import __version__
|
||||
from ...core.constants import ICON_PATH, PLATFORM, PROJECT_NAME, USER_APPLICATIONS
|
||||
from ...core.constants import (
|
||||
ICON_PATH,
|
||||
PLATFORM,
|
||||
PROJECT_NAME,
|
||||
USER_APPLICATIONS,
|
||||
__version__,
|
||||
)
|
||||
|
||||
EXECUTABLE = shutil.which("fastanime")
|
||||
if EXECUTABLE:
|
||||
|
||||
@@ -2,7 +2,7 @@ import textwrap
|
||||
from pathlib import Path
|
||||
|
||||
from ...core.config import AppConfig
|
||||
from ...core.constants import APP_ASCII_ART
|
||||
from ...core.constants import APP_ASCII_ART, DISCORD_INVITE, PROJECT_NAME, REPO_HOME
|
||||
|
||||
# The header for the config file.
|
||||
config_asci = "\n".join([f"# {line}" for line in APP_ASCII_ART.split()])
|
||||
@@ -17,6 +17,19 @@ CONFIG_HEADER = f"""
|
||||
# For path-based options, you can use '~' for your home directory.
|
||||
""".lstrip()
|
||||
|
||||
CONFIG_FOOTER = f"""
|
||||
# ==============================================================================
|
||||
#
|
||||
# HOPE YOU ENJOY {PROJECT_NAME} AND BE SURE TO STAR THE PROJECT ON GITHUB
|
||||
# {REPO_HOME}
|
||||
#
|
||||
# Also join the discord server
|
||||
# where the anime tech community lives :)
|
||||
# {DISCORD_INVITE}
|
||||
#
|
||||
# ==============================================================================
|
||||
""".lstrip()
|
||||
|
||||
|
||||
def generate_config_ini_from_app_model(app_model: AppConfig) -> str:
|
||||
"""Generate a configuration file content from a Pydantic model."""
|
||||
@@ -61,4 +74,5 @@ def generate_config_ini_from_app_model(app_model: AppConfig) -> str:
|
||||
|
||||
config_ini_content.append(f"{field_name} = {value_str}")
|
||||
|
||||
config_ini_content.extend(["\n", CONFIG_FOOTER])
|
||||
return "\n".join(config_ini_content)
|
||||
|
||||
@@ -1,16 +1,24 @@
|
||||
import sys
|
||||
|
||||
from rich.traceback import install as rich_install
|
||||
|
||||
|
||||
def custom_exception_hook(exc_type, exc_value, exc_traceback):
|
||||
print(f"{exc_type.__name__}: {exc_value}")
|
||||
|
||||
|
||||
default_exception_hook = sys.excepthook
|
||||
# sys.tracebacklimit = 0
|
||||
|
||||
|
||||
def setup_exceptions_handler(trace: bool | None, dev: bool | None):
|
||||
def setup_exceptions_handler(
|
||||
trace: bool | None,
|
||||
dev: bool | None,
|
||||
rich_traceback: bool | None,
|
||||
rich_traceback_theme: str,
|
||||
):
|
||||
if trace or dev:
|
||||
sys.excepthook = default_exception_hook
|
||||
if rich_traceback:
|
||||
rich_install(show_locals=True, theme=rich_traceback_theme)
|
||||
else:
|
||||
sys.excepthook = custom_exception_hook
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
import logging
|
||||
|
||||
from rich.traceback import install as rich_install
|
||||
|
||||
from ...core.constants import LOG_FILE_PATH
|
||||
|
||||
|
||||
def setup_logging(
|
||||
log: bool | None, log_file: bool | None, rich_traceback: bool | None
|
||||
) -> None:
|
||||
def setup_logging(log: bool | None, log_file: bool | None) -> None:
|
||||
"""Configures the application's logging based on CLI flags."""
|
||||
if rich_traceback:
|
||||
rich_install(show_locals=True)
|
||||
|
||||
if log:
|
||||
from rich.logging import RichHandler
|
||||
|
||||
@@ -1,12 +1,20 @@
|
||||
import os
|
||||
import sys
|
||||
from importlib import resources
|
||||
from importlib import metadata, resources
|
||||
from pathlib import Path
|
||||
|
||||
PLATFORM = sys.platform
|
||||
APP_NAME = os.environ.get("FASTANIME_APP_NAME", "fastanime")
|
||||
PROJECT_NAME = "FASTANIME"
|
||||
|
||||
__version__ = metadata.version(PROJECT_NAME)
|
||||
|
||||
AUTHOR = "Benexl"
|
||||
GIT_REPO = "github.com"
|
||||
GIT_PROTOCOL = "https://"
|
||||
REPO_HOME = f"https://{GIT_REPO}/{AUTHOR}/FastAnime"
|
||||
DISCORD_INVITE = "https://discord.gg/C4rhMA4mmK"
|
||||
|
||||
try:
|
||||
APP_DIR = Path(str(resources.files(PROJECT_NAME.lower())))
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import shutil
|
||||
import subprocess
|
||||
|
||||
from ....core.config import FzfConfig
|
||||
from ....core.exceptions import FastAnimeError
|
||||
from ..base import BaseSelector
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
@@ -14,7 +15,7 @@ class FzfSelector(BaseSelector):
|
||||
self.config = config
|
||||
self.executable = shutil.which("fzf")
|
||||
if not self.executable:
|
||||
raise FileNotFoundError("fzf executable not found in PATH.")
|
||||
raise FastAnimeError("Please install fzf to use the fzf selector")
|
||||
|
||||
os.environ["FZF_DEFAULT_OPTS"] = self.config.opts
|
||||
|
||||
@@ -29,15 +30,19 @@ class FzfSelector(BaseSelector):
|
||||
def choose(self, prompt, choices, *, preview=None, header=None):
|
||||
fzf_input = "\n".join(choices)
|
||||
|
||||
# Build command from base options and specific arguments
|
||||
commands = []
|
||||
commands.extend(["--prompt", f"{prompt.title()}: "])
|
||||
commands.extend(["--header", self.header, "--header-first"])
|
||||
commands = [
|
||||
self.executable,
|
||||
"--prompt",
|
||||
f"{prompt.title()}: ",
|
||||
"--header",
|
||||
self.header,
|
||||
"--header-first",
|
||||
]
|
||||
if preview:
|
||||
commands.extend(["--preview", preview])
|
||||
|
||||
result = subprocess.run(
|
||||
[self.executable, *commands],
|
||||
commands,
|
||||
input=fzf_input,
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
@@ -54,11 +59,18 @@ class FzfSelector(BaseSelector):
|
||||
|
||||
def ask(self, prompt, *, default=None):
|
||||
# Use FZF's --print-query to capture user input
|
||||
commands = []
|
||||
commands.extend(["--prompt", f"{prompt}: ", "--print-query"])
|
||||
commands = [
|
||||
self.executable,
|
||||
"--prompt",
|
||||
f"{prompt.title()}: ",
|
||||
"--header",
|
||||
self.header,
|
||||
"--header-first",
|
||||
"--print-query",
|
||||
]
|
||||
|
||||
result = subprocess.run(
|
||||
[self.executable, *commands],
|
||||
commands,
|
||||
input="",
|
||||
stdout=subprocess.PIPE,
|
||||
text=True,
|
||||
|
||||
Reference in New Issue
Block a user