-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Add type annotations to mobject_update_utils.py
#4382
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
52ee67e
e48dd53
81b4bf7
38ab575
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
|
@@ -15,7 +15,7 @@ | |||||||||
|
||||||||||
|
||||||||||
import inspect | ||||||||||
from typing import TYPE_CHECKING, Callable | ||||||||||
from typing import TYPE_CHECKING, Any, Callable | ||||||||||
|
||||||||||
import numpy as np | ||||||||||
|
||||||||||
|
@@ -26,6 +26,7 @@ | |||||||||
|
||||||||||
if TYPE_CHECKING: | ||||||||||
from manim.animation.animation import Animation | ||||||||||
from manim.typing import Vector3DLike | ||||||||||
|
||||||||||
|
||||||||||
def assert_is_mobject_method(method: Callable) -> None: | ||||||||||
|
@@ -34,33 +35,39 @@ def assert_is_mobject_method(method: Callable) -> None: | |||||||||
assert isinstance(mobject, (Mobject, OpenGLMobject)) | ||||||||||
|
||||||||||
|
||||||||||
def always(method: Callable, *args, **kwargs) -> Mobject: | ||||||||||
def always(method: Callable, *args: Any, **kwargs: Any) -> Mobject | OpenGLMobject: | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
assert_is_mobject_method(method) | ||||||||||
mobject = method.__self__ | ||||||||||
# error: "Callable[..., Any]" has no attribute "__self__" [attr-defined] | ||||||||||
mobject: Mobject | OpenGLMobject = method.__self__ | ||||||||||
# error: "Callable[..., Any]" has no attribute "__func__" [attr-defined] | ||||||||||
func = method.__func__ | ||||||||||
mobject.add_updater(lambda m: func(m, *args, **kwargs)) | ||||||||||
return mobject | ||||||||||
|
||||||||||
|
||||||||||
def f_always(method: Callable[[Mobject], None], *arg_generators, **kwargs) -> Mobject: | ||||||||||
def f_always( | ||||||||||
method: Callable[[Mobject], None], *arg_generators: Any, **kwargs: Any | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||||||
) -> Mobject | OpenGLMobject: | ||||||||||
""" | ||||||||||
More functional version of always, where instead | ||||||||||
of taking in args, it takes in functions which output | ||||||||||
the relevant arguments. | ||||||||||
""" | ||||||||||
assert_is_mobject_method(method) | ||||||||||
mobject = method.__self__ | ||||||||||
# error: "Callable[[Mobject], None]" has no attribute "__self__" [attr-defined] | ||||||||||
mobject: Mobject | OpenGLMobject = method.__self__ | ||||||||||
# error: "Callable[[Mobject], None]" has no attribute "__func__" [attr-defined] | ||||||||||
func = method.__func__ | ||||||||||
|
||||||||||
def updater(mob): | ||||||||||
def updater(mob: Mobject | OpenGLMobject) -> None: | ||||||||||
args = [arg_generator() for arg_generator in arg_generators] | ||||||||||
func(mob, *args, **kwargs) | ||||||||||
|
||||||||||
mobject.add_updater(updater) | ||||||||||
return mobject | ||||||||||
|
||||||||||
|
||||||||||
def always_redraw(func: Callable[[], Mobject]) -> Mobject: | ||||||||||
def always_redraw(func: Callable[[], Mobject]) -> Mobject | OpenGLMobject: | ||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If the return value is any of these two classes, the
Suggested change
|
||||||||||
"""Redraw the mobject constructed by a function every frame. | ||||||||||
|
||||||||||
This function returns a mobject with an attached updater that | ||||||||||
|
@@ -106,8 +113,8 @@ def construct(self): | |||||||||
|
||||||||||
|
||||||||||
def always_shift( | ||||||||||
mobject: Mobject, direction: np.ndarray[np.float64] = RIGHT, rate: float = 0.1 | ||||||||||
) -> Mobject: | ||||||||||
mobject: Mobject | OpenGLMobject, direction: Vector3DLike = RIGHT, rate: float = 0.1 | ||||||||||
) -> Mobject | OpenGLMobject: | ||||||||||
"""A mobject which is continuously shifted along some direction | ||||||||||
at a certain rate. | ||||||||||
|
||||||||||
|
@@ -144,7 +151,9 @@ def construct(self): | |||||||||
return mobject | ||||||||||
|
||||||||||
|
||||||||||
def always_rotate(mobject: Mobject, rate: float = 20 * DEGREES, **kwargs) -> Mobject: | ||||||||||
def always_rotate( | ||||||||||
mobject: Mobject | OpenGLMobject, rate: float = 20 * DEGREES, **kwargs: Any | ||||||||||
) -> Mobject | OpenGLMobject: | ||||||||||
"""A mobject which is continuously rotated at a certain rate. | ||||||||||
|
||||||||||
Parameters | ||||||||||
|
@@ -178,8 +187,8 @@ def construct(self): | |||||||||
|
||||||||||
|
||||||||||
def turn_animation_into_updater( | ||||||||||
animation: Animation, cycle: bool = False, delay: float = 0, **kwargs | ||||||||||
) -> Mobject: | ||||||||||
animation: Animation, cycle: bool = False, delay: float = 0, **kwargs: Any | ||||||||||
) -> Mobject | OpenGLMobject: | ||||||||||
""" | ||||||||||
Add an updater to the animation's mobject which applies | ||||||||||
the interpolation and update functions of the animation | ||||||||||
|
@@ -210,7 +219,7 @@ def construct(self): | |||||||||
animation.begin() | ||||||||||
animation.total_time = -delay | ||||||||||
|
||||||||||
def update(m: Mobject, dt: float): | ||||||||||
def update(m: Mobject, dt: float) -> None: | ||||||||||
if animation.total_time >= 0: | ||||||||||
run_time = animation.get_run_time() | ||||||||||
time_ratio = animation.total_time / run_time | ||||||||||
|
@@ -230,5 +239,5 @@ def update(m: Mobject, dt: float): | |||||||||
return mobject | ||||||||||
|
||||||||||
|
||||||||||
def cycle_animation(animation: Animation, **kwargs) -> Mobject: | ||||||||||
def cycle_animation(animation: Animation, **kwargs: Any) -> Mobject | OpenGLMobject: | ||||||||||
return turn_animation_into_updater(animation, cycle=True, **kwargs) |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, all the errors you describe below (
method
not having attributes__self__
and__func__
) could be fixed by typing themethod
parameter as aMethodType
instead, which you have to import from thetypes
standard library.However, if you try to pass a method directly to these functions after typing
method
asMethodType
, MyPy raises an error. There's an issue about this on the MyPy GitHub: python/mypy#14235In the meantime, I would say it's still fine to type them as
MethodType
to prevent these errors you're having. We already have some parameters and returned values typed asMethodType
anyways.EDIT: also see my implementation of
MobjectMethod
in PR #3976 if you're interested. The point of it is getting better autocompletion forMobject
s, but it still suffers from the issue described above (and I forgot to include the__self__
attribute).