Skip to content
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

Add type annotations to manim.utils.* #3999

Merged
merged 52 commits into from
Jan 2, 2025

Conversation

henrikmidtiby
Copy link
Contributor

Overview: What does this pull request change?

The PR adds type annotations to some of the elements in manim/utils

Motivation and Explanation: Why and how do your changes improve the library?

Type annotations are a good thing.

Further Information and Comments

I have chosen to go for smaller PR regarding adding type annotations.
The endeavor of #3981 is a bit frightening...

Reviewer Checklist

  • The PR title is descriptive enough for the changelog, and the PR is labeled correctly
  • If applicable: newly added non-private functions and classes have a docstring including a short summary and a PARAMETERS section
  • If applicable: newly added functions and classes are tested

manim/utils/bezier.py Fixed Show fixed Hide fixed
manim/utils/bezier.py Fixed Show fixed Hide fixed
manim/utils/bezier.py Fixed Show fixed Hide fixed
manim/utils/bezier.py Fixed Show fixed Hide fixed
manim/utils/bezier.py Fixed Show fixed Hide fixed
manim/utils/bezier.py Fixed Show fixed Hide fixed
manim/utils/color/core.py Fixed Show fixed Hide fixed
manim/scene/scene_file_writer.py Fixed Show fixed Hide fixed
manim/scene/scene_file_writer.py Fixed Show fixed Hide fixed
manim/utils/simple_functions.py Fixed Show fixed Hide fixed
manim/utils/simple_functions.py Fixed Show fixed Hide fixed
@henrikmidtiby
Copy link
Contributor Author

@JasonGrace2282 Would you take a look at this PR?

@JasonGrace2282
Copy link
Member

I'll take a look when I find the time.

manim/utils/family_ops.py Dismissed Show dismissed Hide dismissed
manim/utils/caching.py Dismissed Show dismissed Hide dismissed
__all__ = ["scene_classes_from_file"]


def get_module(file_name: Path):
def get_module(file_name: Path) -> types.ModuleType:

Check notice

Code scanning / CodeQL

Explicit returns mixed with implicit (fall through) returns Note

Mixing implicit and explicit returns may indicate an error as implicit returns always return None.
@henrikmidtiby henrikmidtiby mentioned this pull request Nov 14, 2024
22 tasks
Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow, this is a huge PR! It will take a long time to review. I added 47 comments and I'm not finished yet, but I hope to continue reviewing soon.

A new PR (#4027) has been merged. This renames InternalPoint3D (the NumPy array) to Point3D, and Point3D (anything resembling a 3D point) to Point3DLike. Something similar happened with the other type aliases. You might wanna take a look at it.

manim/_config/utils.py Outdated Show resolved Hide resolved
manim/_config/utils.py Outdated Show resolved Hide resolved
manim/renderer/cairo_renderer.py Outdated Show resolved Hide resolved
manim/scene/scene.py Outdated Show resolved Hide resolved
manim/scene/scene_file_writer.py Outdated Show resolved Hide resolved
manim/utils/module_ops.py Outdated Show resolved Hide resolved
@@ -125,8 +132,8 @@ def prompt_user_for_choice(scene_classes):


def scene_classes_from_file(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one could be overloaded like this:

@overload
def scene_classes_from_file(
    file_path: Path, require_single_scene: bool, full_list: Literal[True]
) -> list[type[Scene]]: ...

@overload
def scene_classes_from_file(
    file_path: Path, require_single_scene: Literal[True], full_list: Literal[False] = False
) -> type[Scene]: ...

@overload
def scene_classes_from_file(
    file_path: Path, require_single_scene: Literal[False] = False, full_list: Literal[False] = False
) -> list[type[Scene]]: ...

def scene_classes_from_file(
    file_path: Path, require_single_scene: bool = False, full_list: bool = False
) -> type[Scene] | list[type[Scene]]:
    # the rest

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I try the changes that you suggest in module_ops.py, I get the errors shown below.
I will leave it for now and hope to get some help in resolving it.

manim/utils/module_ops.py:95: error: "Scene" has no attribute "__name__"  [attr-defined]
manim/utils/module_ops.py:104: error: "Scene" has no attribute "__name__"  [attr-defined]
manim/utils/module_ops.py:113: error: "Scene" has no attribute "__name__"  [attr-defined]
manim/utils/module_ops.py:115: error: Incompatible types in assignment (expression has type "Scene", target has type "type[Scene]")  [assignment]
manim/utils/module_ops.py:121: error: List comprehension has incompatible type List[type[Scene]]; expected List[Scene]  [misc]
manim/utils/module_ops.py:124: error: "Scene" has no attribute "__name__"  [attr-defined]
manim/utils/module_ops.py:168: error: Incompatible return value type (got "Scene", expected "type[Scene] | list[type[Scene]]")  [return-value]
manim/utils/module_ops.py:169: error: Incompatible return value type (got "list[Scene]", expected "type[Scene] | list[type[Scene]]")  [return-value]
Found 8 errors in 1 file (checked 1 source file)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When I tried myself, everything was OK.
Your errors suggest that you might have used Scene instead of type[Scene].

manim/utils/opengl.py Outdated Show resolved Hide resolved
manim/utils/opengl.py Outdated Show resolved Hide resolved
manim/utils/opengl.py Outdated Show resolved Hide resolved
Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part 2 of 3:

manim/utils/opengl.py Outdated Show resolved Hide resolved
manim/utils/opengl.py Show resolved Hide resolved
manim/utils/opengl.py Outdated Show resolved Hide resolved
manim/utils/opengl.py Outdated Show resolved Hide resolved
manim/utils/opengl.py Outdated Show resolved Hide resolved
manim/utils/space_ops.py Outdated Show resolved Hide resolved
manim/utils/space_ops.py Outdated Show resolved Hide resolved
manim/utils/space_ops.py Show resolved Hide resolved
manim/utils/space_ops.py Show resolved Hide resolved
manim/utils/testing/_frames_testers.py Outdated Show resolved Hide resolved
Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Part 3 of 3:

manim/utils/testing/_frames_testers.py Outdated Show resolved Hide resolved
manim/utils/testing/_frames_testers.py Outdated Show resolved Hide resolved
manim/utils/testing/_frames_testers.py Outdated Show resolved Hide resolved
manim/utils/testing/_show_diff.py Outdated Show resolved Hide resolved
manim/utils/testing/_test_class_makers.py Outdated Show resolved Hide resolved
manim/utils/testing/_test_class_makers.py Outdated Show resolved Hide resolved
manim/utils/tex_file_writing.py Outdated Show resolved Hide resolved
mypy.ini Outdated Show resolved Hide resolved
manim/utils/testing/frames_comparison.py Outdated Show resolved Hide resolved
@henrikmidtiby henrikmidtiby force-pushed the type_annotation_utils branch from 413adfb to de80832 Compare January 1, 2025 19:06
manim/scene/scene_file_writer.py Dismissed Show dismissed Hide dismissed
manim/utils/caching.py Dismissed Show dismissed Hide dismissed
manim/utils/deprecation.py Fixed Show fixed Hide fixed
manim/utils/deprecation.py Fixed Show fixed Hide fixed


def clip(a, min_a, max_a):
class Comparable(Protocol):
def __lt__(self, other: Any) -> bool: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.
class Comparable(Protocol):
def __lt__(self, other: Any) -> bool: ...

def __gt__(self, other: Any) -> bool: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.
@henrikmidtiby
Copy link
Contributor Author

@chopan050, thanks for all the comments. I have addressed most of them in the code, as well as rebased the changes on top of the current main branch.
For the future I will go for smaller pull requests, at it is quite difficult to maintain the overview of this one.

manim/utils/hashing.py Dismissed Show dismissed Hide dismissed
manim/utils/hashing.py Dismissed Show dismissed Hide dismissed
manim/utils/hashing.py Dismissed Show dismissed Hide dismissed
manim/utils/module_ops.py Dismissed Show dismissed Hide dismissed
manim/utils/module_ops.py Dismissed Show dismissed Hide dismissed
@overload
def scene_classes_from_file(
file_path: Path, require_single_scene: bool, full_list: Literal[True]
) -> list[type[Scene]]: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.
file_path: Path,
require_single_scene: Literal[True],
full_list: Literal[False] = False,
) -> type[Scene]: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.
file_path: Path,
require_single_scene: Literal[False] = False,
full_list: Literal[False] = False,
) -> list[type[Scene]]: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.
manim/utils/rate_functions.py Fixed Show fixed Hide fixed
since: str | None = None,
until: str | None = None,
replacement: str | None = None,
message: str | None = "",
) -> Callable:
) -> Callable[..., T]: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.
until: str | None = None,
replacement: str | None = None,
message: str | None = "",
) -> Callable[[Callable[..., T]], Callable[..., T]]: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.

# TODO: rewrite this to use ParamSpec when Python 3.9 is out of life
class RateFunction(Protocol):
def __call__(self, t: float, *args: Any, **kwargs: Any) -> float: ...

Check notice

Code scanning / CodeQL

Statement has no effect Note

This statement has no effect.
Copy link
Contributor

@chopan050 chopan050 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! This has been a huge effort, and we really appreciate it! Thank you so much!

If you don't mind, I pushed some changes in order to finish this PR:

  • I removed caching, polylabel and qhull from mypy.ini after fixing some MyPy errors there. I couldn't, however, remove hashing because it was way too complex to type. Still, this means that almost all of utils is typed, which is great!
  • manim.utils.module_ops could use type[Scene], as I mentioned in a comment.
  • I rewrote that function in manim.utils.rate_functions which used bezier, because MyPy wouldn't stop complaining about that.
  • I realized that ParamSpec was introduced in Python 3.10 and we're still supporting Python 3.9. Therefore, I had to modify the changes where I suggested using ParamSpec. In the future, when we drop support for Python 3.9, we would apply it.
  • It turns out that the dir properties in ManimConfig always return str, because they're always converted to str when you attempt to set them. MyPy, however, dislikes that the getter returns a str and the setter accepts a str | Path to convert it to str, so this is something which must be fixed in a subsequent PR. In the meantime, I had to use some # type: ignore comments to silence it.
  • I fixed some other MyPy errors.

@chopan050 chopan050 enabled auto-merge (squash) January 2, 2025 07:01
@chopan050 chopan050 changed the title Adding type annotations to manim.utils.* Add type annotations to manim.utils.* Jan 2, 2025
@chopan050 chopan050 merged commit 17d4a7f into ManimCommunity:main Jan 2, 2025
21 of 22 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Status: Done
Development

Successfully merging this pull request may close these issues.

3 participants