feat: lazy load all cli commands for fasster start times

This commit is contained in:
Benex254
2024-08-06 15:45:26 +03:00
parent 588a6b4e2b
commit 9772584d2a
5 changed files with 110 additions and 45 deletions

View File

@@ -6,18 +6,14 @@ from .. import __version__
from ..libs.anime_provider import anime_sources
from ..libs.anime_provider.allanime.constants import SERVERS_AVAILABLE
from ..Utility.data import anilist_sort_normalizer
from .commands.anilist import anilist
from .commands.config import configure
from .commands.download import download
from .commands.downloads import downloads
from .commands.search import search
from .commands import LazyGroup
commands = {
"search": search,
"download": download,
"anilist": anilist,
"config": configure,
"downloads": downloads,
"search": "search.search",
"download": "download.download",
"anilist": "anilist.anilist",
"config": "config.config",
"downloads": "downloads.downloads",
}
@@ -35,7 +31,8 @@ signal.signal(signal.SIGINT, handle_exit)
@click.group(
commands=commands,
lazy_subcommands=commands,
cls=LazyGroup,
help="A command line application for streaming anime that provides a complete and featureful interface",
short_help="Stream Anime",
)

View File

@@ -0,0 +1,40 @@
# in lazy_group.py
import importlib
import click
class LazyGroup(click.Group):
def __init__(self, *args, lazy_subcommands=None, **kwargs):
super().__init__(*args, **kwargs)
# lazy_subcommands is a map of the form:
#
# {command-name} -> {module-name}.{command-object-name}
#
self.lazy_subcommands = lazy_subcommands or {}
def list_commands(self, ctx):
base = super().list_commands(ctx)
lazy = sorted(self.lazy_subcommands.keys())
return base + lazy
def get_command(self, ctx, cmd_name): # pyright:ignore
if cmd_name in self.lazy_subcommands:
return self._lazy_load(cmd_name)
return super().get_command(ctx, cmd_name)
def _lazy_load(self, cmd_name: str):
# lazily loading a command, first get the module name and attribute name
import_path: str = self.lazy_subcommands[cmd_name]
modname, cmd_object_name = import_path.rsplit(".", 1)
# do the import
mod = importlib.import_module(f".{modname}", package="fastanime.cli.commands")
# get the Command object from that module
cmd_object = getattr(mod, cmd_object_name)
# check the result to make debugging easier
if not isinstance(cmd_object, click.BaseCommand):
raise ValueError(
f"Lazy loading of {import_path} failed by returning "
"a non-command object"
)
return cmd_object

View File

@@ -1,45 +1,31 @@
import click
from ...utils.tools import QueryDict
from .completed import completed
from .dropped import dropped
from .favourites import favourites
from .login import login
from .notifier import notifier
from .paused import paused
from .planning import planning
from .popular import popular
from .random_anime import random_anime
from .recent import recent
from .rewatching import rewatching
from .scores import scores
from .search import search
from .trending import trending
from .upcoming import upcoming
from .watching import watching
from .__lazyloader__ import LazyGroup
commands = {
"trending": trending,
"recent": recent,
"search": search,
"upcoming": upcoming,
"scores": scores,
"popular": popular,
"favourites": favourites,
"random": random_anime,
"login": login,
"watching": watching,
"paused": paused,
"rewatching": rewatching,
"dropped": dropped,
"completed": completed,
"planning": planning,
"notifier": notifier,
"trending": "trending.trending",
"recent": "recent.recent",
"search": "search.search",
"upcoming": "upcoming.upcoming",
"scores": "scores.scores",
"popular": "popular.popular",
"favourites": "favourites.favourites",
"random": "random_anime.random_anime",
"login": "login.login",
"watching": "watching.watching",
"paused": "paused.paused",
"rewatching": "rewatching.rewatching",
"dropped": "dropped.dropped",
"completed": "completed.completed",
"planning": "planning.planning",
"notifier": "notifier.notifier",
}
@click.group(
commands=commands,
lazy_subcommands=commands,
cls=LazyGroup,
invoke_without_command=True,
help="A beautiful interface that gives you access to a commplete streaming experience",
short_help="Access all streaming options",

View File

@@ -0,0 +1,42 @@
# in lazy_group.py
import importlib
import click
class LazyGroup(click.Group):
def __init__(self, *args, lazy_subcommands=None, **kwargs):
super().__init__(*args, **kwargs)
# lazy_subcommands is a map of the form:
#
# {command-name} -> {module-name}.{command-object-name}
#
self.lazy_subcommands = lazy_subcommands or {}
def list_commands(self, ctx):
base = super().list_commands(ctx)
lazy = sorted(self.lazy_subcommands.keys())
return base + lazy
def get_command(self, ctx, cmd_name): # pyright:ignore
if cmd_name in self.lazy_subcommands:
return self._lazy_load(cmd_name)
return super().get_command(ctx, cmd_name)
def _lazy_load(self, cmd_name: str):
# lazily loading a command, first get the module name and attribute name
import_path: str = self.lazy_subcommands[cmd_name]
modname, cmd_object_name = import_path.rsplit(".", 1)
# do the import
mod = importlib.import_module(
f".{modname}", package="fastanime.cli.commands.anilist"
)
# get the Command object from that module
cmd_object = getattr(mod, cmd_object_name)
# check the result to make debugging easier
if not isinstance(cmd_object, click.BaseCommand):
raise ValueError(
f"Lazy loading of {import_path} failed by returning "
"a non-command object"
)
return cmd_object

View File

@@ -13,7 +13,7 @@ import click
is_flag=True,
)
# @click.pass_obj
def configure(path, desktop_entry):
def config(path, desktop_entry):
import os
import subprocess