feat(convert): support base64 encoded videos (#236)

* feat(convert): support base64 encoded videos

Thanks to @t-fritsch, Manim Slides can now convert to a fully self-contained HTML presentation using base64 encoded videos!

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

* chore(lib): explicit decode type

* feat(lib): auto detect mime type

* [pre-commit.ci] auto fixes from pre-commit.com hooks

for more information, see https://pre-commit.ci

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
This commit is contained in:
Jérome Eertmans
2023-08-06 19:06:43 +02:00
committed by GitHub
parent dec2f5e724
commit d282766f2d
5 changed files with 346 additions and 215 deletions

View File

@ -43,7 +43,7 @@ jobs:
poetry install --with test poetry install --with test
- name: Run pytest - name: Run pytest
run: poetry run pytest run: poetry run pytest -x
build-examples: build-examples:
strategy: strategy:

View File

@ -4,6 +4,7 @@ import subprocess
import sys import sys
import tempfile import tempfile
import webbrowser import webbrowser
from base64 import b64encode
from enum import Enum from enum import Enum
from importlib import resources from importlib import resources
from pathlib import Path from pathlib import Path
@ -33,6 +34,29 @@ from .config import PresentationConfig
from .logger import logger from .logger import logger
from .present import get_scenes_presentation_config from .present import get_scenes_presentation_config
DATA_URI_FIX = r"""
// Fix found by @t-fritsch on GitHub
// see: https://github.com/hakimel/reveal.js/discussions/3362#discussioncomment-6651475.
function fixBase64VideoBackground(event) {
// event.previousSlide, event.currentSlide, event.indexh, event.indexv
if (event.currentSlide.getAttribute('data-background-video')) {
const background = Reveal.getSlideBackground(event.indexh, event.indexv),
video = background.querySelector('video'),
sources = video.querySelectorAll('source');
sources.forEach((source, i) => {
const src = source.getAttribute('src');
if(src.match(/^data:video.*;base64$/)){
const nextSrc = sources[i+1]?.getAttribute('src');
video.setAttribute('src', `${src},${nextSrc}`);
}
});
}
}
Reveal.on( 'ready', fixBase64VideoBackground );
Reveal.on( 'slidechanged', fixBase64VideoBackground );
"""
def open_with_default(file: Path) -> None: def open_with_default(file: Path) -> None:
system = platform.system() system = platform.system()
@ -61,6 +85,25 @@ def validate_config_option(
return config return config
def get_file_mime_type(file: Path) -> str:
ext = file.suffix.lower()
if ext == ".mp4":
return "video/mp4"
else:
return "video/webm"
def data_uri(file: Path) -> str:
"""
Reads a video and returns the corresponding data-uri.
"""
b64 = b64encode(file.read_bytes()).decode("ascii")
mime_type = get_file_mime_type(file)
return f"data:{mime_type};base64,{b64}"
class Converter(BaseModel): # type: ignore class Converter(BaseModel): # type: ignore
presentation_configs: List[PresentationConfig] = [] presentation_configs: List[PresentationConfig] = []
assets_dir: str = "{basename}_assets" assets_dir: str = "{basename}_assets"
@ -238,6 +281,8 @@ class RevealTheme(str, Enum):
class RevealJS(Converter): class RevealJS(Converter):
# Export option: use data-uri
data_uri: bool = False
# Presentation size options from RevealJS # Presentation size options from RevealJS
width: Union[Str, int] = Str("100%") width: Union[Str, int] = Str("100%")
height: Union[Str, int] = Str("100%") height: Union[Str, int] = Str("100%")
@ -326,10 +371,14 @@ class RevealJS(Converter):
for presentation_config in self.presentation_configs: for presentation_config in self.presentation_configs:
for slide_config in presentation_config.slides: for slide_config in presentation_config.slides:
file = presentation_config.files[slide_config.start_animation] file = presentation_config.files[slide_config.start_animation]
file = assets_dir / file.name
logger.debug(f"Writing video section with file {file}") logger.debug(f"Writing video section with file {file}")
if self.data_uri:
file = data_uri(file)
else:
file = assets_dir / file.name
# TODO: document this # TODO: document this
# Videos are muted because, otherwise, the first slide never plays correctly. # Videos are muted because, otherwise, the first slide never plays correctly.
# This is due to a restriction in playing audio without the user doing anything. # This is due to a restriction in playing audio without the user doing anything.
@ -356,27 +405,38 @@ class RevealJS(Converter):
def convert_to(self, dest: Path) -> None: def convert_to(self, dest: Path) -> None:
"""Converts this configuration into a RevealJS HTML presentation, saved to DEST.""" """Converts this configuration into a RevealJS HTML presentation, saved to DEST."""
dirname = dest.parent if self.data_uri:
basename = dest.stem assets_dir = Path("") # Actually we won't care.
ext = dest.suffix else:
dirname = dest.parent
basename = dest.stem
ext = dest.suffix
assets_dir = Path( assets_dir = Path(
self.assets_dir.format(dirname=dirname, basename=basename, ext=ext) self.assets_dir.format(dirname=dirname, basename=basename, ext=ext)
) )
full_assets_dir = dirname / assets_dir full_assets_dir = dirname / assets_dir
logger.debug(f"Assets will be saved to: {full_assets_dir}") logger.debug(f"Assets will be saved to: {full_assets_dir}")
os.makedirs(full_assets_dir, exist_ok=True) os.makedirs(full_assets_dir, exist_ok=True)
for presentation_config in self.presentation_configs: for presentation_config in self.presentation_configs:
presentation_config.concat_animations().copy_to(full_assets_dir) presentation_config.concat_animations().copy_to(full_assets_dir)
with open(dest, "w") as f: with open(dest, "w") as f:
sections = "".join(self.get_sections_iter(assets_dir)) sections = "".join(self.get_sections_iter(assets_dir))
revealjs_template = self.load_template() revealjs_template = self.load_template()
content = revealjs_template.format(sections=sections, **self.dict())
if self.data_uri:
data_uri_fix = DATA_URI_FIX
else:
data_uri_fix = ""
content = revealjs_template.format(
sections=sections, data_uri_fix=data_uri_fix, **self.dict()
)
f.write(content) f.write(content)
@ -496,6 +556,8 @@ class PowerPoint(Converter):
): ):
file = presentation_config.files[slide_config.start_animation] file = presentation_config.files[slide_config.start_animation]
mime_type = get_file_mime_type(file)
if self.poster_frame_image is None: if self.poster_frame_image is None:
poster_frame_image = save_first_image_from_video_file(file) poster_frame_image = save_first_image_from_video_file(file)
else: else:
@ -509,7 +571,7 @@ class PowerPoint(Converter):
self.width * 9525, self.width * 9525,
self.height * 9525, self.height * 9525,
poster_frame_image=poster_frame_image, poster_frame_image=poster_frame_image,
mime_type="video/mp4", mime_type=mime_type,
) )
if self.auto_play_media: if self.auto_play_media:
auto_play_media(movie, loop=slide_config.is_loop()) auto_play_media(movie, loop=slide_config.is_loop())

View File

@ -30,261 +30,260 @@
<!-- <script src="index.js"></script> --> <!-- <script src="index.js"></script> -->
<script> <script>
Reveal.initialize({{ Reveal.initialize({{
// The "normal" size of the presentation, aspect ratio will
// be preserved when the presentation is scaled to fit different
// resolutions. Can be specified using percentage units.
width: {width},
height: {height},
// The "normal" size of the presentation, aspect ratio will // Factor of the display size that should remain empty around
// be preserved when the presentation is scaled to fit different // the content
// resolutions. Can be specified using percentage units. margin: {margin},
width: {width},
height: {height},
// Factor of the display size that should remain empty around // Bounds for smallest/largest possible scale to apply to content
// the content minScale: {min_scale},
margin: {margin}, maxScale: {max_scale},
// Bounds for smallest/largest possible scale to apply to content // Display presentation control arrows
minScale: {min_scale}, controls: {controls},
maxScale: {max_scale},
// Display presentation control arrows // Help the user learn the controls by providing hints, for example by
controls: {controls}, // bouncing the down arrow when they first encounter a vertical slide
controlsTutorial: {controls_tutorial},
// Help the user learn the controls by providing hints, for example by // Determines where controls appear, "edges" or "bottom-right"
// bouncing the down arrow when they first encounter a vertical slide controlsLayout: {controls_layout},
controlsTutorial: {controls_tutorial},
// Determines where controls appear, "edges" or "bottom-right" // Visibility rule for backwards navigation arrows; "faded", "hidden"
controlsLayout: {controls_layout}, // or "visible"
controlsBackArrows: {controls_back_arrows},
// Visibility rule for backwards navigation arrows; "faded", "hidden" // Display a presentation progress bar
// or "visible" progress: {progress},
controlsBackArrows: {controls_back_arrows},
// Display a presentation progress bar // Display the page number of the current slide
progress: {progress}, // - true: Show slide number
// - false: Hide slide number
//
// Can optionally be set as a string that specifies the number formatting:
// - "h.v": Horizontal . vertical slide number (default)
// - "h/v": Horizontal / vertical slide number
// - "c": Flattened slide number
// - "c/t": Flattened slide number / total slides
//
// Alternatively, you can provide a function that returns the slide
// number for the current slide. The function should take in a slide
// object and return an array with one string [slideNumber] or
// three strings [n1,delimiter,n2]. See #formatSlideNumber().
slideNumber: {slide_number},
// Display the page number of the current slide // Can be used to limit the contexts in which the slide number appears
// - true: Show slide number // - "all": Always show the slide number
// - false: Hide slide number // - "print": Only when printing to PDF
// // - "speaker": Only in the speaker view
// Can optionally be set as a string that specifies the number formatting: showSlideNumber: {show_slide_number},
// - "h.v": Horizontal . vertical slide number (default)
// - "h/v": Horizontal / vertical slide number
// - "c": Flattened slide number
// - "c/t": Flattened slide number / total slides
//
// Alternatively, you can provide a function that returns the slide
// number for the current slide. The function should take in a slide
// object and return an array with one string [slideNumber] or
// three strings [n1,delimiter,n2]. See #formatSlideNumber().
slideNumber: {slide_number},
// Can be used to limit the contexts in which the slide number appears // Use 1 based indexing for # links to match slide number (default is zero
// - "all": Always show the slide number // based)
// - "print": Only when printing to PDF hashOneBasedIndex: {hash_one_based_index},
// - "speaker": Only in the speaker view
showSlideNumber: {show_slide_number},
// Use 1 based indexing for # links to match slide number (default is zero // Add the current slide number to the URL hash so that reloading the
// based) // page/copying the URL will return you to the same slide
hashOneBasedIndex: {hash_one_based_index}, hash: {hash},
// Add the current slide number to the URL hash so that reloading the // Flags if we should monitor the hash and change slides accordingly
// page/copying the URL will return you to the same slide respondToHashChanges: {respond_to_hash_changes},
hash: {hash},
// Flags if we should monitor the hash and change slides accordingly // Push each slide change to the browser history. Implies `hash: true`
respondToHashChanges: {respond_to_hash_changes}, history: {history},
// Push each slide change to the browser history. Implies `hash: true` // Enable keyboard shortcuts for navigation
history: {history}, keyboard: {keyboard},
// Enable keyboard shortcuts for navigation // Optional function that blocks keyboard events when retuning false
keyboard: {keyboard}, //
// If you set this to 'focused', we will only capture keyboard events
// for embedded decks when they are in focus
keyboardCondition: {keyboard_condition},
// Optional function that blocks keyboard events when retuning false // Disables the default reveal.js slide layout (scaling and centering)
// // so that you can use custom CSS layout
// If you set this to 'focused', we will only capture keyboard events disableLayout: {disable_layout},
// for embedded decks when they are in focus
keyboardCondition: {keyboard_condition},
// Disables the default reveal.js slide layout (scaling and centering) // Enable the slide overview mode
// so that you can use custom CSS layout overview: {overview},
disableLayout: {disable_layout},
// Enable the slide overview mode // Vertical centering of slides
overview: {overview}, center: {center},
// Vertical centering of slides // Enables touch navigation on devices with touch input
center: {center}, touch: {touch},
// Enables touch navigation on devices with touch input // Loop the presentation
touch: {touch}, loop: {loop},
// Loop the presentation // Change the presentation direction to be RTL
loop: {loop}, rtl: {rtl},
// Change the presentation direction to be RTL // Changes the behavior of our navigation directions.
rtl: {rtl}, //
// "default"
// Left/right arrow keys step between horizontal slides, up/down
// arrow keys step between vertical slides. Space key steps through
// all slides (both horizontal and vertical).
//
// "linear"
// Removes the up/down arrows. Left/right arrows step through all
// slides (both horizontal and vertical).
//
// "grid"
// When this is enabled, stepping left/right from a vertical stack
// to an adjacent vertical stack will land you at the same vertical
// index.
//
// Consider a deck with six slides ordered in two vertical stacks:
// 1.1 2.1
// 1.2 2.2
// 1.3 2.3
//
// If you're on slide 1.3 and navigate right, you will normally move
// from 1.3 -> 2.1. If "grid" is used, the same navigation takes you
// from 1.3 -> 2.3.
navigationMode: {navigation_mode},
// Changes the behavior of our navigation directions. // Randomizes the order of slides each time the presentation loads
// shuffle: {shuffle},
// "default"
// Left/right arrow keys step between horizontal slides, up/down
// arrow keys step between vertical slides. Space key steps through
// all slides (both horizontal and vertical).
//
// "linear"
// Removes the up/down arrows. Left/right arrows step through all
// slides (both horizontal and vertical).
//
// "grid"
// When this is enabled, stepping left/right from a vertical stack
// to an adjacent vertical stack will land you at the same vertical
// index.
//
// Consider a deck with six slides ordered in two vertical stacks:
// 1.1 2.1
// 1.2 2.2
// 1.3 2.3
//
// If you're on slide 1.3 and navigate right, you will normally move
// from 1.3 -> 2.1. If "grid" is used, the same navigation takes you
// from 1.3 -> 2.3.
navigationMode: {navigation_mode},
// Randomizes the order of slides each time the presentation loads // Turns fragments on and off globally
shuffle: {shuffle}, fragments: {fragments},
// Turns fragments on and off globally // Flags whether to include the current fragment in the URL,
fragments: {fragments}, // so that reloading brings you to the same fragment position
fragmentInURL: {fragment_in_url},
// Flags whether to include the current fragment in the URL, // Flags if the presentation is running in an embedded mode,
// so that reloading brings you to the same fragment position // i.e. contained within a limited portion of the screen
fragmentInURL: {fragment_in_url}, embedded: {embedded},
// Flags if the presentation is running in an embedded mode, // Flags if we should show a help overlay when the question-mark
// i.e. contained within a limited portion of the screen // key is pressed
embedded: {embedded}, help: {help},
// Flags if we should show a help overlay when the question-mark // Flags if it should be possible to pause the presentation (blackout)
// key is pressed pause: {pause},
help: {help},
// Flags if it should be possible to pause the presentation (blackout) // Flags if speaker notes should be visible to all viewers
pause: {pause}, showNotes: {show_notes},
// Flags if speaker notes should be visible to all viewers // Global override for autolaying embedded media (video/audio/iframe)
showNotes: {show_notes}, // - null: Media will only autoplay if data-autoplay is present
// - true: All media will autoplay, regardless of individual setting
// - false: No media will autoplay, regardless of individual setting
autoPlayMedia: {auto_play_media},
// Global override for autolaying embedded media (video/audio/iframe) // Global override for preloading lazy-loaded iframes
// - null: Media will only autoplay if data-autoplay is present // - null: Iframes with data-src AND data-preload will be loaded when within
// - true: All media will autoplay, regardless of individual setting // the viewDistance, iframes with only data-src will be loaded when visible
// - false: No media will autoplay, regardless of individual setting // - true: All iframes with data-src will be loaded when within the viewDistance
autoPlayMedia: {auto_play_media}, // - false: All iframes with data-src will be loaded only when visible
preloadIframes: {preload_iframes},
// Global override for preloading lazy-loaded iframes // Can be used to globally disable auto-animation
// - null: Iframes with data-src AND data-preload will be loaded when within autoAnimate: {auto_animate},
// the viewDistance, iframes with only data-src will be loaded when visible
// - true: All iframes with data-src will be loaded when within the viewDistance
// - false: All iframes with data-src will be loaded only when visible
preloadIframes: {preload_iframes},
// Can be used to globally disable auto-animation // Optionally provide a custom element matcher that will be
autoAnimate: {auto_animate}, // used to dictate which elements we can animate between.
autoAnimateMatcher: {auto_animate_matcher},
// Optionally provide a custom element matcher that will be // Default settings for our auto-animate transitions, can be
// used to dictate which elements we can animate between. // overridden per-slide or per-element via data arguments
autoAnimateMatcher: {auto_animate_matcher}, autoAnimateEasing: {auto_animate_easing},
autoAnimateDuration: {auto_animate_duration},
autoAnimateUnmatched: {auto_animate_unmatched},
// Default settings for our auto-animate transitions, can be // CSS properties that can be auto-animated. Position & scale
// overridden per-slide or per-element via data arguments // is matched separately so there's no need to include styles
autoAnimateEasing: {auto_animate_easing}, // like top/right/bottom/left, width/height or margin.
autoAnimateDuration: {auto_animate_duration}, autoAnimateStyles: {auto_animate_styles},
autoAnimateUnmatched: {auto_animate_unmatched},
// CSS properties that can be auto-animated. Position & scale // Controls automatic progression to the next slide
// is matched separately so there's no need to include styles // - 0: Auto-sliding only happens if the data-autoslide HTML attribute
// like top/right/bottom/left, width/height or margin. // is present on the current slide or fragment
autoAnimateStyles: {auto_animate_styles}, // - 1+: All slides will progress automatically at the given interval
// - false: No auto-sliding, even if data-autoslide is present
autoSlide: {auto_slide},
// Controls automatic progression to the next slide // Stop auto-sliding after user input
// - 0: Auto-sliding only happens if the data-autoslide HTML attribute autoSlideStoppable: {auto_slide_stoppable},
// is present on the current slide or fragment
// - 1+: All slides will progress automatically at the given interval
// - false: No auto-sliding, even if data-autoslide is present
autoSlide: {auto_slide},
// Stop auto-sliding after user input // Use this method for navigation when auto-sliding (defaults to navigateNext)
autoSlideStoppable: {auto_slide_stoppable}, autoSlideMethod: {auto_slide_method},
// Use this method for navigation when auto-sliding (defaults to navigateNext) // Specify the average time in seconds that you think you will spend
autoSlideMethod: {auto_slide_method}, // presenting each slide. This is used to show a pacing timer in the
// speaker view
defaultTiming: {default_timing},
// Specify the average time in seconds that you think you will spend // Enable slide navigation via mouse wheel
// presenting each slide. This is used to show a pacing timer in the mouseWheel: {mouse_wheel},
// speaker view
defaultTiming: {default_timing},
// Enable slide navigation via mouse wheel // Opens links in an iframe preview overlay
mouseWheel: {mouse_wheel}, // Add `data-preview-link` and `data-preview-link="false"` to customise each link
// individually
previewLinks: {preview_links},
// Opens links in an iframe preview overlay // Exposes the reveal.js API through window.postMessage
// Add `data-preview-link` and `data-preview-link="false"` to customise each link postMessage: {post_message},
// individually
previewLinks: {preview_links},
// Exposes the reveal.js API through window.postMessage // Dispatches all reveal.js events to the parent window through postMessage
postMessage: {post_message}, postMessageEvents: {post_message_events},
// Dispatches all reveal.js events to the parent window through postMessage // Focuses body when page changes visibility to ensure keyboard shortcuts work
postMessageEvents: {post_message_events}, focusBodyOnPageVisibilityChange: {focus_body_on_page_visibility_change},
// Focuses body when page changes visibility to ensure keyboard shortcuts work // Transition style
focusBodyOnPageVisibilityChange: {focus_body_on_page_visibility_change}, transition: {transition}, // none/fade/slide/convex/concave/zoom
// Transition style // Transition speed
transition: {transition}, // none/fade/slide/convex/concave/zoom transitionSpeed: {transition_speed}, // default/fast/slow
// Transition speed // Transition style for full page slide backgrounds
transitionSpeed: {transition_speed}, // default/fast/slow backgroundTransition: {background_transition}, // none/fade/slide/convex/concave/zoom
// Transition style for full page slide backgrounds // The maximum number of pages a single slide can expand onto when printing
backgroundTransition: {background_transition}, // none/fade/slide/convex/concave/zoom // to PDF, unlimited by default
pdfMaxPagesPerSlide: {pdf_max_pages_per_slide},
// The maximum number of pages a single slide can expand onto when printing // Prints each fragment on a separate slide
// to PDF, unlimited by default pdfSeparateFragments: {pdf_separate_fragments},
pdfMaxPagesPerSlide: {pdf_max_pages_per_slide},
// Prints each fragment on a separate slide // Offset used to reduce the height of content within exported PDF pages.
pdfSeparateFragments: {pdf_separate_fragments}, // This exists to account for environment differences based on how you
// print to PDF. CLI printing options, like phantomjs and wkpdf, can end
// on precisely the total height of the document whereas in-browser
// printing has to end one pixel before.
pdfPageHeightOffset: {pdf_page_height_offset},
// Offset used to reduce the height of content within exported PDF pages. // Number of slides away from the current that are visible
// This exists to account for environment differences based on how you viewDistance: {view_distance},
// print to PDF. CLI printing options, like phantomjs and wkpdf, can end
// on precisely the total height of the document whereas in-browser
// printing has to end one pixel before.
pdfPageHeightOffset: {pdf_page_height_offset},
// Number of slides away from the current that are visible // Number of slides away from the current that are visible on mobile
viewDistance: {view_distance}, // devices. It is advisable to set this to a lower number than
// viewDistance in order to save resources.
mobileViewDistance: {mobile_view_distance},
// Number of slides away from the current that are visible on mobile // The display mode that will be used to show slides
// devices. It is advisable to set this to a lower number than display: {display},
// viewDistance in order to save resources.
mobileViewDistance: {mobile_view_distance},
// The display mode that will be used to show slides // Hide cursor if inactive
display: {display}, hideInactiveCursor: {hide_inactive_cursor},
// Hide cursor if inactive // Time before the cursor is hidden (in ms)
hideInactiveCursor: {hide_inactive_cursor}, hideCursorTime: {hide_cursor_time}
}});
// Time before the cursor is hidden (in ms) {data_uri_fix}
hideCursorTime: {hide_cursor_time}
}});
</script> </script>
</body> </body>

67
poetry.lock generated
View File

@ -263,6 +263,17 @@ python-versions = ">=3.7"
[package.extras] [package.extras]
test = ["pytest (>=6)"] test = ["pytest (>=6)"]
[[package]]
name = "execnet"
version = "2.0.2"
description = "execnet: rapid multi-Python deployment"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.extras]
testing = ["hatch", "pre-commit", "pytest", "tox"]
[[package]] [[package]]
name = "executing" name = "executing"
version = "1.2.0" version = "1.2.0"
@ -1172,6 +1183,37 @@ pytest = ">=4.6"
[package.extras] [package.extras]
testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"] testing = ["fields", "hunter", "process-tests", "pytest-xdist", "six", "virtualenv"]
[[package]]
name = "pytest-env"
version = "0.8.2"
description = "py.test plugin that allows you to add environment variables."
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
pytest = ">=7.3.1"
[package.extras]
test = ["coverage (>=7.2.7)", "pytest-mock (>=3.10)"]
[[package]]
name = "pytest-xdist"
version = "3.3.1"
description = "pytest xdist plugin for distributed testing, most importantly across multiple CPUs"
category = "dev"
optional = false
python-versions = ">=3.7"
[package.dependencies]
execnet = ">=1.1"
pytest = ">=6.2.0"
[package.extras]
psutil = ["psutil (>=3.0)"]
setproctitle = ["setproctitle"]
testing = ["filelock"]
[[package]] [[package]]
name = "python-dateutil" name = "python-dateutil"
version = "2.8.2" version = "2.8.2"
@ -1699,7 +1741,7 @@ manimgl = ["manimgl"]
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = ">=3.8.1,<3.12" python-versions = ">=3.8.1,<3.12"
content-hash = "a5277069682f9b5fa18214ac6f2b0ab7b7b25e07d92b6adc540865dd61df6244" content-hash = "5aa73b904cc728d93fbef1529966bbde03aa5643814e9477da0fa4bb7026f754"
[metadata.files] [metadata.files]
alabaster = [ alabaster = [
@ -2035,6 +2077,10 @@ exceptiongroup = [
{file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"}, {file = "exceptiongroup-1.1.2-py3-none-any.whl", hash = "sha256:e346e69d186172ca7cf029c8c1d16235aa0e04035e5750b4b95039e65204328f"},
{file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"}, {file = "exceptiongroup-1.1.2.tar.gz", hash = "sha256:12c3e887d6485d16943a309616de20ae5582633e0a2eda17f4e10fd61c1e8af5"},
] ]
execnet = [
{file = "execnet-2.0.2-py3-none-any.whl", hash = "sha256:88256416ae766bc9e8895c76a87928c0012183da3cc4fc18016e6f050e025f41"},
{file = "execnet-2.0.2.tar.gz", hash = "sha256:cc59bc4423742fd71ad227122eb0dd44db51efb3dc4095b45ac9a08c770096af"},
]
executing = [ executing = [
{file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"}, {file = "executing-1.2.0-py2.py3-none-any.whl", hash = "sha256:0314a69e37426e3608aada02473b4161d4caf5a4b244d1d0c48072b8fee7bacc"},
{file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"}, {file = "executing-1.2.0.tar.gz", hash = "sha256:19da64c18d2d851112f09c287f8d3dbbdf725ab0e569077efb6cdcbd3497c107"},
@ -2262,10 +2308,13 @@ lxml = [
{file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"}, {file = "lxml-4.9.3-cp27-cp27m-macosx_11_0_x86_64.whl", hash = "sha256:b0a545b46b526d418eb91754565ba5b63b1c0b12f9bd2f808c852d9b4b2f9b5c"},
{file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"}, {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:075b731ddd9e7f68ad24c635374211376aa05a281673ede86cbe1d1b3455279d"},
{file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"}, {file = "lxml-4.9.3-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1e224d5755dba2f4a9498e150c43792392ac9b5380aa1b845f98a1618c94eeef"},
{file = "lxml-4.9.3-cp27-cp27m-win32.whl", hash = "sha256:2c74524e179f2ad6d2a4f7caf70e2d96639c0954c943ad601a9e146c76408ed7"},
{file = "lxml-4.9.3-cp27-cp27m-win_amd64.whl", hash = "sha256:4f1026bc732b6a7f96369f7bfe1a4f2290fb34dce00d8644bc3036fb351a4ca1"},
{file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"}, {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c0781a98ff5e6586926293e59480b64ddd46282953203c76ae15dbbbf302e8bb"},
{file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"}, {file = "lxml-4.9.3-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:cef2502e7e8a96fe5ad686d60b49e1ab03e438bd9123987994528febd569868e"},
{file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"}, {file = "lxml-4.9.3-cp310-cp310-macosx_11_0_x86_64.whl", hash = "sha256:b86164d2cff4d3aaa1f04a14685cbc072efd0b4f99ca5708b2ad1b9b5988a991"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:42871176e7896d5d45138f6d28751053c711ed4d48d8e30b498da155af39aebd"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ae8b9c6deb1e634ba4f1930eb67ef6e6bf6a44b6eb5ad605642b2d6d5ed9ce3c"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_aarch64.whl", hash = "sha256:411007c0d88188d9f621b11d252cce90c4a2d1a49db6c068e3c16422f306eab8"},
{file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"}, {file = "lxml-4.9.3-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:cd47b4a0d41d2afa3e58e5bf1f62069255aa2fd6ff5ee41604418ca925911d76"},
{file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"}, {file = "lxml-4.9.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:0e2cb47860da1f7e9a5256254b74ae331687b9672dfa780eed355c4c9c3dbd23"},
@ -2274,6 +2323,7 @@ lxml = [
{file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"}, {file = "lxml-4.9.3-cp310-cp310-win_amd64.whl", hash = "sha256:97047f0d25cd4bcae81f9ec9dc290ca3e15927c192df17331b53bebe0e3ff96d"},
{file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"}, {file = "lxml-4.9.3-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:1f447ea5429b54f9582d4b955f5f1985f278ce5cf169f72eea8afd9502973dd5"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:57d6ba0ca2b0c462f339640d22882acc711de224d769edf29962b09f77129cbf"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:9767e79108424fb6c3edf8f81e6730666a50feb01a328f4a016464a5893f835a"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_aarch64.whl", hash = "sha256:71c52db65e4b56b8ddc5bb89fb2e66c558ed9d1a74a45ceb7dcb20c191c3df2f"},
{file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"}, {file = "lxml-4.9.3-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:d73d8ecf8ecf10a3bd007f2192725a34bd62898e8da27eb9d32a58084f93962b"},
{file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"}, {file = "lxml-4.9.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:0a3d3487f07c1d7f150894c238299934a2a074ef590b583103a45002035be120"},
@ -2293,6 +2343,7 @@ lxml = [
{file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"}, {file = "lxml-4.9.3-cp36-cp36m-macosx_11_0_x86_64.whl", hash = "sha256:64f479d719dc9f4c813ad9bb6b28f8390360660b73b2e4beb4cb0ae7104f1c12"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:dd708cf4ee4408cf46a48b108fb9427bfa00b9b85812a9262b5c668af2533ea5"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5c31c7462abdf8f2ac0577d9f05279727e698f97ecbb02f17939ea99ae8daa98"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e3cd95e10c2610c360154afdc2f1480aea394f4a4f1ea0a5eacce49640c9b190"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_28_x86_64.whl", hash = "sha256:4930be26af26ac545c3dffb662521d4e6268352866956672231887d18f0eaab2"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:4aec80cde9197340bc353d2768e2a75f5f60bacda2bab72ab1dc499589b3878c"},
{file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"}, {file = "lxml-4.9.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:14e019fd83b831b2e61baed40cab76222139926b1fb5ed0e79225bc0cae14584"},
@ -2302,6 +2353,7 @@ lxml = [
{file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"}, {file = "lxml-4.9.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bef4e656f7d98aaa3486d2627e7d2df1157d7e88e7efd43a65aa5dd4714916cf"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:46f409a2d60f634fe550f7133ed30ad5321ae2e6630f13657fb9479506b00601"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:4c28a9144688aef80d6ea666c809b4b0e50010a2aca784c97f5e6bf143d9f129"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:141f1d1a9b663c679dc524af3ea1773e618907e96075262726c7612c02b149a4"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_28_x86_64.whl", hash = "sha256:53ace1c1fd5a74ef662f844a0413446c0629d151055340e9893da958a374f70d"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:17a753023436a18e27dd7769e798ce302963c236bc4114ceee5b25c18c52c693"},
{file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"}, {file = "lxml-4.9.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:7d298a1bd60c067ea75d9f684f5f3992c9d6766fadbc0bcedd39750bf344c2f4"},
@ -2311,6 +2363,7 @@ lxml = [
{file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"}, {file = "lxml-4.9.3-cp37-cp37m-win_amd64.whl", hash = "sha256:120fa9349a24c7043854c53cae8cec227e1f79195a7493e09e0c12e29f918e52"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4d2d1edbca80b510443f51afd8496be95529db04a509bc8faee49c7b0fb6d2cc"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:8d7e43bd40f65f7d97ad8ef5c9b1778943d02f04febef12def25f7583d19baac"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:71d66ee82e7417828af6ecd7db817913cb0cf9d4e61aa0ac1fde0583d84358db"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_28_x86_64.whl", hash = "sha256:6fc3c450eaa0b56f815c7b62f2b7fba7266c4779adcf1cece9e6deb1de7305ce"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:65299ea57d82fb91c7f019300d24050c4ddeb7c5a190e076b5f48a2b43d19c42"},
{file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"}, {file = "lxml-4.9.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:eadfbbbfb41b44034a4c757fd5d70baccd43296fb894dba0295606a7cf3124aa"},
@ -2320,6 +2373,7 @@ lxml = [
{file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"}, {file = "lxml-4.9.3-cp38-cp38-win_amd64.whl", hash = "sha256:92af161ecbdb2883c4593d5ed4815ea71b31fafd7fd05789b23100d081ecac96"},
{file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"}, {file = "lxml-4.9.3-cp39-cp39-macosx_11_0_x86_64.whl", hash = "sha256:9bb6ad405121241e99a86efff22d3ef469024ce22875a7ae045896ad23ba2340"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8ed74706b26ad100433da4b9d807eae371efaa266ffc3e9191ea436087a9d6a7"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fbf521479bcac1e25a663df882c46a641a9bff6b56dc8b0fafaebd2f66fb231b"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_aarch64.whl", hash = "sha256:303bf1edce6ced16bf67a18a1cf8339d0db79577eec5d9a6d4a80f0fb10aa2da"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_28_x86_64.whl", hash = "sha256:5515edd2a6d1a5a70bfcdee23b42ec33425e405c5b351478ab7dc9347228f96e"},
{file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"}, {file = "lxml-4.9.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:690dafd0b187ed38583a648076865d8c229661ed20e48f2335d68e2cf7dc829d"},
@ -2330,13 +2384,16 @@ lxml = [
{file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"}, {file = "lxml-4.9.3-cp39-cp39-win_amd64.whl", hash = "sha256:4dd9a263e845a72eacb60d12401e37c616438ea2e5442885f65082c276dfb2b2"},
{file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"}, {file = "lxml-4.9.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:6689a3d7fd13dc687e9102a27e98ef33730ac4fe37795d5036d18b4d527abd35"},
{file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:f6bdac493b949141b733c5345b6ba8f87a226029cbabc7e9e121a413e49441e0"},
{file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:05186a0f1346ae12553d66df1cfce6f251589fea3ad3da4f3ef4e34b2d58c6a3"},
{file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"}, {file = "lxml-4.9.3-pp37-pypy37_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c2006f5c8d28dee289f7020f721354362fa304acbaaf9745751ac4006650254b"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-macosx_11_0_x86_64.whl", hash = "sha256:5c245b783db29c4e4fbbbfc9c5a78be496c9fea25517f90606aa1f6b2b3d5f7b"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:4fb960a632a49f2f089d522f70496640fdf1218f1243889da3822e0a9f5f3ba7"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:50670615eaf97227d5dc60de2dc99fb134a7130d310d783314e7724bf163f75d"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:9719fe17307a9e814580af1f5c6e05ca593b12fb7e44fe62450a5384dbf61b4b"},
{file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"}, {file = "lxml-4.9.3-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:3331bece23c9ee066e0fb3f96c61322b9e0f54d775fccefff4c38ca488de283a"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-macosx_11_0_x86_64.whl", hash = "sha256:ed667f49b11360951e201453fc3967344d0d0263aa415e1619e85ae7fd17b4e0"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:8b77946fd508cbf0fccd8e400a7f71d4ac0e1595812e66025bac475a8e811694"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:e4da8ca0c0c0aea88fd46be8e44bd49716772358d648cce45fe387f7b92374a7"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fe4bda6bd4340caa6e5cf95e73f8fea5c4bfc55763dd42f1b50a94c1b4a2fbd4"},
{file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"}, {file = "lxml-4.9.3-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:f3df3db1d336b9356dd3112eae5f5c2b8b377f3bc826848567f10bfddfee77e9"},
{file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"}, {file = "lxml-4.9.3.tar.gz", hash = "sha256:48628bd53a426c9eb9bc066a923acaa0878d1e86129fd5359aee99285f4eed9c"},
@ -3004,6 +3061,14 @@ pytest-cov = [
{file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"}, {file = "pytest-cov-4.1.0.tar.gz", hash = "sha256:3904b13dfbfec47f003b8e77fd5b589cd11904a21ddf1ab38a64f204d6a10ef6"},
{file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"}, {file = "pytest_cov-4.1.0-py3-none-any.whl", hash = "sha256:6ba70b9e97e69fcc3fb45bfeab2d0a138fb65c4d0d6a41ef33983ad114be8c3a"},
] ]
pytest-env = [
{file = "pytest_env-0.8.2-py3-none-any.whl", hash = "sha256:5e533273f4d9e6a41c3a3120e0c7944aae5674fa773b329f00a5eb1f23c53a38"},
{file = "pytest_env-0.8.2.tar.gz", hash = "sha256:baed9b3b6bae77bd75b9238e0ed1ee6903a42806ae9d6aeffb8754cd5584d4ff"},
]
pytest-xdist = [
{file = "pytest-xdist-3.3.1.tar.gz", hash = "sha256:d5ee0520eb1b7bcca50a60a518ab7a7707992812c578198f8b44fdfac78e8c93"},
{file = "pytest_xdist-3.3.1-py3-none-any.whl", hash = "sha256:ff9daa7793569e6a68544850fd3927cd257cc03a7ef76c95e86915355e82b5f2"},
]
python-dateutil = [ python-dateutil = [
{file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"}, {file = "python-dateutil-2.8.2.tar.gz", hash = "sha256:0123cacc1627ae19ddf3c27a5de5bd67ee4586fbdd6440d9748f8abb483d3e86"},
{file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"}, {file = "python_dateutil-2.8.2-py2.py3-none-any.whl", hash = "sha256:961d03dc3453ebbc59dbdea9e4e11c5651520a876d0f4db161e8674aae935da9"},

View File

@ -90,6 +90,8 @@ manim = "^0.17.3"
manimgl = "^1.6.1" manimgl = "^1.6.1"
pytest = "^7.4.0" pytest = "^7.4.0"
pytest-cov = "^4.1.0" pytest-cov = "^4.1.0"
pytest-env = "^0.8.2"
pytest-xdist = "^3.3.1"
[tool.poetry.plugins] [tool.poetry.plugins]
@ -97,6 +99,9 @@ pytest-cov = "^4.1.0"
manim-slides = "manim_slides.__main__:cli" manim-slides = "manim_slides.__main__:cli"
[tool.pytest.ini_options] [tool.pytest.ini_options]
env = [
"QT_QPA_PLATFORM=offscreen"
]
filterwarnings = [ filterwarnings = [
"error", "error",
"ignore::DeprecationWarning" "ignore::DeprecationWarning"