krassowski / jupyter-manim

manim cell magic for IPython/Jupyter to show the output video
MIT License
194 stars 11 forks source link

Improve support for imports #11

Closed qo4on closed 4 years ago

qo4on commented 4 years ago

This code works: !python3 -m manim example_scenes.py WarpSquare -pl

This code works also in jupyter:

%%manim Shapes --base64 --low_quality
import numpy as np
class WarpSquare(Scene):
    def construct(self):
        square = Square()
        self.play(ApplyPointwiseFunction(
            lambda point: complex_to_R3(np.exp(R3_to_complex(point))),
            square
        ))
        self.wait()

But when import numpy as np was written in the cell before and already executed the code without this line can't find numpy:

%%manim Shapes --base64 --low_quality
class WarpSquare(Scene):
    def construct(self):
        square = Square()
        self.play(ApplyPointwiseFunction(
            lambda point: complex_to_R3(np.exp(R3_to_complex(point))),
            square
        ))
        self.wait()

Media will be written to ./media/. You can change this behavior with the --media_dir flag.

/usr/local/lib/python3.6/dist-packages/jupyter_manim/init.py:259: UserWarning: Could not find path in the manim output warn('Could not find path in the manim output') /tmp/tmp0wa7hz1o.py:15: UserWarning: Import from notebook: name already in the globals(), skipping warn('Import from notebook: ' + name + ' already in the globals(), skipping')

Shapes is not in the script

Traceback (most recent call last): File "/content/manim/manimlib/extract_scene.py", line 155, in main scene = SceneClass(scene_kwargs) File "/content/manim/manimlib/scene/scene.py", line 53, in init self.construct() File "/tmp/tmp0wa7hz1o.py", line 25, in construct square File "/content/manim/manimlib/scene/scene.py", line 406, in wrapper func(self, *args, *kwargs) File "/content/manim/manimlib/scene/scene.py", line 462, in play self.begin_animations(animations) File "/content/manim/manimlib/scene/scene.py", line 415, in begin_animations animation.begin() File "/content/manim/manimlib/animation/transform.py", line 46, in begin self.target_mobject = self.create_target() File "/content/manim/manimlib/animation/transform.py", line 182, in create_target method.func(target, args, method_kwargs) File "/content/manim/manimlib/mobject/types/vectorized_mobject.py", line 564, in apply_function Mobject.apply_function(self, function) File "/content/manim/manimlib/mobject/mobject.py", line 280, in apply_function **kwargs File "/content/manim/manimlib/mobject/mobject.py", line 360, in apply_points_function_about_point mob.points = func(mob.points) File "/content/manim/manimlib/mobject/mobject.py", line 279, in lambda points: np.apply_along_axis(function, 1, points), File "<__array_function__ internals>", line 6, in apply_along_axis File "/usr/local/lib/python3.6/dist-packages/numpy/lib/shape_base.py", line 379, in apply_along_axis for ind in inds: File "/tmp/tmp0wa7hz1o.py", line 24, in lambda point: complex_to_R3(np.exp(R3_to_complex(point))), NameError: name 'np' is not defined

Why doesn't the cell with magic %%manim find already imported libraries?

qo4on commented 4 years ago

import sys and import random do the same.

krassowski commented 4 years ago

One solution would be to search through the global objects, collect modules, assemble import code and then add it to the code passed to manim. The first two steps are easy:

from types import ModuleType

@magics_class
class ManimMagics(Magics):

    # ....

    def extract_imports(self):
        frame = find_ipython_frame(inspect.stack())
        if not frame:
            raise Exception('Could not find IPython frame')

        globals_dict = frame[0].f_globals

        modules = {
            name: obj
            for name, obj in globals_dict.items()
            if (not name.startswith('_')) and isinstance(obj, ModuleType)
        }
        return '\n'.join(
            assemble_import(module) for module in modules
        )

but writing the assemble_import function is difficult. It should work like this:

import statistics
from os import path
import os
sys = os.sys
import abc as xyz
assert assemble_import(statistics) = 'import statistics'
assert assemble_import(path) = 'from os import path'
assert assemble_import(sys) = 'from os import sys'
assert assemble_import(xyz) = 'import abc as xyz'

All I had time for this weekend, maybe someone else will have more ideas/time to send pick up from there and send a PR - help wanted!

krassowski commented 4 years ago

Posted a question on the topic here: https://stackoverflow.com/questions/61049832/is-it-possible-to-generate-import-statement-from-the-module-file-in-python