Skip to content

Commit 3ab1a1c

Browse files
committed
chore(lib): re-organizing the module
This PR refactors a lot of the code in order to have a better organized codebase. If will that I needed to create a second level of submodules, to better distinguish the different parts of this project.
1 parent 8c0dd6a commit 3ab1a1c

33 files changed

+474
-300
lines changed

docs/source/faq.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ This will also show the default value for each option.
9494
If you want to create your own template, the best is to start from the default one.
9595

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

docs/source/reference/cli.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ This page contains an exhaustive list of all the commands available with `manim-
44

55

66
```{eval-rst}
7-
.. click:: manim_slides.__main__:cli
7+
.. click:: manim_slides.cli.commands:main
88
:prog: manim-slides
99
:nested: full
1010
```

docs/source/reference/customize_html.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ One of the benefits of the `convert` command is the use of template files.
44

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

99
Because you can actually use your own template with the `--use-template`
1010
option, possibilities are infinite!

docs/source/reference/html.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ manim-slides convert --show-config
3030
## Using a Custom Template
3131

3232
The default template used for HTML conversion can be found on
33-
[GitHub](https://github.com/jeertmans/manim-slides/blob/main/manim_slides/templates/revealjs.html)
33+
[GitHub](https://github.com/jeertmans/manim-slides/blob/main/manim_slides/cli/convert/templates/revealjs.html)
3434
or printed with the `--show-template` option.
3535
If you wish to use another template, you can do so with the
3636
`--use-template FILE` option.

manim_slides/__init__.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
"""
2+
Manim Slides module.
3+
4+
Submodules are lazily imported, in order to provide a faster import experience
5+
in some cases.
6+
"""
7+
18
import sys
29
from types import ModuleType
310
from typing import Any
@@ -8,9 +15,7 @@
815
class Module(ModuleType):
916
def __getattr__(self, name: str) -> Any:
1017
if name == "Slide" or name == "ThreeDSlide":
11-
module = __import__(
12-
"manim_slides.slide", None, None, ["Slide", "ThreeDSlide"]
13-
)
18+
module = __import__("manim_slides.slide", None, None, [name])
1419
return getattr(module, name)
1520
elif name == "ManimSlidesMagic":
1621
module = __import__(

manim_slides/__main__.py

Lines changed: 3 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,6 @@
1-
import json
1+
"""Manim Slides' main entrypoint."""
22

3-
import click
4-
import requests
5-
from click_default_group import DefaultGroup
6-
7-
from .__version__ import __version__
8-
from .convert import convert
9-
from .logger import logger
10-
from .present import list_scenes, present
11-
from .render import render
12-
from .wizard import init, wizard
13-
14-
15-
@click.group(cls=DefaultGroup, default="present", default_if_no_args=True)
16-
@click.option(
17-
"--notify-outdated-version/--silent",
18-
" /-S",
19-
is_flag=True,
20-
default=True,
21-
help="Check if a new version of Manim Slides is available.",
22-
)
23-
@click.version_option(__version__, "-v", "--version")
24-
@click.help_option("-h", "--help")
25-
def cli(notify_outdated_version: bool) -> None:
26-
"""
27-
Manim Slides command-line utilities.
28-
29-
If no command is specified, defaults to `present`.
30-
"""
31-
# Code below is mostly a copy from:
32-
# https://github.com/ManimCommunity/manim/blob/main/manim/cli/render/commands.py
33-
if notify_outdated_version:
34-
manim_info_url = "https://pypi.org/pypi/manim-slides/json"
35-
warn_prompt = "Cannot check if latest release of Manim Slides is installed"
36-
try:
37-
req_info: requests.models.Response = requests.get(manim_info_url, timeout=2)
38-
req_info.raise_for_status()
39-
stable = req_info.json()["info"]["version"]
40-
if stable != __version__:
41-
click.echo(
42-
"You are using Manim Slides version "
43-
+ click.style(f"v{__version__}", fg="red")
44-
+ ", but version "
45-
+ click.style(f"v{stable}", fg="green")
46-
+ " is available."
47-
)
48-
click.echo(
49-
"You should consider upgrading via "
50-
+ click.style("pip install -U manim-slides", fg="yellow")
51-
)
52-
except requests.exceptions.HTTPError:
53-
logger.debug(f"HTTP Error: {warn_prompt}")
54-
except requests.exceptions.ConnectionError:
55-
logger.debug(f"Connection Error: {warn_prompt}")
56-
except requests.exceptions.Timeout:
57-
logger.debug(f"Timed Out: {warn_prompt}")
58-
except json.JSONDecodeError:
59-
logger.debug(warn_prompt)
60-
logger.debug(f"Error decoding JSON from {manim_info_url}")
61-
except Exception:
62-
logger.debug(f"Something went wrong: {warn_prompt}")
63-
64-
65-
cli.add_command(convert)
66-
cli.add_command(init)
67-
cli.add_command(list_scenes)
68-
cli.add_command(present)
69-
cli.add_command(render)
70-
cli.add_command(wizard)
3+
from .cli.commands import main
714

725
if __name__ == "__main__":
73-
cli()
6+
main()

manim_slides/__version__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
"""Manim Slides' version."""
2+
13
__version__ = "5.1.7"

manim_slides/cli/commands.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
"""Manim Slides' CLI."""
2+
3+
import json
4+
5+
import click
6+
import requests
7+
from click_default_group import DefaultGroup
8+
9+
from ..__version__ import __version__
10+
from ..core.logger import logger
11+
from .convert.commands import convert
12+
from .present.commands import list_scenes, present
13+
from .render.commands import render
14+
from .wizard.commands import init, wizard
15+
16+
17+
@click.group(cls=DefaultGroup, default="present", default_if_no_args=True)
18+
@click.option(
19+
"--notify-outdated-version/--silent",
20+
" /-S",
21+
is_flag=True,
22+
default=True,
23+
help="Check if a new version of Manim Slides is available.",
24+
)
25+
@click.version_option(__version__, "-v", "--version")
26+
@click.help_option("-h", "--help")
27+
def main(notify_outdated_version: bool) -> None:
28+
"""
29+
Manim Slides command-line utilities.
30+
31+
If no command is specified, defaults to `present`.
32+
"""
33+
# Code below is mostly a copy from:
34+
# https://github.com/ManimCommunity/manim/blob/main/manim/cli/render/commands.py
35+
if notify_outdated_version:
36+
manim_info_url = "https://pypi.org/pypi/manim-slides/json"
37+
warn_prompt = "Cannot check if latest release of Manim Slides is installed"
38+
try:
39+
req_info: requests.models.Response = requests.get(manim_info_url, timeout=2)
40+
req_info.raise_for_status()
41+
stable = req_info.json()["info"]["version"]
42+
if stable != __version__:
43+
click.echo(
44+
"You are using Manim Slides version "
45+
+ click.style(f"v{__version__}", fg="red")
46+
+ ", but version "
47+
+ click.style(f"v{stable}", fg="green")
48+
+ " is available."
49+
)
50+
click.echo(
51+
"You should consider upgrading via "
52+
+ click.style("pip install -U manim-slides", fg="yellow")
53+
)
54+
except requests.exceptions.HTTPError:
55+
logger.debug(f"HTTP Error: {warn_prompt}")
56+
except requests.exceptions.ConnectionError:
57+
logger.debug(f"Connection Error: {warn_prompt}")
58+
except requests.exceptions.Timeout:
59+
logger.debug(f"Timed Out: {warn_prompt}")
60+
except json.JSONDecodeError:
61+
logger.debug(warn_prompt)
62+
logger.debug(f"Error decoding JSON from {manim_info_url}")
63+
except Exception:
64+
logger.debug(f"Something went wrong: {warn_prompt}")
65+
66+
67+
main.add_command(convert)
68+
main.add_command(init)
69+
main.add_command(list_scenes)
70+
main.add_command(present)
71+
main.add_command(render)
72+
main.add_command(wizard)

manim_slides/cli/commons.py

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
from pathlib import Path
2+
from typing import Any, Callable
3+
4+
import click
5+
from click import Context, Parameter
6+
7+
from ..core.config import list_presentation_configs
8+
from ..core.defaults import CONFIG_PATH, FOLDER_PATH
9+
from ..core.logger import logger
10+
11+
F = Callable[..., Any]
12+
Wrapper = Callable[[F], F]
13+
14+
15+
def config_path_option(function: F) -> F:
16+
"""Wrap a function to add configuration path option."""
17+
wrapper: Wrapper = click.option(
18+
"-c",
19+
"--config",
20+
"config_path",
21+
metavar="FILE",
22+
default=CONFIG_PATH,
23+
type=click.Path(dir_okay=False, path_type=Path),
24+
help="Set path to configuration file.",
25+
show_default=True,
26+
)
27+
return wrapper(function)
28+
29+
30+
def config_options(function: F) -> F:
31+
"""Wrap a function to add configuration options."""
32+
function = config_path_option(function)
33+
function = click.option(
34+
"-f", "--force", is_flag=True, help="Overwrite any existing configuration file."
35+
)(function)
36+
function = click.option(
37+
"-m",
38+
"--merge",
39+
is_flag=True,
40+
help="Merge any existing configuration file with the new configuration.",
41+
)(function)
42+
return function
43+
44+
45+
def verbosity_option(function: F) -> F:
46+
"""Wrap a function to add verbosity option."""
47+
48+
def callback(ctx: Context, param: Parameter, value: str) -> None:
49+
if not value or ctx.resilient_parsing:
50+
return
51+
52+
logger.setLevel(value)
53+
54+
wrapper: Wrapper = click.option(
55+
"-v",
56+
"--verbosity",
57+
type=click.Choice(
58+
["DEBUG", "INFO", "WARNING", "ERROR", "CRITICAL"],
59+
case_sensitive=False,
60+
),
61+
help="Verbosity of CLI output.",
62+
default=None,
63+
expose_value=False,
64+
envvar="MANIM_SLIDES_VERBOSITY",
65+
show_envvar=True,
66+
callback=callback,
67+
)
68+
69+
return wrapper(function)
70+
71+
72+
def folder_path_option(function: F) -> F:
73+
"""Wrap a function to add folder path option."""
74+
wrapper: Wrapper = click.option(
75+
"--folder",
76+
metavar="DIRECTORY",
77+
default=FOLDER_PATH,
78+
type=click.Path(exists=True, file_okay=False, path_type=Path),
79+
help="Set slides folder.",
80+
show_default=True,
81+
is_eager=True, # Needed to expose its value to other callbacks
82+
)
83+
84+
return wrapper(function)
85+
86+
87+
def scenes_argument(function: F) -> F:
88+
"""
89+
Wrap a function to add a scenes arguments.
90+
91+
This function assumes that :func:`folder_path_option` is also used
92+
on the same decorated function.
93+
"""
94+
95+
def callback(ctx: Context, param: Parameter, value: tuple[str]) -> list[Path]:
96+
folder: Path = ctx.params.get("folder")
97+
98+
presentation_config_paths = list_presentation_configs(folder)
99+
scene_names = [path.stem for path in presentation_config_paths]
100+
num_scenes = len(scene_names)
101+
num_digits = len(str(num_scenes))
102+
103+
if num_scenes == 0:
104+
raise click.UsageError(
105+
f"Folder {folder} does not contain "
106+
"any valid config file, did you render the animations first?"
107+
)
108+
109+
paths = []
110+
111+
if value:
112+
for scene_name in value:
113+
try:
114+
i = scene_names.index(scene_name)
115+
paths.append(presentation_config_paths[i])
116+
except ValueError:
117+
raise click.UsageError(
118+
f"Could not find scene `{scene_name}` in: "
119+
+ ", ".join(scene_names)
120+
+ ". Did you make a typo or forgot to render the animations first?"
121+
) from None
122+
else:
123+
click.echo(
124+
"Choose at least one or more scenes from "
125+
"(enter the corresponding number):\n"
126+
+ "\n".join(
127+
f"- {i:{num_digits}d}: {name}"
128+
for i, name in enumerate(scene_names, start=1)
129+
)
130+
)
131+
continue_prompt = True
132+
while continue_prompt:
133+
index = click.prompt(
134+
"Please enter a value", type=click.IntRange(1, num_scenes)
135+
)
136+
paths.append(presentation_config_paths[index - 1])
137+
continue_prompt = click.confirm(
138+
"Do you want to enter an additional scene?"
139+
)
140+
141+
return paths
142+
143+
wrapper: Wrapper = click.argument("scenes", nargs=-1, callback=callback)
144+
145+
return wrapper(function)

0 commit comments

Comments
 (0)