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 4 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 @@ -92,7 +92,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
```
Expand Down
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
9 changes: 3 additions & 6 deletions manim_slides/__main__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,8 @@
import json

import click
import requests
from click_default_group import DefaultGroup
"""Manim Slides' main entrypoint."""

from .__version__ import __version__
from .checkhealth import checkhealth
from .cli.commands import main
from .convert import convert
from .logger import logger
from .present import list_scenes, present
Expand Down Expand Up @@ -72,4 +69,4 @@ def cli(notify_outdated_version: bool) -> None:
cli.add_command(wizard)

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.4.2"
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 @@ -37,14 +37,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 @@ -142,6 +146,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 @@ -547,6 +552,11 @@ def load_template(self) -> str:
return resources.files(templates).joinpath("revealjs.html").read_text()

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

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

def convert_to(self, dest: Path) -> None: # noqa: C901
Expand Down Expand Up @@ -910,7 +920,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 @@ -960,7 +970,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 @@ -971,7 +981,7 @@ def convert(
one_file: bool,
) -> 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."""
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,14 @@
from click import Context, Parameter
from pydantic import ValidationError

from ..commons import config_path_option, folder_path_option, verbosity_option
from ..config import Config, PresentationConfig
from ..logger import logger
from ...core.config import Config, PresentationConfig, list_presentation_configs
from ...core.logger import logger
from ..commons import (
config_path_option,
folder_path_option,
scenes_argument,
verbosity_option,
)


@click.command()
Expand All @@ -18,8 +23,10 @@
@verbosity_option
def list_scenes(folder: Path) -> None:
"""List available scenes."""
for i, scene in enumerate(_list_scenes(folder), start=1):
click.secho(f"{i}: {scene}", fg="green")
scene_names = [path.stem for path in list_presentation_configs(folder)]
num_digits = len(str(len(scene_names)))
for i, scene_name in enumerate(scene_names, start=1):
click.secho(f"{i:{num_digits}d}: {scene_name}", fg="green")


def _list_scenes(folder: Path) -> list[str]:
Expand Down Expand Up @@ -130,7 +137,7 @@ def str_to_int_or_none(value: str) -> Optional[int]:


@click.command()
@click.argument("scenes", nargs=-1)
@scenes_argument
@config_path_option
@folder_path_option
@click.option("--start-paused", is_flag=True, help="Start paused.")
Expand Down Expand Up @@ -276,7 +283,7 @@ def present( # noqa: C901
if skip_all:
exit_after_last_slide = True

presentation_configs = get_scenes_presentation_config(scenes, folder)
presentation_configs = [PresentationConfig.from_file(path) for path in scenes]

if config_path.exists():
try:
Expand Down
File renamed without changes.
File renamed without changes.
Empty file.
26 changes: 14 additions & 12 deletions manim_slides/render.py → manim_slides/cli/render/commands.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,4 @@
"""
Alias command to either
``manim render [OPTIONS] [ARGS]...`` or
``manimgl -w [OPTIONS] [ARGS]...``.

This is especially useful for two reasons:

1. You can are sure to execute the rendering command with the same Python environment
as for ``manim-slides``.
2. You can pass options to the config.
"""
"""Manim Slides' rendering commands."""

import subprocess
import sys
Expand Down Expand Up @@ -44,10 +34,22 @@ def render(ce: bool, gl: bool, args: tuple[str, ...]) -> None:

Use ``manim-slides render --help`` to see help information for
a specific renderer.

Alias command to either
``manim render [OPTIONS] [ARGS]...`` or
``manimgl [OPTIONS] [ARGS]...``.

This is especially useful for two reasons:

1. You can are sure to execute the rendering command with the same Python environment
as for ``manim-slides``.
2. You can pass options to the config.
"""
if ce and gl:
raise click.UsageError("You cannot specify both --CE and --GL renderers.")
if gl:
subprocess.run([sys.executable, "-m", "manimlib", "-w", *args])
else:
subprocess.run([sys.executable, "-m", "manim", "render", *args])
from manim.cli.render.commands import render as render_ce

render_ce(args, standalone_mode=False)
File renamed without changes.
Loading
Loading