mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-08-06 06:12:56 +08:00
chore(dev): move to Rye instead of PDM (#420)
* test: re-add opencv-python * chore(dev): move to Rye instead of PDM * try fix * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: build backend * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix: string quotes? * small fixes * upgrade typing * fix(ci): rye install on Windows * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(ci): typos * fix * fix(ci): actually use right python version * fix(deps): manimgl * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix docs * another fix * cleanup * make sure to use trusted publisher * chore(docs): remove PDM --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@ -29,7 +29,7 @@ class Module(ModuleType):
|
||||
|
||||
return ModuleType.__getattribute__(self, name)
|
||||
|
||||
def __dir__(self) -> List[str]:
|
||||
def __dir__(self) -> list[str]:
|
||||
result = list(new_module.__all__)
|
||||
result.extend(
|
||||
(
|
||||
|
@ -4,7 +4,7 @@ from functools import wraps
|
||||
from inspect import Parameter, signature
|
||||
from pathlib import Path
|
||||
from textwrap import dedent
|
||||
from typing import Any, Callable, Dict, List, Optional, Set, Tuple
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import rtoml
|
||||
from pydantic import (
|
||||
@ -24,7 +24,7 @@ Receiver = Callable[..., Any]
|
||||
|
||||
|
||||
class Signal(BaseModel): # type: ignore[misc]
|
||||
__receivers: List[Receiver] = PrivateAttr(default_factory=list)
|
||||
__receivers: list[Receiver] = PrivateAttr(default_factory=list)
|
||||
|
||||
def connect(self, receiver: Receiver) -> None:
|
||||
self.__receivers.append(receiver)
|
||||
@ -47,14 +47,14 @@ def key_id(name: str) -> PositiveInt:
|
||||
class Key(BaseModel): # type: ignore[misc]
|
||||
"""Represents a list of key codes, with optionally a name."""
|
||||
|
||||
ids: List[PositiveInt] = Field(unique=True)
|
||||
ids: list[PositiveInt] = Field(unique=True)
|
||||
name: Optional[str] = None
|
||||
|
||||
__signal: Signal = PrivateAttr(default_factory=Signal)
|
||||
|
||||
@field_validator("ids")
|
||||
@classmethod
|
||||
def ids_is_non_empty_set(cls, ids: Set[Any]) -> Set[Any]:
|
||||
def ids_is_non_empty_set(cls, ids: set[Any]) -> set[Any]:
|
||||
if len(ids) <= 0:
|
||||
raise ValueError("Key's ids must be a non-empty set")
|
||||
return ids
|
||||
@ -98,8 +98,8 @@ class Keys(BaseModel): # type: ignore[misc]
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def ids_are_unique_across_keys(cls, values: Dict[str, Key]) -> Dict[str, Key]:
|
||||
ids: Set[int] = set()
|
||||
def ids_are_unique_across_keys(cls, values: dict[str, Key]) -> dict[str, Key]:
|
||||
ids: set[int] = set()
|
||||
|
||||
for key in values.values():
|
||||
if len(ids.intersection(key["ids"])) != 0:
|
||||
@ -296,8 +296,8 @@ class SlideConfig(BaseSlideConfig):
|
||||
|
||||
|
||||
class PresentationConfig(BaseModel): # type: ignore[misc]
|
||||
slides: List[SlideConfig] = Field(min_length=1)
|
||||
resolution: Tuple[PositiveInt, PositiveInt] = (1920, 1080)
|
||||
slides: list[SlideConfig] = Field(min_length=1)
|
||||
resolution: tuple[PositiveInt, PositiveInt] = (1920, 1080)
|
||||
background_color: Color = "black"
|
||||
|
||||
@classmethod
|
||||
|
@ -2,7 +2,6 @@ import mimetypes
|
||||
import os
|
||||
import platform
|
||||
import subprocess
|
||||
import sys
|
||||
import tempfile
|
||||
import webbrowser
|
||||
from base64 import b64encode
|
||||
@ -10,7 +9,7 @@ from collections import deque
|
||||
from enum import Enum
|
||||
from importlib import resources
|
||||
from pathlib import Path
|
||||
from typing import Any, Callable, Dict, List, Optional, Type, Union
|
||||
from typing import Any, Callable, Optional, Union
|
||||
|
||||
import av
|
||||
import click
|
||||
@ -53,7 +52,7 @@ def open_with_default(file: Path) -> None:
|
||||
|
||||
def validate_config_option(
|
||||
ctx: Context, param: Parameter, value: Any
|
||||
) -> Dict[str, str]:
|
||||
) -> dict[str, str]:
|
||||
config = {}
|
||||
|
||||
for c_option in value:
|
||||
@ -121,7 +120,7 @@ class Converter(BaseModel): # type: ignore
|
||||
raise NotImplementedError
|
||||
|
||||
@classmethod
|
||||
def from_string(cls, s: str) -> Type["Converter"]:
|
||||
def from_string(cls, s: str) -> type["Converter"]:
|
||||
"""Return the appropriate converter from a string name."""
|
||||
return {
|
||||
"html": RevealJS,
|
||||
@ -329,7 +328,7 @@ class RevealJS(Converter):
|
||||
auto_animate_easing: AutoAnimateEasing = AutoAnimateEasing.ease
|
||||
auto_animate_duration: float = 1.0
|
||||
auto_animate_unmatched: JsBool = JsBool.true
|
||||
auto_animate_styles: List[str] = Field(
|
||||
auto_animate_styles: list[str] = Field(
|
||||
default_factory=lambda: [
|
||||
"opacity",
|
||||
"color",
|
||||
@ -379,9 +378,6 @@ class RevealJS(Converter):
|
||||
if isinstance(self.template, Path):
|
||||
return self.template.read_text()
|
||||
|
||||
if sys.version_info < (3, 9):
|
||||
return resources.read_text(templates, "revealjs.html")
|
||||
|
||||
return resources.files(templates).joinpath("revealjs.html").read_text()
|
||||
|
||||
def open(self, file: Path) -> bool:
|
||||
@ -658,13 +654,13 @@ def show_template_option(function: Callable[..., Any]) -> Callable[..., Any]:
|
||||
@show_config_options
|
||||
@verbosity_option
|
||||
def convert(
|
||||
scenes: List[str],
|
||||
scenes: list[str],
|
||||
folder: Path,
|
||||
dest: Path,
|
||||
to: str,
|
||||
open_result: bool,
|
||||
force: bool,
|
||||
config_options: Dict[str, str],
|
||||
config_options: dict[str, str],
|
||||
template: Optional[Path],
|
||||
) -> None:
|
||||
"""Convert SCENE(s) into a given format and writes the result in DEST."""
|
||||
|
@ -39,7 +39,7 @@ def list_scenes(folder: Path) -> None:
|
||||
click.secho(f"{i}: {scene}", fg="green")
|
||||
|
||||
|
||||
def _list_scenes(folder: Path) -> List[str]:
|
||||
def _list_scenes(folder: Path) -> list[str]:
|
||||
"""List available scenes in given directory."""
|
||||
scenes = []
|
||||
|
||||
@ -59,7 +59,7 @@ def _list_scenes(folder: Path) -> List[str]:
|
||||
return scenes
|
||||
|
||||
|
||||
def prompt_for_scenes(folder: Path) -> List[str]:
|
||||
def prompt_for_scenes(folder: Path) -> list[str]:
|
||||
"""Prompt the user to select scenes within a given folder."""
|
||||
scene_choices = dict(enumerate(_list_scenes(folder), start=1))
|
||||
|
||||
@ -71,7 +71,7 @@ def prompt_for_scenes(folder: Path) -> List[str]:
|
||||
click.echo("Choose number corresponding to desired scene/arguments.")
|
||||
click.echo("(Use comma separated list for multiple entries)")
|
||||
|
||||
def value_proc(value: Optional[str]) -> List[str]:
|
||||
def value_proc(value: Optional[str]) -> list[str]:
|
||||
indices = list(map(int, (value or "").strip().replace(" ", "").split(",")))
|
||||
|
||||
if not all(0 < i <= len(scene_choices) for i in indices):
|
||||
@ -93,8 +93,8 @@ def prompt_for_scenes(folder: Path) -> List[str]:
|
||||
|
||||
|
||||
def get_scenes_presentation_config(
|
||||
scenes: List[str], folder: Path
|
||||
) -> List[PresentationConfig]:
|
||||
scenes: list[str], folder: Path
|
||||
) -> list[PresentationConfig]:
|
||||
"""Return a list of presentation configurations based on the user input."""
|
||||
if len(scenes) == 0:
|
||||
scenes = prompt_for_scenes(folder)
|
||||
@ -116,7 +116,7 @@ def get_scenes_presentation_config(
|
||||
|
||||
def start_at_callback(
|
||||
ctx: Context, param: Parameter, values: str
|
||||
) -> Tuple[Optional[int], ...]:
|
||||
) -> tuple[Optional[int], ...]:
|
||||
if values == "(None, None)":
|
||||
return (None, None)
|
||||
|
||||
@ -253,7 +253,7 @@ def start_at_callback(
|
||||
@click.help_option("-h", "--help")
|
||||
@verbosity_option
|
||||
def present(
|
||||
scenes: List[str],
|
||||
scenes: list[str],
|
||||
config_path: Path,
|
||||
folder: Path,
|
||||
start_paused: bool,
|
||||
@ -262,7 +262,7 @@ def present(
|
||||
exit_after_last_slide: bool,
|
||||
hide_mouse: bool,
|
||||
aspect_ratio: str,
|
||||
start_at: Tuple[Optional[int], Optional[int], Optional[int]],
|
||||
start_at: tuple[Optional[int], Optional[int], Optional[int]],
|
||||
start_at_scene_number: int,
|
||||
start_at_slide_number: int,
|
||||
screen_number: Optional[int],
|
||||
|
@ -1,6 +1,6 @@
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from typing import List, Optional
|
||||
from typing import Optional
|
||||
|
||||
from qtpy.QtCore import Qt, QTimer, QUrl, Signal, Slot
|
||||
from qtpy.QtGui import QCloseEvent, QIcon, QKeyEvent, QScreen
|
||||
@ -169,7 +169,7 @@ class Player(QMainWindow): # type: ignore[misc]
|
||||
def __init__(
|
||||
self,
|
||||
config: Config,
|
||||
presentation_configs: List[PresentationConfig],
|
||||
presentation_configs: list[PresentationConfig],
|
||||
*,
|
||||
start_paused: bool = False,
|
||||
full_screen: bool = False,
|
||||
|
@ -12,7 +12,6 @@ This is especially useful for two reasons:
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from typing import Tuple
|
||||
|
||||
import click
|
||||
|
||||
@ -39,7 +38,7 @@ import click
|
||||
help="If set, use ManimGL renderer.",
|
||||
)
|
||||
@click.argument("args", metavar="[RENDERER_ARGS]...", nargs=-1, type=click.UNPROCESSED)
|
||||
def render(ce: bool, gl: bool, args: Tuple[str, ...]) -> None:
|
||||
def render(ce: bool, gl: bool, args: tuple[str, ...]) -> None:
|
||||
"""
|
||||
Render SCENE(s) from the input FILE, using the specified renderer.
|
||||
|
||||
|
@ -11,7 +11,8 @@ that directly calls ``self.play(Animation(...))``, see
|
||||
|
||||
__all__ = ["Wipe", "Zoom"]
|
||||
|
||||
from typing import Any, Mapping, Optional, Sequence
|
||||
from collections.abc import Mapping, Sequence
|
||||
from typing import Any, Optional
|
||||
|
||||
import numpy as np
|
||||
|
||||
|
@ -4,13 +4,11 @@ __all__ = ["BaseSlide"]
|
||||
|
||||
import platform
|
||||
from abc import abstractmethod
|
||||
from collections.abc import MutableMapping, Sequence, ValuesView
|
||||
from pathlib import Path
|
||||
from typing import (
|
||||
TYPE_CHECKING,
|
||||
Any,
|
||||
MutableMapping,
|
||||
Sequence,
|
||||
ValuesView,
|
||||
)
|
||||
|
||||
import numpy as np
|
||||
|
@ -1,5 +1,5 @@
|
||||
from pathlib import Path
|
||||
from typing import Any, List, Optional, Tuple
|
||||
from typing import Any, Optional
|
||||
|
||||
from manim import Scene, ThreeDScene, config
|
||||
|
||||
@ -30,11 +30,11 @@ class Slide(BaseSlide, Scene): # type: ignore[misc]
|
||||
return color.to_hex() # type: ignore
|
||||
|
||||
@property
|
||||
def _resolution(self) -> Tuple[int, int]:
|
||||
def _resolution(self) -> tuple[int, int]:
|
||||
return config["pixel_width"], config["pixel_height"]
|
||||
|
||||
@property
|
||||
def _partial_movie_files(self) -> List[Path]:
|
||||
def _partial_movie_files(self) -> list[Path]:
|
||||
# When rendering with -na,b (manim only)
|
||||
# the animations not in [a,b] will be skipped,
|
||||
# but animation before a will have a None source file.
|
||||
|
@ -1,5 +1,5 @@
|
||||
from pathlib import Path
|
||||
from typing import Any, ClassVar, Dict, List, Optional, Tuple
|
||||
from typing import Any, ClassVar, Optional
|
||||
|
||||
from manimlib import Scene, ThreeDCamera
|
||||
from manimlib.utils.file_ops import get_sorted_integer_files
|
||||
@ -31,11 +31,11 @@ class Slide(BaseSlide, Scene): # type: ignore[misc]
|
||||
return self.camera_config["background_color"].hex # type: ignore
|
||||
|
||||
@property
|
||||
def _resolution(self) -> Tuple[int, int]:
|
||||
def _resolution(self) -> tuple[int, int]:
|
||||
return self.camera_config["pixel_width"], self.camera_config["pixel_height"]
|
||||
|
||||
@property
|
||||
def _partial_movie_files(self) -> List[Path]:
|
||||
def _partial_movie_files(self) -> list[Path]:
|
||||
kwargs = {
|
||||
"remove_non_integer_files": True,
|
||||
"extension": self.file_writer.movie_file_extension,
|
||||
@ -66,7 +66,7 @@ class Slide(BaseSlide, Scene): # type: ignore[misc]
|
||||
|
||||
|
||||
class ThreeDSlide(Slide):
|
||||
CONFIG: ClassVar[Dict[str, Any]] = {
|
||||
CONFIG: ClassVar[dict[str, Any]] = {
|
||||
"camera_class": ThreeDCamera,
|
||||
}
|
||||
pass
|
||||
|
@ -1,18 +1,18 @@
|
||||
import hashlib
|
||||
import os
|
||||
import tempfile
|
||||
from collections.abc import Iterator
|
||||
from pathlib import Path
|
||||
from typing import Iterator, List
|
||||
|
||||
import av
|
||||
|
||||
from .logger import logger
|
||||
|
||||
|
||||
def concatenate_video_files(files: List[Path], dest: Path) -> None:
|
||||
def concatenate_video_files(files: list[Path], dest: Path) -> None:
|
||||
"""Concatenate multiple video files into one."""
|
||||
|
||||
def _filter(files: List[Path]) -> Iterator[Path]:
|
||||
def _filter(files: list[Path]) -> Iterator[Path]:
|
||||
"""Patch possibly empty video files."""
|
||||
for file in files:
|
||||
with av.open(str(file)) as container:
|
||||
@ -30,9 +30,10 @@ def concatenate_video_files(files: List[Path], dest: Path) -> None:
|
||||
f.writelines(f"file '{file}'\n" for file in _filter(files))
|
||||
tmp_file = f.name
|
||||
|
||||
with av.open(
|
||||
tmp_file, format="concat", options={"safe": "0"}
|
||||
) as input_container, av.open(str(dest), mode="w") as output_container:
|
||||
with (
|
||||
av.open(tmp_file, format="concat", options={"safe": "0"}) as input_container,
|
||||
av.open(str(dest), mode="w") as output_container,
|
||||
):
|
||||
input_video_stream = input_container.streams.video[0]
|
||||
output_video_stream = output_container.add_stream(
|
||||
template=input_video_stream,
|
||||
@ -61,7 +62,7 @@ def concatenate_video_files(files: List[Path], dest: Path) -> None:
|
||||
os.unlink(tmp_file) # https://stackoverflow.com/a/54768241
|
||||
|
||||
|
||||
def merge_basenames(files: List[Path]) -> Path:
|
||||
def merge_basenames(files: list[Path]) -> Path:
|
||||
"""Merge multiple filenames by concatenating basenames."""
|
||||
if len(files) == 0:
|
||||
raise ValueError("Cannot merge an empty list of files!")
|
||||
@ -90,9 +91,10 @@ def link_nodes(*nodes: av.filter.context.FilterContext) -> None:
|
||||
|
||||
def reverse_video_file(src: Path, dest: Path) -> None:
|
||||
"""Reverses a video file, writing the result to `dest`."""
|
||||
with av.open(str(src)) as input_container, av.open(
|
||||
str(dest), mode="w"
|
||||
) as output_container:
|
||||
with (
|
||||
av.open(str(src)) as input_container,
|
||||
av.open(str(dest), mode="w") as output_container,
|
||||
):
|
||||
input_stream = input_container.streams.video[0]
|
||||
output_stream = output_container.add_stream(
|
||||
codec_name="libx264", rate=input_stream.base_rate
|
||||
|
Reference in New Issue
Block a user