-
Notifications
You must be signed in to change notification settings - Fork 2.2k
Configparser setup: figuring out config.py, constants.py, dirs.py, CLI arguments, and more [WIP] #98
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
… where the user can change them if needed
After more thought, I have a proposal that would fix points 1, 2, and 3 under "The Discussion Points" above. In the original code base, I propose that we leave inside Another reason why I was reluctant to put So, to sum up (and I do apologize for the literal walls of text in this PR), I'm suggesting that on top of the current PR, we take out (many) more variables out of Please let me know if this makes sense, as this will involve quite a bit more refactoring and it will most likely NOT be backward-compatible. EDIT: on the topic of backward-compatibility, we could easily implement this as well as keep the old constants/configs and emit a |
My remarks so far before I go to sleep
Alternatives:
Please, please, please have the config params be in lower (snake) case...
To be used with attributes yes. However, we do not need that, of course. We can just have the config dict be stored in some shape-free dictionary or something... This is, if the config types and whatnot are specified somewhere else (E.g. in some sort of Config class or w/e). Otherwise, we should use attributes in our favor, as they allow us to document each type and name. |
I think
Yes, there are libraries whose configuration files are just plain
Yes! My latest comment would achieve this, but it comes with the price of breaking changes (or |
I really like the idea of using |
Extensions/addons are actually a compelling argument. In that case, the structure would probably look like this:
This seems achievable. However, we still have the problem that it is very difficult to detect when manim has been called from the command line (see discussion point 4 in the OP). BTW, the "normal" way of having separate behavior when called from the command line is to put all of that logic inside the |
IMO it makes more sense if it's a To solve the problem of the arithmetic operations,
That is the best idea. In addition, we wouldn't be too far from the actual functioning. |
On the problem of moving the arithmetic operations from
|
Small clarification: this will never happen, because there will always be a .cfg file shipped with manim. If the user doesn't specify a CLI argument or a .cfg file, there will be a library-wide .cfg file to set defaults. |
OK I just figured out how to solve problem 4) above. It's hacky, but it will work. Also, a lot of the constant/config variables are only ever used by |
OK I heard y'all loud and clear and just pushed some changes. Let me summarize:
|
The problem with the slow rendering is caused because the Scenes are being initialized incorrectly. Line 155 in b660303
Compare to master: Line 176 in 4b6492b
The tests are still failing after the update; they aren't reading the config variables correctly. One error I got was But you can't get that far without patching the testing code because it's currently reading the wrong path and won't tell you what path it's failing to read since the error is caught and another one is thrown in its place: Lines 57 to 63 in b660303
The scene data shouldn't be loaded this way. Instead we should just attempt to load the file and let the exception be thrown if it fails. |
Actually even the tests that don't run into errors loading config variables are failing from another error: |
This is a very easy fix, get_config_test is a pytest fixture that is now deprecated (because of this PR). To me, the tests are working fine, they are just not updated yet. We can ignore them if you want, but then I don’t get the point of having tests if we ignore them every time they fail |
I designed that to be used by |
…into configparser-setup
I see three different comments here.
Also, importantly, I can't believe I failed to say this: the command |
Since this PR has already been blocking development for weeks, we've been developing without any tests up to this point for better or for worse, and adding the tests are causing more problems to solve, I'd be fine with it in this instance. I do think all of the test-related issues should be fixed eventually.
The page you linked is describing how to write assertions about raised exceptions, but in
This is true, but we will see
Gotcha, that's fine.
It's basically this snippet Lines 682 to 686 in 4b6492b
Points 2 and 3 are fine though I don't think the config files are causing the problem with the tests, at least not from what I've seen so far. |
Alright, as you wish.
I see, you are right. thank you very much for this comment. |
I actually wanted to add something related to the cascading config files: I don't think config files from the directory where we start manim should be considered. Instead we should only use the global config, one in the home directory, and one in the directory where the script is located. The reasoning is that it doesn't make much sense for a Scene to render differently depending on the place where it's run. |
Fully agreed with you there RE the cascading files. |
skp_animations was not reset to a default value. Appeared in ManimCommunity#98
* added caching functionnality * deleted comments * fixed wait bug * added docstrings * fixed minor typo in doc * added cache cleaner * added disable_caching option * supported camera_config, and hashing functions * use now digital naming when disable_caching * added an option to flush the cache. * fixed shameful typo * added max_cached_files * fixed merge issues * added digital naming when disable_caching * foxed skip_animations dlag that #98 broke * removed cairo context from hash * removed deprecated code * fixed tests by setting write_to_movie to False * removed useless code * Revert "Use file_writer_config instead of config to get text_dir for Text() (#220)" This reverts commit 7f28769. * Apply suggestions from code review Thanks @pgsuper ! Co-authored-by: Pg Biel <9021226+PgBiel@users.noreply.github.com> * fixed max_files_cached typo * Use file_writer_config instead of config to get text_dir for Text() (#220) * Use file_writer_config instead if config for Text() Fixes a bug where a keyerror would be thrown when rendering a Text() mobject. * Make testing_utils.py use file_writer_config. Thank you @huguesdevimeux ! Co-authored-by: Aathish Sivasubrahmanian <aathishs@Aathishs-MacBook-Air.local> * removed useless code * used self.camera instead of __dict__ * Apply suggestions from code review Thanks @pgsuper ! Co-authored-by: Pg Biel <9021226+PgBiel@users.noreply.github.com> * more pythonic * fixed bug related to dict keys When a dict containing keys not of the right type it caused an error. thanks to @Aatish04. * fixed minor bufg of the last commit * Now clean_cache can remove multiple files. * fixed bug when rendering multiple scenes. skp_animations was not reset to a default value. Appeared in #98 * fixed bug related to nested dict with wrong keys See previous commit. * added infinity support for max_file_cached and changed default * changed default value of max_files_cached to 100 * deleted comments * fixed docstrings * fixed format (maybe) * fixed format * fixed tests * Update expected.txt Fixes logging tests. * fixed merge conflict * fixed logging test * fixed docstrings * minor doc improvement * Apply suggestions from code review Thank you Captain Docs ! Co-authored-by: Pg Biel <9021226+PgBiel@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Pg Biel <9021226+PgBiel@users.noreply.github.com> * fixed typo of the last commit -_- Co-authored-by: Aathish <aathish04@gmail.com> Co-authored-by: Aathish Sivasubrahmanian <aathishs@Aathishs-MacBook-Air.local> Co-authored-by: Pg Biel <9021226+PgBiel@users.noreply.github.com>
Ok y'all, this is a doozy. I've been working on this and there's a few things that we need to decide as an organization. I'm going to try to make my points clearly and I'll bold the points at which I need y'all's input.
Rationale
The current repo is very badly disorganized and needs a good cleanup. One of the sources of this is the fact that there are different ways of manipulating what manim does as a whole:
constants.py
sets some global variables (most of which are actually constant, but some of which are not).config.py
parses the CLI arguments, uses it to setcamera_config
among other things. It also initializes directories.dirs.py
file that allowed you to set default output directories.config.py
reads that in as well as the CLI arguments.As a result, I think it's quite the hurdle for a user to figure out where to get or set the value of a particular variable that determines how manim works. If I want to change a directory, should I use
dirs.py
, or a CLI argument? Which has precedence over the other? If I want to change the resolution, should I do it inconstants.py
, by CLI argument, or in theCONFIG
of myScene
objects?The main purpose of this PR is to provide a single default source of global truth for configuring how manim works. (And, in the event that there is more than one possibility to set the value of a particular variable, the precedence between those possibilities should be very clear and documented.)
The proposal
The main way to do this is as follows. This PR makes it so that there is one single place to write/determine/set how manim works, and one single place to read/check/get those configurations once they are in place.
manim.cfg
file to tweak the default behavior. Note this is a file that the user can have on their machine without touching the manim codebase at all. We will also ship a default config file so the user doesn't need to have such a file, but can have one if they want it.config
dictionary that can now be imported by doingfrom .config import config
(if within the library codebase) or byfrom manim import config
(if importing the library as end user).The
constants.py
file still exists, but as much as possible, it should not contain configuration variables, only constants. (more on this later).How it works
I want to explain the main workflow of how this works. For this, note that there are two different ways of using manim: importing it as a library (to have access to
Scene
,Mobjects
and so on), and as a command line script (to actually render a video).If being imported as a library, this is what's going to happen. The user will type
from manim import *
at the top of their file. This executes the contents of__init__.py
, which in turn imports/executesconfig.py
before anything else.config.py
defines and exports theconfig
dictionary by parsing themanim.cfg
file. Thisconfig
will now contain all configuration needed for the end user (and indeed for internal purposes as well). Finally,config.py
will also setup some directories (more on this later). (Note that this is a very odd way of using manim, like for example if you open a python interpreter and typeimport manim
. Importantly, this will also be the case during testing. In most cases, manim will be used in the other way, in the next paragraph.)If being used from the command line, for example as
python -m manim ...
or asmanim ...
or asmanimcm ...
, the file that gets executed first is__main__.py
. This file also imports/executesconfig.py
as the first order of business. In this case,config.py
will recognize it's being used from the command line and, after parsingmanim.cfg
first, it will also parse the CLI arguments. The CLI arguments will override the default settings defined inmanim.cfg
. At the end, the globalconfig
dictionary will contain all configuration that correctly corresponds to the defaults in the.cfg
file as well as the CLI arguments.The result
When manim is being used as a library, there is no visible change other than the fact that some imports of the type
from manim.constants import SOME_VAR
and then usingSOME_VAR
will becomeimport manim.config as config
and then usingconfig['SOME_VAR']
, because some variables (that weren't constants) have been moved fromconstants.py
into theconfig
dictionary. More on this later (see discussion points).When manim is being used from the command line to actually generate scenes, the changes are as follows. All the command line arguments are still in place. BUT, now the user may set the defaults by using their own
manim.cfg
file. For example, the-p
(or--preview
) flag is turned off by default so if the user wants the movie to play after it's rendered, currently they need to always specify-p
. After this PR, the user can create a newmanim.cfg
file with the linePREVIEW = TRUE
. If they do so, running manim without the-p
flag will still show the video once it's rendered. This has the advantage that if a user needs a particularly long CLI command with lots of flags to render their scenes, they can just dump all of those flags into themanim.cfg
file and just runmanim myfile.py
. Themanim.cfg
file currently allows to set default values for each command line argument.The discussion points
I'm just going to dump a bunch of issues here in no particular order. I'll do my best to be brief and to point exactly what needs to be answered/discussed/decided in bold.
The main problem here is that we are still conflating constants along with configuration variables, in the following way. The
PIXEL_WIDTH
(previously calledDEFAULT_PIXEL_WIDTH
) variable can be set in command line via the-l, -m, -e, -r
flags. So it is not a constant, and it should live insideconfig
, not inconstants.py
. This PR makes it so. Now, there are other values, for exampleFRAME_WIDTH
, that directly depend on the value ofPIXEL_WIDTH
, thus they are also not constant. HOWEVER, theconfigparser
library that parses.cfg
files does not support arithmetic operations. So right now there is no easy way to only setPIXEL_WIDTH
once and haveFRAME_WIDTH
and related variables update their values as well. Right now, in this PR this is being done in theconstants.py
file: it is loading theconfig
dictionary, which containsPIXEL_WIDTH
and defines the other variables accordingly. I very much don't think this should be merged as is, but I wanted to ask what is the best way to solve this, knowing that this cannot happen in the.cfg
file? My suggestion is that this happens insideconfig.py
, with a special function that defines those variables such asFRAME_WIDTH
oncePIXEL_WIDTH
is set. However, this means that the user can set only the latter and the former will always be tied to it in the same hard-coded way. EDIT: I think a good example to fix this issue is to consider the question, where do we defineTOP
? Currently, it is defined inconstants.py
, with the lineTOP = FRAME_Y_RADIUS * UP
. Now, I thinkUP
is a true constant and it should stay inconstants.py
. ButFRAME_Y_RADIUS
depends onFRAME_HEIGHT
, which is not constant and is set by CLI argument inconfig.py
. If we adhere to the strict view that only true constants must remain inconstants.py
, thenTOP
must go elsewhere.Due to the above (some configuration variables are being loaded into
constants.py
), I converted all configs and constants toALL_CAPS
. Accordingly, theCONFIG
dictionary ofScene
s andMobject
s were also turned toALL_CAPS
. I think this is ugly as hell and hope this changes in the future. Fixing the issue of constants vs config variables should make this go away.For the problem of importing
config.py
before anything else, see Relative imports #26. I think we are in the clear here.The current version does not work with
python -m manim ...
(see definition ofprog
inconfig.py
). This is because it is actually very difficult to determine if the library is being imported or run from the command line (at least from theconfig.py
file). I need suggestions for how to fix this.This is a pretty big change so if there is anything that you can think of, please bring it up here.
Currently, all of the keys in
SceneFileWriter.CONFIG
are keys of the globalconfig
dict. DoesSceneFileWriter
really need a copy of each? Pinging @PgBiel here who is working onattr
s.Yes, this has been tested and renders scenes well. I have by no means ran an exhaustive test suite of all constants/configs/CLI flags yet.
List of changes
I'll list some of the minor changes made that are not big-picture issues. Please comment on these too if you have anything to say.
start_at_animation_number
is nowFROM_ANIMATION_NUMBER
. Similarly,end_at_animation_number
is nowUPTO_ANIMATION_NUMBER
. These are shorter and have the same meaning. (I don't like the ALL_CAPS, but that's a different issue touched upon above.)dirs.py
has been eliminated. Instead ofdirs.video_dir
, now you have to doconfig['VIDEO_DIR']
. This will require changingfrom dirs import video_dir
tofrom config import config
and usingconfig['VIDEO_DIR']
. If we don't want to have to load the whole global config just for the one directory name, we can package the directories in a separate dict, also defined inconfig.py
and load it asfrom config import dirs
.input_file_path
has changed toINPUT_FILE
, andfile_name
has changed toOUTPUT_FILE
. The original names were just confusing.setup.py
.DEFAULT_PIXEL_WIDTH
,DEFAULT_PIXEL_HEIGHT
, andDEFAULT_FRAME_RATE
have been changed toPIXEL_WIDTH
,PIXEL_HEIGHT
, andFRAME_RATE
. The reason is that once the config is read and the CLI arguments parsed, they are no longer a 'default' value, but possibly a value set by the user, so the names were misleading. The new names are also shorter.PRODUCTION_QUALITY_CAMERA_CONFIG
is now just the defaultconfig['CAMERA_CONFIG']
. Also, note thatconfig['CAMERA_CONFIG']
is changed by the *-quality flags (low-quality, medium-quality, high-quality) and by the --resolution flag.scene_names
CLI argument is now--scene_names
for easier parsing.open_video_upon_completion
toPREVIEW
so it has the same name as its corresponding CLI flag. It is also way shorter.ignore_waits
option which was never used and was set to the value ofargs.preview
.-p
was by defaultFalse
, but will now default toNone
. This is done that we can easily determine whether the user actually specified this flag by comparing it toNone
. If the flag isNone
, we read the default value from themanim.cfg
file instead. Importantly, this is a subtle change in the code (see comments inside_parse_cli()
inconfig.py
if you are interested.-s
flag will also write a movie (sinceWRITE_TO_MOVIE
isTrue
by default inmanim.cfg
). See -w flag enabled by default #41.List of standing or future issues
While working on this, I realized there is a number of other things that I would also like to change, but I'm not sure if they should be part of this PR or a separate one.
DEFAULT_
in the name but that should not have it.config.py
is read). I think the directories should be created at the last possible minute. If I run manim and it crashes before writing the scene, the directories will be created but empty.PS
Mostly for my sanity: this interacts with #65, #81, #77, #151 so I'll get back to those when this is merged.