Skip to content

Commit

Permalink
Remove support for dynamic plugin imports (#3524)
Browse files Browse the repository at this point in the history
* Remove call to deprecated `pkg_resources`

* Remove support for dynamic plugin imports, update plugin utilities

* fix affected tests

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

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

* more fixes

* Last fix

* Fix import

* Update docs

---------

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Jason Villanueva <[email protected]>
  • Loading branch information
3 people authored Feb 15, 2024
1 parent fcd81b2 commit 206f874
Show file tree
Hide file tree
Showing 8 changed files with 32 additions and 212 deletions.
32 changes: 5 additions & 27 deletions docs/source/plugins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,40 +99,18 @@ directory structure, build system, and naming are completely up to your
discretion as an author. The aforementioned template plugin is only a model
using Poetry since this is the build system Manim uses. The plugin's `entry
point <https://packaging.python.org/specifications/entry-points/>`_ can be
specified in poetry as:
specified in Poetry as:

.. code-block:: toml
[tool.poetry.plugins."manim.plugins"]
"name" = "object_reference"
Here ``name`` is the name of the module of the plugin.
.. versionremoved:: 0.19.0

Here ``object_reference`` can point to either a function in a module or a module
itself. For example,

.. code-block:: toml
[tool.poetry.plugins."manim.plugins"]
"manim_plugintemplate" = "manim_plugintemplate"
Here a module is used as ``object_reference``, and when this plugin is enabled,
Manim will look for ``__all__`` keyword defined in ``manim_plugintemplate`` and
everything as a global variable one by one.

If ``object_reference`` is a function, Manim calls the function and expects the
function to return a list of modules or functions that need to be defined globally.

For example,

.. code-block:: toml
[tool.poetry.plugins."manim.plugins"]
"manim_plugintemplate" = "manim_awesomeplugin.imports:setup_things"
Here, Manim will call the function ``setup_things`` defined in
``manim_awesomeplugin.imports`` and calls that. It returns a list of function or
modules which will be imported globally.
Plugins should be imported explicitly to be usable in user code. The plugin
system will probably be refactored in the future to provide a more structured
interface.

A note on Renderer Compatibility
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Expand Down
16 changes: 15 additions & 1 deletion manim/plugins/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,17 @@
from __future__ import annotations

from .import_plugins import *
from manim import config, logger

from .plugins_flags import get_plugins, list_plugins

__all__ = [
"get_plugins",
"list_plugins",
]

requested_plugins: set[str] = set(config["plugins"])
missing_plugins = requested_plugins - set(get_plugins().keys())


if missing_plugins:
logger.warning("Missing Plugins: %s", missing_plugins)
43 changes: 0 additions & 43 deletions manim/plugins/import_plugins.py

This file was deleted.

16 changes: 11 additions & 5 deletions manim/plugins/plugins_flags.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,28 @@

from __future__ import annotations

import pkg_resources
import sys
from typing import Any

if sys.version_info < (3, 10):
from importlib_metadata import entry_points
else:
from importlib.metadata import entry_points

from manim import console

__all__ = ["list_plugins"]


def get_plugins():
plugins = {
def get_plugins() -> dict[str, Any]:
plugins: dict[str, Any] = {
entry_point.name: entry_point.load()
for entry_point in pkg_resources.iter_entry_points("manim.plugins")
for entry_point in entry_points(group="manim.plugins")
}
return plugins


def list_plugins():
def list_plugins() -> None:
console.print("[green bold]Plugins:[/green bold]", justify="left")

plugins = get_plugins()
Expand Down
8 changes: 0 additions & 8 deletions poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ click = ">=8.0"
cloup = ">=2.0.0"
dearpygui = { version = ">=1.0.0", optional = true }
decorator = ">=4.3.2"
importlib-metadata = {version = ">=3.6", python = "<=3.9"} # Required to discover plugins
isosurfaces = ">=0.1.0"
jupyterlab = { version = ">=3.0.0", optional = true }
manimpango = ">=0.5.0,<1.0.0" # Complete API change in 1.0.0
Expand Down
14 changes: 0 additions & 14 deletions tests/test_plugins/simple_scenes.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,3 @@ def construct(self):
square = Square()
circle = Circle()
self.play(Transform(square, circle))


class FunctionLikeTest(Scene):
def construct(self):
assert "FunctionLike" in globals()
a = FunctionLike()
self.play(FadeIn(a))


class WithAllTest(Scene):
def construct(self):
assert "WithAll" in globals()
a = WithAll()
self.play(FadeIn(a))
114 changes: 0 additions & 114 deletions tests/test_plugins/test_plugins.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import random
import string
import tempfile
import textwrap
from pathlib import Path

Expand Down Expand Up @@ -142,116 +141,3 @@ def _create_plugin(entry_point, class_name, function_name, all_dec=""):
out, err, exit_code = capture(command)
print(out)
assert exit_code == 0, err


@pytest.mark.slow
def test_plugin_function_like(
tmp_path,
create_plugin,
python_version,
simple_scenes_path,
):
function_like_plugin = create_plugin(
"{plugin_name}.__init__:import_all",
"FunctionLike",
"import_all",
)
cfg_file = cfg_file_create(
cfg_file_contents.format(plugin_name=function_like_plugin["plugin_name"]),
tmp_path,
)
scene_name = "FunctionLikeTest"
command = [
python_version,
"-m",
"manim",
"-ql",
"--media_dir",
str(cfg_file.parent),
"--config_file",
str(cfg_file),
str(simple_scenes_path),
scene_name,
]
out, err, exit_code = capture(command, cwd=str(cfg_file.parent))
print(out)
print(err)
assert exit_code == 0, err


@pytest.mark.slow
def test_plugin_no_all(tmp_path, create_plugin, python_version):
create_plugin = create_plugin("{plugin_name}", "NoAll", "import_all")
plugin_name = create_plugin["plugin_name"]
cfg_file = cfg_file_create(
cfg_file_contents.format(plugin_name=plugin_name),
tmp_path,
)
test_class = textwrap.dedent(
f"""\
from manim import *
class NoAllTest(Scene):
def construct(self):
assert "{plugin_name}" in globals()
a = {plugin_name}.NoAll()
self.play(FadeIn(a))
""",
)

with tempfile.NamedTemporaryFile(
mode="w",
encoding="utf-8",
suffix=".py",
delete=False,
) as tmpfile:
tmpfile.write(test_class)
scene_name = "NoAllTest"
command = [
python_version,
"-m",
"manim",
"-ql",
"--media_dir",
str(cfg_file.parent),
"--config_file",
str(cfg_file),
tmpfile.name,
scene_name,
]
out, err, exit_code = capture(command, cwd=str(cfg_file.parent))
print(out)
print(err)
assert exit_code == 0, err
Path(tmpfile.name).unlink()


@pytest.mark.slow
def test_plugin_with_all(tmp_path, create_plugin, python_version, simple_scenes_path):
create_plugin = create_plugin(
"{plugin_name}",
"WithAll",
"import_all",
all_dec="__all__=['WithAll']",
)
plugin_name = create_plugin["plugin_name"]
cfg_file = cfg_file_create(
cfg_file_contents.format(plugin_name=plugin_name),
tmp_path,
)
scene_name = "WithAllTest"
command = [
python_version,
"-m",
"manim",
"-ql",
"--media_dir",
str(cfg_file.parent),
"--config_file",
str(cfg_file),
str(simple_scenes_path),
scene_name,
]
out, err, exit_code = capture(command, cwd=str(cfg_file.parent))
print(out)
print(err)
assert exit_code == 0, err

0 comments on commit 206f874

Please sign in to comment.