mirror of
https://github.com/jeertmans/manim-slides.git
synced 2025-09-19 12:44:11 +08:00
chore(deps): bumping pydantic to V2 (#207)
* chore(deps): bumping pydantic to V2 * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * fix(lib): validators * fix(ci): add tests and fixes * fix(lib): add missing mode arg * fix(lib): change function name * chore(tests): add more tests and use pytest * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * chore(deps): change test deps * chore(ci): install manim deps * fix(ci): move to right place * fix(lib): add custom schema * fix(lib): validators --------- Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
@ -1,21 +1,40 @@
|
|||||||
on:
|
on:
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
|
||||||
- pyproject.toml
|
|
||||||
- poetry.lock
|
|
||||||
- '**.py'
|
|
||||||
- .github/workflows/test_examples.yml
|
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
name: Test Examples
|
name: Tests
|
||||||
|
|
||||||
env:
|
|
||||||
QT_QPA_PLATFORM: offscreen
|
|
||||||
MANIM_SLIDES_VERBOSITY: debug
|
|
||||||
PYTHONFAULTHANDLER: 1
|
|
||||||
DISPLAY: :99
|
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
|
pytest:
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pyversion: ['3.8', '3.9', '3.10', '3.11']
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
|
- name: Install Poetry
|
||||||
|
run: pipx install poetry
|
||||||
|
|
||||||
|
- name: Install Python
|
||||||
|
uses: actions/setup-python@v4
|
||||||
|
with:
|
||||||
|
python-version: ${{ matrix.pyversion }}
|
||||||
|
cache: poetry
|
||||||
|
|
||||||
|
- name: Install manim dependencies on Ubuntu
|
||||||
|
run: |
|
||||||
|
sudo apt-get install libcairo2-dev libpango1.0-dev ffmpeg freeglut3-dev
|
||||||
|
|
||||||
|
- name: Install Manim Slides
|
||||||
|
run: |
|
||||||
|
poetry install --with test
|
||||||
|
|
||||||
|
- name: Run pytest
|
||||||
|
run: poetry run pytest
|
||||||
|
|
||||||
build-examples:
|
build-examples:
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: false
|
fail-fast: false
|
||||||
@ -45,11 +64,18 @@ jobs:
|
|||||||
pyversion: '3.10'
|
pyversion: '3.10'
|
||||||
manim: manim
|
manim: manim
|
||||||
runs-on: ${{ matrix.os }}
|
runs-on: ${{ matrix.os }}
|
||||||
|
env:
|
||||||
|
QT_QPA_PLATFORM: offscreen
|
||||||
|
MANIM_SLIDES_VERBOSITY: debug
|
||||||
|
PYTHONFAULTHANDLER: 1
|
||||||
|
DISPLAY: :99
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v3
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
- name: Install Poetry
|
- name: Install Poetry
|
||||||
run: pipx install poetry
|
run: pipx install poetry
|
||||||
|
|
||||||
- name: Install Python
|
- name: Install Python
|
||||||
uses: actions/setup-python@v4
|
uses: actions/setup-python@v4
|
||||||
with:
|
with:
|
||||||
@ -62,9 +88,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
echo "${HOME}/.local/bin" >> $GITHUB_PATH
|
echo "${HOME}/.local/bin" >> $GITHUB_PATH
|
||||||
echo "/Users/runner/Library/Python/${{ matrix.pyversion }}/bin" >> $GITHUB_PATH
|
echo "/Users/runner/Library/Python/${{ matrix.pyversion }}/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Append to Path on Ubuntu
|
- name: Append to Path on Ubuntu
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: echo "${HOME}/.local/bin" >> $GITHUB_PATH
|
run: echo "${HOME}/.local/bin" >> $GITHUB_PATH
|
||||||
|
|
||||||
- name: Append to Path on Windows
|
- name: Append to Path on Windows
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
run: echo "${HOME}/.local/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
run: echo "${HOME}/.local/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
|
||||||
@ -73,25 +101,31 @@ jobs:
|
|||||||
- name: Install manim dependencies on MacOs
|
- name: Install manim dependencies on MacOs
|
||||||
if: matrix.os == 'macos-latest' && matrix.manim == 'manim'
|
if: matrix.os == 'macos-latest' && matrix.manim == 'manim'
|
||||||
run: brew install ffmpeg py3cairo
|
run: brew install ffmpeg py3cairo
|
||||||
|
|
||||||
- name: Install manimgl dependencies on MacOS
|
- name: Install manimgl dependencies on MacOS
|
||||||
if: matrix.os == 'macos-latest' && matrix.manim == 'manimgl'
|
if: matrix.os == 'macos-latest' && matrix.manim == 'manimgl'
|
||||||
run: brew install ffmpeg
|
run: brew install ffmpeg
|
||||||
|
|
||||||
- name: Run apt-get update on Ubuntu
|
- name: Run apt-get update on Ubuntu
|
||||||
if: matrix.os == 'ubuntu-latest'
|
if: matrix.os == 'ubuntu-latest'
|
||||||
run: sudo apt-get update
|
run: sudo apt-get update
|
||||||
|
|
||||||
- name: Install manim dependencies on Ubuntu
|
- name: Install manim dependencies on Ubuntu
|
||||||
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manim'
|
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manim'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install libcairo2-dev libpango1.0-dev ffmpeg freeglut3-dev
|
sudo apt-get install libcairo2-dev libpango1.0-dev ffmpeg freeglut3-dev
|
||||||
|
|
||||||
- name: Install manimgl dependencies on Ubuntu
|
- name: Install manimgl dependencies on Ubuntu
|
||||||
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manimgl'
|
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manimgl'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install libpango1.0-dev ffmpeg freeglut3-dev
|
sudo apt-get install libpango1.0-dev ffmpeg freeglut3-dev
|
||||||
|
|
||||||
- name: Install xvfb on Ubuntu
|
- name: Install xvfb on Ubuntu
|
||||||
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manimgl'
|
if: matrix.os == 'ubuntu-latest' && matrix.manim == 'manimgl'
|
||||||
run: |
|
run: |
|
||||||
sudo apt-get install xvfb
|
sudo apt-get install xvfb
|
||||||
nohup Xvfb $DISPLAY &
|
nohup Xvfb $DISPLAY &
|
||||||
|
|
||||||
- name: Install Windows dependencies
|
- name: Install Windows dependencies
|
||||||
if: matrix.os == 'windows-latest'
|
if: matrix.os == 'windows-latest'
|
||||||
run: choco install ffmpeg
|
run: choco install ffmpeg
|
||||||
@ -99,12 +133,13 @@ jobs:
|
|||||||
# Install Manim Slides
|
# Install Manim Slides
|
||||||
- name: Install Manim Slides
|
- name: Install Manim Slides
|
||||||
run: |
|
run: |
|
||||||
poetry install --with test
|
poetry install --extras ${{ matrix.manim }}
|
||||||
|
|
||||||
# Render slides
|
# Render slides
|
||||||
- name: Render slides
|
- name: Render slides
|
||||||
if: matrix.manim == 'manim'
|
if: matrix.manim == 'manim'
|
||||||
run: poetry run manim -ql example.py BasicExample ThreeDExample
|
run: poetry run manim -ql example.py BasicExample ThreeDExample
|
||||||
|
|
||||||
- name: Render slides
|
- name: Render slides
|
||||||
if: matrix.manim == 'manimgl'
|
if: matrix.manim == 'manimgl'
|
||||||
run: poetry run -v manimgl -l example.py BasicExample ThreeDExample
|
run: poetry run -v manimgl -l example.py BasicExample ThreeDExample
|
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,6 +1,5 @@
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
/env
|
/env
|
||||||
/tests
|
|
||||||
/build
|
/build
|
||||||
/dist
|
/dist
|
||||||
*.egg-info/
|
*.egg-info/
|
||||||
|
@ -5,10 +5,10 @@ import subprocess
|
|||||||
import tempfile
|
import tempfile
|
||||||
from enum import Enum
|
from enum import Enum
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Dict, List, Optional, Set, Tuple, Union
|
from typing import Any, Dict, List, Optional, Set, Tuple, Union
|
||||||
|
|
||||||
from pydantic import BaseModel, FilePath, PositiveInt, root_validator, validator
|
from pydantic import BaseModel, FilePath, PositiveInt, field_validator, model_validator
|
||||||
from pydantic.color import Color
|
from pydantic_extra_types.color import Color
|
||||||
from PySide6.QtCore import Qt
|
from PySide6.QtCore import Qt
|
||||||
|
|
||||||
from .defaults import FFMPEG_BIN
|
from .defaults import FFMPEG_BIN
|
||||||
@ -38,18 +38,19 @@ def merge_basenames(files: List[FilePath]) -> Path:
|
|||||||
class Key(BaseModel): # type: ignore
|
class Key(BaseModel): # type: ignore
|
||||||
"""Represents a list of key codes, with optionally a name."""
|
"""Represents a list of key codes, with optionally a name."""
|
||||||
|
|
||||||
ids: Set[int]
|
ids: Set[PositiveInt]
|
||||||
name: Optional[str] = None
|
name: Optional[str] = None
|
||||||
|
|
||||||
|
@field_validator("ids")
|
||||||
|
@classmethod
|
||||||
|
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
|
||||||
|
|
||||||
def set_ids(self, *ids: int) -> None:
|
def set_ids(self, *ids: int) -> None:
|
||||||
self.ids = set(ids)
|
self.ids = set(ids)
|
||||||
|
|
||||||
@validator("ids", each_item=True)
|
|
||||||
def id_is_posint(cls, v: int) -> int:
|
|
||||||
if v < 0:
|
|
||||||
raise ValueError("Key ids cannot be negative integers")
|
|
||||||
return v
|
|
||||||
|
|
||||||
def match(self, key_id: int) -> bool:
|
def match(self, key_id: int) -> bool:
|
||||||
m = key_id in self.ids
|
m = key_id in self.ids
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ class Config(BaseModel): # type: ignore
|
|||||||
PLAY_PAUSE: Key = Key(ids=[Qt.Key_Space], name="PLAY / PAUSE")
|
PLAY_PAUSE: Key = Key(ids=[Qt.Key_Space], name="PLAY / PAUSE")
|
||||||
HIDE_MOUSE: Key = Key(ids=[Qt.Key_H], name="HIDE / SHOW MOUSE")
|
HIDE_MOUSE: Key = Key(ids=[Qt.Key_H], name="HIDE / SHOW MOUSE")
|
||||||
|
|
||||||
@root_validator
|
@model_validator(mode="before")
|
||||||
def ids_are_unique_across_keys(cls, values: Dict[str, Key]) -> Dict[str, Key]:
|
def ids_are_unique_across_keys(cls, values: Dict[str, Key]) -> Dict[str, Key]:
|
||||||
ids: Set[int] = set()
|
ids: Set[int] = set()
|
||||||
|
|
||||||
@ -105,19 +106,21 @@ class SlideConfig(BaseModel): # type: ignore
|
|||||||
number: int
|
number: int
|
||||||
terminated: bool = False
|
terminated: bool = False
|
||||||
|
|
||||||
@validator("start_animation", "end_animation")
|
@field_validator("start_animation", "end_animation")
|
||||||
|
@classmethod
|
||||||
def index_is_posint(cls, v: int) -> int:
|
def index_is_posint(cls, v: int) -> int:
|
||||||
if v < 0:
|
if v < 0:
|
||||||
raise ValueError("Animation index (start or end) cannot be negative")
|
raise ValueError("Animation index (start or end) cannot be negative")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@validator("number")
|
@field_validator("number")
|
||||||
|
@classmethod
|
||||||
def number_is_strictly_posint(cls, v: int) -> int:
|
def number_is_strictly_posint(cls, v: int) -> int:
|
||||||
if v <= 0:
|
if v <= 0:
|
||||||
raise ValueError("Slide number cannot be negative or zero")
|
raise ValueError("Slide number cannot be negative or zero")
|
||||||
return v
|
return v
|
||||||
|
|
||||||
@root_validator
|
@model_validator(mode="before")
|
||||||
def start_animation_is_before_end(
|
def start_animation_is_before_end(
|
||||||
cls, values: Dict[str, Union[SlideType, int, bool]]
|
cls, values: Dict[str, Union[SlideType, int, bool]]
|
||||||
) -> Dict[str, Union[SlideType, int, bool]]:
|
) -> Dict[str, Union[SlideType, int, bool]]:
|
||||||
@ -153,15 +156,12 @@ class PresentationConfig(BaseModel): # type: ignore
|
|||||||
resolution: Tuple[PositiveInt, PositiveInt] = (1920, 1080)
|
resolution: Tuple[PositiveInt, PositiveInt] = (1920, 1080)
|
||||||
background_color: Color = "black"
|
background_color: Color = "black"
|
||||||
|
|
||||||
@root_validator
|
@model_validator(mode="after")
|
||||||
def animation_indices_match_files(
|
def animation_indices_match_files(
|
||||||
cls, values: Dict[str, Union[List[SlideConfig], List[FilePath]]]
|
cls, config: "PresentationConfig"
|
||||||
) -> Dict[str, Union[List[SlideConfig], List[FilePath]]]:
|
) -> "PresentationConfig":
|
||||||
files: List[FilePath] = values.get("files") # type: ignore
|
files = config.files
|
||||||
slides: List[SlideConfig] = values.get("slides") # type: ignore
|
slides = config.slides
|
||||||
|
|
||||||
if files is None or slides is None:
|
|
||||||
return values
|
|
||||||
|
|
||||||
n_files = len(files)
|
n_files = len(files)
|
||||||
|
|
||||||
@ -171,7 +171,7 @@ class PresentationConfig(BaseModel): # type: ignore
|
|||||||
f"The following slide's contains animations not listed in files {files}: {slide}"
|
f"The following slide's contains animations not listed in files {files}: {slide}"
|
||||||
)
|
)
|
||||||
|
|
||||||
return values
|
return config
|
||||||
|
|
||||||
def copy_to(self, dest: Path, use_cached: bool = True) -> "PresentationConfig":
|
def copy_to(self, dest: Path, use_cached: bool = True) -> "PresentationConfig":
|
||||||
"""
|
"""
|
||||||
|
@ -15,7 +15,16 @@ import pptx
|
|||||||
from click import Context, Parameter
|
from click import Context, Parameter
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
from PIL import Image
|
from PIL import Image
|
||||||
from pydantic import BaseModel, FilePath, PositiveFloat, PositiveInt, ValidationError
|
from pydantic import (
|
||||||
|
BaseModel,
|
||||||
|
ConfigDict,
|
||||||
|
FilePath,
|
||||||
|
GetCoreSchemaHandler,
|
||||||
|
PositiveFloat,
|
||||||
|
PositiveInt,
|
||||||
|
ValidationError,
|
||||||
|
)
|
||||||
|
from pydantic_core import CoreSchema, core_schema
|
||||||
from tqdm import tqdm
|
from tqdm import tqdm
|
||||||
|
|
||||||
from . import data
|
from . import data
|
||||||
@ -87,6 +96,12 @@ class Str(str):
|
|||||||
# This fixes pickling issue on Python 3.8
|
# This fixes pickling issue on Python 3.8
|
||||||
__reduce_ex__ = str.__reduce_ex__
|
__reduce_ex__ = str.__reduce_ex__
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def __get_pydantic_core_schema__(
|
||||||
|
cls, source_type: Any, handler: GetCoreSchemaHandler
|
||||||
|
) -> CoreSchema:
|
||||||
|
return core_schema.str_schema()
|
||||||
|
|
||||||
def __str__(self) -> str:
|
def __str__(self) -> str:
|
||||||
"""Ensures that the string is correctly quoted."""
|
"""Ensures that the string is correctly quoted."""
|
||||||
if self in ["true", "false", "null"]:
|
if self in ["true", "false", "null"]:
|
||||||
@ -304,10 +319,7 @@ class RevealJS(Converter):
|
|||||||
reveal_version: str = "4.4.0"
|
reveal_version: str = "4.4.0"
|
||||||
reveal_theme: RevealTheme = RevealTheme.black
|
reveal_theme: RevealTheme = RevealTheme.black
|
||||||
title: str = "Manim Slides"
|
title: str = "Manim Slides"
|
||||||
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
||||||
class Config:
|
|
||||||
use_enum_values = True
|
|
||||||
extra = "forbid"
|
|
||||||
|
|
||||||
def get_sections_iter(self, assets_dir: Path) -> Generator[str, None, None]:
|
def get_sections_iter(self, assets_dir: Path) -> Generator[str, None, None]:
|
||||||
"""Generates a sequence of sections, one per slide, that will be included into the html template."""
|
"""Generates a sequence of sections, one per slide, that will be included into the html template."""
|
||||||
@ -377,10 +389,7 @@ class FrameIndex(str, Enum):
|
|||||||
class PDF(Converter):
|
class PDF(Converter):
|
||||||
frame_index: FrameIndex = FrameIndex.last
|
frame_index: FrameIndex = FrameIndex.last
|
||||||
resolution: PositiveFloat = 100.0
|
resolution: PositiveFloat = 100.0
|
||||||
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
||||||
class Config:
|
|
||||||
use_enum_values = True
|
|
||||||
extra = "forbid"
|
|
||||||
|
|
||||||
def open(self, file: Path) -> None:
|
def open(self, file: Path) -> None:
|
||||||
return open_with_default(file)
|
return open_with_default(file)
|
||||||
@ -432,10 +441,7 @@ class PowerPoint(Converter):
|
|||||||
height: PositiveInt = 720
|
height: PositiveInt = 720
|
||||||
auto_play_media: bool = True
|
auto_play_media: bool = True
|
||||||
poster_frame_image: Optional[FilePath] = None
|
poster_frame_image: Optional[FilePath] = None
|
||||||
|
model_config = ConfigDict(use_enum_values=True, extra="forbid")
|
||||||
class Config:
|
|
||||||
use_enum_values = True
|
|
||||||
extra = "forbid"
|
|
||||||
|
|
||||||
def open(self, file: Path) -> None:
|
def open(self, file: Path) -> None:
|
||||||
return open_with_default(file)
|
return open_with_default(file)
|
||||||
|
@ -11,7 +11,7 @@ import cv2
|
|||||||
import numpy as np
|
import numpy as np
|
||||||
from click import Context, Parameter
|
from click import Context, Parameter
|
||||||
from pydantic import ValidationError
|
from pydantic import ValidationError
|
||||||
from pydantic.color import Color
|
from pydantic_extra_types.color import Color
|
||||||
from PySide6.QtCore import Qt, QThread, Signal, Slot
|
from PySide6.QtCore import Qt, QThread, Signal, Slot
|
||||||
from PySide6.QtGui import QCloseEvent, QIcon, QImage, QKeyEvent, QPixmap, QResizeEvent
|
from PySide6.QtGui import QCloseEvent, QIcon, QImage, QKeyEvent, QPixmap, QResizeEvent
|
||||||
from PySide6.QtWidgets import QApplication, QGridLayout, QLabel, QWidget
|
from PySide6.QtWidgets import QApplication, QGridLayout, QLabel, QWidget
|
||||||
|
@ -333,7 +333,7 @@ class Slide(Scene): # type:ignore
|
|||||||
files=files,
|
files=files,
|
||||||
resolution=self.__resolution,
|
resolution=self.__resolution,
|
||||||
background_color=self.__background_color,
|
background_color=self.__background_color,
|
||||||
).json(indent=2)
|
).model_dump_json(indent=2)
|
||||||
)
|
)
|
||||||
|
|
||||||
logger.info(
|
logger.info(
|
||||||
|
645
poetry.lock
generated
645
poetry.lock
generated
File diff suppressed because it is too large
Load Diff
@ -54,7 +54,8 @@ manimgl = {version = "^1.6.1", optional = true}
|
|||||||
numpy = "^1.19"
|
numpy = "^1.19"
|
||||||
opencv-python = "^4.6.0.66"
|
opencv-python = "^4.6.0.66"
|
||||||
pillow = "^9.5.0"
|
pillow = "^9.5.0"
|
||||||
pydantic = "^1.10.2"
|
pydantic = "^2.0.1"
|
||||||
|
pydantic-extra-types = "^2.0.0"
|
||||||
pyside6 = "^6.5.1.1"
|
pyside6 = "^6.5.1.1"
|
||||||
python = ">=3.8.1,<3.12"
|
python = ">=3.8.1,<3.12"
|
||||||
python-pptx = "^0.6.21"
|
python-pptx = "^0.6.21"
|
||||||
@ -84,8 +85,7 @@ sphinx-copybutton = "^0.5.1"
|
|||||||
sphinxext-opengraph = "^0.7.5"
|
sphinxext-opengraph = "^0.7.5"
|
||||||
|
|
||||||
[tool.poetry.group.test.dependencies]
|
[tool.poetry.group.test.dependencies]
|
||||||
manim = "^0.17.0"
|
pytest = "^7.4.0"
|
||||||
manimgl = "^1.6.1"
|
|
||||||
|
|
||||||
[tool.poetry.plugins]
|
[tool.poetry.plugins]
|
||||||
|
|
||||||
|
99
tests/test_config.py
Normal file
99
tests/test_config.py
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
import random
|
||||||
|
import string
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
from typing import Any, Generator, List
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
from pydantic import ValidationError
|
||||||
|
|
||||||
|
from manim_slides.config import (
|
||||||
|
Key,
|
||||||
|
PresentationConfig,
|
||||||
|
SlideConfig,
|
||||||
|
SlideType,
|
||||||
|
merge_basenames,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def random_path(
|
||||||
|
length: int = 20,
|
||||||
|
dirname: Path = Path("./media/videos/example"),
|
||||||
|
suffix: str = ".mp4",
|
||||||
|
touch: bool = False,
|
||||||
|
) -> Path:
|
||||||
|
basename = "".join(random.choices(string.ascii_letters, k=length))
|
||||||
|
|
||||||
|
filepath = dirname.joinpath(basename + suffix)
|
||||||
|
|
||||||
|
if touch:
|
||||||
|
filepath.touch()
|
||||||
|
|
||||||
|
return filepath
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def paths() -> Generator[List[Path], None, None]:
|
||||||
|
random.seed(1234)
|
||||||
|
|
||||||
|
yield [random_path() for _ in range(20)]
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture
|
||||||
|
def presentation_config(paths: List[Path]) -> Generator[PresentationConfig, None, None]:
|
||||||
|
dirname = Path(tempfile.mkdtemp())
|
||||||
|
files = [random_path(dirname=dirname, touch=True) for _ in range(10)]
|
||||||
|
|
||||||
|
slides = [
|
||||||
|
SlideConfig(
|
||||||
|
type=SlideType.slide,
|
||||||
|
start_animation=0,
|
||||||
|
end_animation=5,
|
||||||
|
number=1,
|
||||||
|
),
|
||||||
|
SlideConfig(
|
||||||
|
type=SlideType.loop,
|
||||||
|
start_animation=5,
|
||||||
|
end_animation=6,
|
||||||
|
number=2,
|
||||||
|
),
|
||||||
|
SlideConfig(
|
||||||
|
type=SlideType.last,
|
||||||
|
start_animation=6,
|
||||||
|
end_animation=10,
|
||||||
|
number=3,
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
|
yield PresentationConfig(
|
||||||
|
slides=slides,
|
||||||
|
files=files,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def test_merge_basenames(paths: List[Path]) -> None:
|
||||||
|
path = merge_basenames(paths)
|
||||||
|
assert path.suffix == paths[0].suffix
|
||||||
|
assert path.parent == paths[0].parent
|
||||||
|
|
||||||
|
|
||||||
|
class TestKey:
|
||||||
|
@pytest.mark.parametrize(("ids", "name"), [([1], None), ([1], "some key name")])
|
||||||
|
def test_valid_keys(self, ids: Any, name: Any) -> None:
|
||||||
|
_ = Key(ids=ids, name=name)
|
||||||
|
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("ids", "name"), [([], None), ([-1], None), ([1], {"an": " invalid name"})]
|
||||||
|
)
|
||||||
|
def test_invalid_keys(self, ids: Any, name: Any) -> None:
|
||||||
|
with pytest.raises(ValidationError):
|
||||||
|
_ = Key(ids=ids, name=name)
|
||||||
|
|
||||||
|
|
||||||
|
class TestPresentationConfig:
|
||||||
|
def test_validate(self, presentation_config: PresentationConfig) -> None:
|
||||||
|
obj = presentation_config.model_dump()
|
||||||
|
_ = PresentationConfig.model_validate(obj)
|
||||||
|
|
||||||
|
def test_bump_to_json(self, presentation_config: PresentationConfig) -> None:
|
||||||
|
_ = presentation_config.model_dump_json(indent=2)
|
11
tests/test_convert.py
Normal file
11
tests/test_convert.py
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import pytest
|
||||||
|
|
||||||
|
from manim_slides.convert import PDF, Converter, PowerPoint, RevealJS
|
||||||
|
|
||||||
|
|
||||||
|
class TestConverter:
|
||||||
|
@pytest.mark.parametrize(
|
||||||
|
("name", "converter"), [("html", RevealJS), ("pdf", PDF), ("pptx", PowerPoint)]
|
||||||
|
)
|
||||||
|
def test_from_string(self, name: str, converter: type) -> None:
|
||||||
|
assert Converter.from_string(name) == converter
|
Reference in New Issue
Block a user