-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Replace the global config dict with new class ManimConfig #620
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
Conversation
…reparation for the new classes coming soon
Everything here looks good to me, it's just a matter of addressing the problems with the merge and docs. |
Just pushed a new commit that fixes the -s flag when the scene contains no animations. Thanks @kolibril13 for reporting. (It also removes a couple of repeated keys.) @ManimCommunity/tests could you please double-check the test that was added inhttps://github.com/ManimCommunity/manim/pull/620/commits/e74289e57955cf160239a6337370f7a8ef4a2d54? I'm not sure if there's a better way of doing this. |
Yeah, I want to add a few tests for some more CLI flags, at least. Do you have suggestions on what else I should be testing? |
Update: tests pass locally and docs build. However, there are a few tests that break only in python3.6. I'll investigate. |
All tests pass! Now I'll add some more tests as requested by @huguesdevimeux |
(Hopefully) last update: tests pass, docs build. Added a few more tests and cleaned up some unnecessary code. @eulertour I believe there are no tests for the js renderer so if you want to test that out before merging, that'd be great. Otherwise, I move we merge this ASAP because rebasing is killing me. I will write documentation in a separate PR. |
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.
This looks great; amazing job! I love how readable especially arg parsing suddenly is. 🚀
I only have two issues/comments, but they can both be settled in follow-up PRs.
def make_config_parser(): | ||
"""Make a ConfigParser object and load the .cfg files.""" | ||
def make_config_parser(custom_file=None): | ||
"""Make a ConfigParser object and load the .cfg files. |
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.
Detailed documentation for module-level functions is currently not rendered it seems (the oneline-summary is rendered in the list of functions, but documentation for parameters and notes etc. are not) -- we should take care of this in a separate PR, though.
@eulertour do you want to test JS renderer stuff before I merge and inevitably break everything? |
I'm actually fine with you merging it first, since after making the CairoRenderer the JS renderer will have to be reworked anyway |
Motivation
Short version: the config system is Bad. This PR makes it Less Bad by introducing a new dict-like class
ManimConfig
. Instances of this class behave pretty much like the previous globalconfig
dict, but maintain internal consistency, among other features. Long version: see #337.List of Changes
The diff is long. Here I explain the changes file by file. Hopefully this will make reviewing easier. Note most of the changes are due to the removal of
file_writer_config
.manim/config/utils.py
into a new filemanim/config/logger.py
. This also explains the diff intests/control_data/logs_data/BasicSceneLoggingTest.txt
.file_writer_config
,camera_config
andconfig
, and replaces them with a (still global) instance of theManimConfig
class. This global is also calledconfig
. This accounts for the diff inmanim/__main__.py
,manim/camera/camera.py
,manim/config/__init__.py
,manim/grpc/impl/frame_server_impl.py
,manim/mobject/mobject.py
,manim/mobject/svg/text_mobject.py
,manim/utils/module_ops.py
,tests/utils/GraphicalUnitTester.py
,tests/test_copy.py
,tests/test_container.py
,tests/helpers/graphical_units.py
, andmanim/utils/tex_file_writing.py
.video_dir
,tex_dir
,text_dir
,images_dir
tomanim/config/default.cfg
. These DO NOT use the placeholder syntax used byconfigparser
. Instead, they use f-string syntax, as used byManimConfig._get_dir()
.manim/config/main_utils.py
has two modifications: theinit_dirs
function has been moved tomanim/config/utils.py
and the functionupdate_config_with_cli
has disappeared and its logic moved insideManimConfig.digest_args()
.manim/constants.py
: we have changed the default resolution from "production" to "high" (I believe either @kolibril13 or @huguesdevimeux did this) so I had to update one line in this file.tests/test_scene_rendering/test_cli_flags.py
tests/test_scene_rendering/test_caching_relalted.py
, which had the same content astests/test_scene_rendering/test_caching_related.py
(note the typo). This was already removed before, but I don't know how it snuck into my branch 🤷tests/test_logging/test_logging.py
,tests/test_scene_rendering/test_caching_related.py
,tests/test_scene_rendering/conftest.py
, andtests/test_cli_flags.py
each have a one-line quality of live improvement.tests/test_config.py
has been modified becauseManimConfig
no longer allows adding/updating keys that did not previously exist.manim/scene/scene_file_writer.py
is explained by the removal ofscene_file_writer
EXCEPT for the new logic in lilne 128. This change in logic now checks whether a file exists before using it (which makes sense and has nothing to do with theManimConfig
class!)manim/scene/scene.py
is explained by the removal ofscene_file_writer
EXCEPT for the change in logic in lines 100-102 (some child objects are now made to use the global config objects instead of passing around things like**camera_config
) and the logic in line 128, which now correctly restores the previous state ofskip_animations
(before it was always just set to False, which makes no sense).manim/config/utils.py
- this is the big one. Theinit_dirs()
function comes frommanim/config/main_utils.py
, and the parser logic has moved tomanim/config/parser.py
. The big change: it now contains the definition of the two main classesManimConfig
andManimFrame
. More on these later.Explanation for Changes
Ok the main idea of
ManimConfig
is the following. It behaves like a dict but it keeps track of all the logic between different config options. To behave like a dict, it implements some dunder methods such as__iter__
,__len__
,__contains__
,__getitem__
,__setitem__
. However, for documentation purposes, each config option is also an attribute. For example the config optionwrite_to_movie
can be accessed asconfig["write_to_movie"]
or asconfig.write_to_movie
. The former is backwards compatible, while the latter allows for easy documentation and auto-completion. To implement this behavior, there is a private attributeself._d
which is an actual dictionary which contains the config options as keys. The user should never access it directly. Most of the get/set code touchesself._d
in one way or another. Furthermore,ManimConfig
does not allow to add new keys or remove existing keys (see__delitem__
and__delattr__
).ManimConfig
can only be initialized by passing aConfigParser
instance. This will usually contain the options set inmanim/config/default.cfg
, as well as any other config files such as the folder-wide or the user-wide. The globalconfig
is instantiated on line 25 ofmanim/config/__init__.py
with the config parser object returned bymanim/config/utils.py::make_config_parser
. During initialization, the functiondigest_parser()
is called, which consumes the parser. After this, all config files have been parsed and digested into the globalconfig
, and manim is ready to go.Now, if manim is being called from the command line, then
manim/__main__.py
will callconfig.digest_args(args)
. This will take the parsedsys.argv
args and update the necessary config options. Note that the options from the config files and from the CLI flags are parsed at different times. Because of this, there is no longer any need of hacky things such as trying to guess whether manim was being used from the command line or not. This is a huge gain in readability I think.I have left placeholder methods
ManimConfig.digest_dict
andManimConfig.digest_file
. The latter will eventually allow to read a configuration file on the fly. This feature was requested by @ManimCommunity/tests and will simplify testing a lot.Finally, the rest of the code of
ManimConfig
is the definition of a ton of properties. Each config option is now a property. In this way, we have complete control of what happens when a config option is read or set. In this way, we keep internal consistency of all options. For example, seeManimConfig.tex_template_file
orManimConfig.dry_run
. Look also atManimConfig.quality
which now allows to change the width, height, and frame rate with a single line of codeconfig["quality"] = "low_quality"
(@eulertour recently requested aset_quality
method 😉 ). The rest of the properties are mostly boilerplate. I chose to use the syntaxconfig_opt = property(...)
because it reduces boilerplate and wasted space (though I am not married to this). There are a few helpers that help with this boilerplate (_set_from_list
,_set_boolean
,_set_str
,_set_between
,_set_pos_number
).The
ManimConfig
class is meant to contain all the configuration in one place, a single source of truth, and it replacesfile_writer_config
, and most ofconfig
, and is meant to be used mostly internally. HOWEVER, I have also added a new class,ManimFrame
, which is meant to contain the config options that are related to frame/video geometry and are meant to be used mostly by the user. The global frame is calledframe
and is instantiated in line 26 ofmanim/config/__init__.py
and depends intimately on the globalManimConfig
object. In short,ManimFrame
just reflects a bunch of config options set inManimConfig
(see bottom of the filemanim/config/utils.py
) but aManimFrame
is completely immutable. My intention is to allow the user to do something likefrom manim import frame as F
and then do something likeF.pixel_width
orF.top
. (This is something @kolibril13 has been asking for for a while!) However, I'm not married to the current implementation ofManimFrame
and that is subject to change. Please let's focus right now onManimConfig
first!That's all folks. I've done my best to explain the inner workings of this beast. Please let me know if I missed anything.
Testing Status
Passes locally. Added a new test for the -s flag.
Further Comments
Right now the
ManimFrame
class is implemented but never used. I wanted to reduce the size of this PR as much as possible before actually using it. If you have general comments on it, please leave them here. If you have implementation comments, let's discuss them in the next PR.Once the review of this stabilizes a bit, I will write the changelog right before merging. I will add extensive documentation in a separate PR.
I still want to add a test for
--disable_caching
before merging this, but haven't figured out how. Help please @ManimCommunity/tests.Acknowledgement