Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(lib): re-organizing the module #460

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docs/source/faq.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ This will also show the default value for each option.
If you want to create your own template, the best is to start from the default one.

You can either download it from the
[template folder](https://github.com/jeertmans/manim-slides/tree/main/manim_slides/templates)
[template folder](https://github.com/jeertmans/manim-slides/tree/main/manim_slides/cli/convert/templates)
or use the `manim-slides convert --to=FORMAT --show-template` command,
where `FORMAT` is one of the supported formats.

Expand Down
2 changes: 1 addition & 1 deletion docs/source/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ This page contains an exhaustive list of all the commands available with `manim-


```{eval-rst}
.. click:: manim_slides.__main__:cli
.. click:: manim_slides.cli.commands:main
:prog: manim-slides
:nested: full
```
2 changes: 1 addition & 1 deletion docs/source/reference/customize_html.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ One of the benefits of the `convert` command is the use of template files.

Currently, only the HTML export uses one. If not specified, the template
will be the one shipped with Manim Slides, see
[`manim_slides/templates/revealjs.html`](https://github.com/jeertmans/manim-slides/blob/main/manim_slides/templates/revealjs.html).
[`manim_slides/cli/convert/templates/revealjs.html`](https://github.com/jeertmans/manim-slides/blob/main/manim_slides/cli/convert/templates/revealjs.html).

Because you can actually use your own template with the `--use-template`
option, possibilities are infinite!
Expand Down
2 changes: 1 addition & 1 deletion docs/source/reference/html.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ manim-slides convert --show-config
## Using a Custom Template

The default template used for HTML conversion can be found on
[GitHub](https://github.com/jeertmans/manim-slides/blob/main/manim_slides/templates/revealjs.html)
[GitHub](https://github.com/jeertmans/manim-slides/blob/main/manim_slides/cli/convert/templates/revealjs.html)
or printed with the `--show-template` option.
If you wish to use another template, you can do so with the
`--use-template FILE` option.
Expand Down
11 changes: 8 additions & 3 deletions manim_slides/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,10 @@
"""
Manim Slides module.

Submodules are lazily imported, in order to provide a faster import experience
in some cases.
"""

import sys
from types import ModuleType
from typing import Any
Expand All @@ -8,9 +15,7 @@
class Module(ModuleType):
def __getattr__(self, name: str) -> Any:
if name == "Slide" or name == "ThreeDSlide":
module = __import__(
"manim_slides.slide", None, None, ["Slide", "ThreeDSlide"]
)
module = __import__("manim_slides.slide", None, None, [name])
return getattr(module, name)
elif name == "ManimSlidesMagic":
module = __import__(
Expand Down
73 changes: 3 additions & 70 deletions manim_slides/__main__.py
Original file line number Diff line number Diff line change
@@ -1,73 +1,6 @@
import json
"""Manim Slides' main entrypoint."""

import click
import requests
from click_default_group import DefaultGroup

from .__version__ import __version__
from .convert import convert
from .logger import logger
from .present import list_scenes, present
from .render import render
from .wizard import init, wizard


@click.group(cls=DefaultGroup, default="present", default_if_no_args=True)
@click.option(
"--notify-outdated-version/--silent",
" /-S",
is_flag=True,
default=True,
help="Check if a new version of Manim Slides is available.",
)
@click.version_option(__version__, "-v", "--version")
@click.help_option("-h", "--help")
def cli(notify_outdated_version: bool) -> None:
"""
Manim Slides command-line utilities.

If no command is specified, defaults to `present`.
"""
# Code below is mostly a copy from:
# https://github.com/ManimCommunity/manim/blob/main/manim/cli/render/commands.py
if notify_outdated_version:
manim_info_url = "https://pypi.org/pypi/manim-slides/json"
warn_prompt = "Cannot check if latest release of Manim Slides is installed"
try:
req_info: requests.models.Response = requests.get(manim_info_url, timeout=2)
req_info.raise_for_status()
stable = req_info.json()["info"]["version"]
if stable != __version__:
click.echo(
"You are using Manim Slides version "
+ click.style(f"v{__version__}", fg="red")
+ ", but version "
+ click.style(f"v{stable}", fg="green")
+ " is available."
)
click.echo(
"You should consider upgrading via "
+ click.style("pip install -U manim-slides", fg="yellow")
)
except requests.exceptions.HTTPError:
logger.debug(f"HTTP Error: {warn_prompt}")
except requests.exceptions.ConnectionError:
logger.debug(f"Connection Error: {warn_prompt}")
except requests.exceptions.Timeout:
logger.debug(f"Timed Out: {warn_prompt}")
except json.JSONDecodeError:
logger.debug(warn_prompt)
logger.debug(f"Error decoding JSON from {manim_info_url}")
except Exception:
logger.debug(f"Something went wrong: {warn_prompt}")


cli.add_command(convert)
cli.add_command(init)
cli.add_command(list_scenes)
cli.add_command(present)
cli.add_command(render)
cli.add_command(wizard)
from .cli.commands import main

if __name__ == "__main__":
cli()
main()
2 changes: 2 additions & 0 deletions manim_slides/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
"""Manim Slides' version."""

__version__ = "5.1.7"
72 changes: 72 additions & 0 deletions manim_slides/cli/commands.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
"""Manim Slides' CLI."""

import json

import click
import requests
from click_default_group import DefaultGroup

from ..__version__ import __version__
from ..core.logger import logger
from .convert.commands import convert
from .present.commands import list_scenes, present
from .render.commands import render
from .wizard.commands import init, wizard


@click.group(cls=DefaultGroup, default="present", default_if_no_args=True)
@click.option(
"--notify-outdated-version/--silent",
" /-S",
is_flag=True,
default=True,
help="Check if a new version of Manim Slides is available.",
)
@click.version_option(__version__, "-v", "--version")
@click.help_option("-h", "--help")
def main(notify_outdated_version: bool) -> None:
"""
Manim Slides command-line utilities.

If no command is specified, defaults to `present`.
"""
# Code below is mostly a copy from:
# https://github.com/ManimCommunity/manim/blob/main/manim/cli/render/commands.py
if notify_outdated_version:
manim_info_url = "https://pypi.org/pypi/manim-slides/json"
warn_prompt = "Cannot check if latest release of Manim Slides is installed"
try:
req_info: requests.models.Response = requests.get(manim_info_url, timeout=2)
req_info.raise_for_status()
stable = req_info.json()["info"]["version"]
if stable != __version__:
click.echo(
"You are using Manim Slides version "
+ click.style(f"v{__version__}", fg="red")
+ ", but version "
+ click.style(f"v{stable}", fg="green")
+ " is available."
)
click.echo(
"You should consider upgrading via "
+ click.style("pip install -U manim-slides", fg="yellow")
)
except requests.exceptions.HTTPError:
logger.debug(f"HTTP Error: {warn_prompt}")
except requests.exceptions.ConnectionError:
logger.debug(f"Connection Error: {warn_prompt}")
except requests.exceptions.Timeout:
logger.debug(f"Timed Out: {warn_prompt}")
except json.JSONDecodeError:
logger.debug(warn_prompt)
logger.debug(f"Error decoding JSON from {manim_info_url}")
except Exception:
logger.debug(f"Something went wrong: {warn_prompt}")


main.add_command(convert)
main.add_command(init)
main.add_command(list_scenes)
main.add_command(present)
main.add_command(render)
main.add_command(wizard)
67 changes: 65 additions & 2 deletions manim_slides/commons.py → manim_slides/cli/commons.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@
import click
from click import Context, Parameter

from .defaults import CONFIG_PATH, FOLDER_PATH
from .logger import logger
from ..core.config import list_presentation_configs
from ..core.defaults import CONFIG_PATH, FOLDER_PATH
from ..core.logger import logger

F = Callable[..., Any]
Wrapper = Callable[[F], F]
Expand Down Expand Up @@ -88,6 +89,68 @@ def callback(ctx: Context, param: Parameter, value: Path) -> Path:
callback=callback,
help="Set slides folder.",
show_default=True,
is_eager=True, # Needed to expose its value to other callbacks
)

return wrapper(function)


def scenes_argument(function: F) -> F:
"""
Wrap a function to add a scenes arguments.

This function assumes that :func:`folder_path_option` is also used
on the same decorated function.
"""

def callback(ctx: Context, param: Parameter, value: tuple[str]) -> list[Path]:
folder: Path = ctx.params.get("folder")

presentation_config_paths = list_presentation_configs(folder)
scene_names = [path.stem for path in presentation_config_paths]
num_scenes = len(scene_names)
num_digits = len(str(num_scenes))

if num_scenes == 0:
raise click.UsageError(
f"Folder {folder} does not contain "
"any valid config file, did you render the animations first?"
)

paths = []

if value:
for scene_name in value:
try:
i = scene_names.index(scene_name)
paths.append(presentation_config_paths[i])
except ValueError:
raise click.UsageError(
f"Could not find scene `{scene_name}` in: "
+ ", ".join(scene_names)
+ ". Did you make a typo or forgot to render the animations first?"
) from None
else:
click.echo(
"Choose at least one or more scenes from "
"(enter the corresponding number):\n"
+ "\n".join(
f"- {i:{num_digits}d}: {name}"
for i, name in enumerate(scene_names, start=1)
)
)
continue_prompt = True
while continue_prompt:
index = click.prompt(
"Please enter a value", type=click.IntRange(1, num_scenes)
)
paths.append(presentation_config_paths[index - 1])
continue_prompt = click.confirm(
"Do you want to enter an additional scene?"
)

return paths

wrapper: Wrapper = click.argument("scenes", nargs=-1, callback=callback)

return wrapper(function)
24 changes: 17 additions & 7 deletions manim_slides/convert.py → manim_slides/cli/convert/commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,14 +33,18 @@
from pydantic_extra_types.color import Color
from tqdm import tqdm

from ...core.config import PresentationConfig
from ...core.logger import logger
from ..commons import folder_path_option, scenes_argument, verbosity_option
from . import templates
from .commons import folder_path_option, verbosity_option
from .config import PresentationConfig
from .logger import logger
from .present import get_scenes_presentation_config


def open_with_default(file: Path) -> None:
"""
Open a file with the default application.

:param file: The file to open.
"""
system = platform.system()
if system == "Darwin":
subprocess.call(("open", str(file)))
Expand Down Expand Up @@ -134,6 +138,7 @@ class Str(str):

# This fixes pickling issue on Python 3.8
__reduce_ex__ = str.__reduce_ex__
# TODO: do we still need this?

@classmethod
def __get_pydantic_core_schema__(
Expand Down Expand Up @@ -381,6 +386,11 @@ def load_template(self) -> str:
return resources.files(templates).joinpath("revealjs.html").read_text()

def open(self, file: Path) -> bool:
"""
Open the HTML file inside a web browser.

:param path: The path to the HTML file.
"""
return webbrowser.open(file.absolute().as_uri())

def convert_to(self, dest: Path) -> None:
Expand Down Expand Up @@ -635,7 +645,7 @@ def callback(ctx: Context, param: Parameter, value: bool) -> None:


@click.command()
@click.argument("scenes", nargs=-1)
@scenes_argument
@folder_path_option
@click.argument("dest", type=click.Path(dir_okay=False, path_type=Path))
@click.option(
Expand Down Expand Up @@ -674,7 +684,7 @@ def callback(ctx: Context, param: Parameter, value: bool) -> None:
@show_config_options
@verbosity_option
def convert(
scenes: list[str],
scenes: list[Path],
folder: Path,
dest: Path,
to: str,
Expand All @@ -684,7 +694,7 @@ def convert(
template: Optional[Path],
) -> None:
"""Convert SCENE(s) into a given format and writes the result in DEST."""
presentation_configs = get_scenes_presentation_config(scenes, folder)
presentation_configs = [PresentationConfig.from_file(scene) for scene in scenes]

try:
if to == "auto":
Expand Down
1 change: 1 addition & 0 deletions manim_slides/cli/convert/templates/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Manim Slides conversion templates."""
1 change: 1 addition & 0 deletions manim_slides/cli/present/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
"""Manim Slides' presentation commands."""
Loading
Loading