15
15
import urllib .request
16
16
from argparse import Namespace
17
17
from pathlib import Path
18
- from typing import Any , cast
18
+ from typing import TYPE_CHECKING , Any , cast
19
19
20
20
import cloup
21
21
27
27
logger ,
28
28
tempconfig ,
29
29
)
30
+ from manim .cli .cli_utils import code_input_prompt , prompt_user_with_choice
30
31
from manim .cli .render .ease_of_access_options import ease_of_access_options
31
32
from manim .cli .render .global_options import global_options
32
33
from manim .cli .render .output_options import output_options
33
34
from manim .cli .render .render_options import render_options
34
- from manim .constants import EPILOG , RendererType
35
- from manim .utils .module_ops import scene_classes_from_file
35
+ from manim .constants import (
36
+ EPILOG ,
37
+ INVALID_NUMBER_MESSAGE ,
38
+ NO_SCENE_MESSAGE ,
39
+ SCENE_NOT_FOUND_MESSAGE ,
40
+ RendererType ,
41
+ )
42
+ from manim .scene .scene_file_writer import SceneFileWriter
43
+ from manim .utils .module_ops import (
44
+ module_from_file ,
45
+ module_from_text ,
46
+ search_classes_from_module ,
47
+ )
36
48
37
49
__all__ = ["render" ]
38
50
51
+ if TYPE_CHECKING :
52
+ from ...scene .scene import Scene
53
+
54
+ INPUT_CODE_RENDER = "Rendering animation from typed code"
55
+ MULTIPLE_SCENES = "Found multiple scenes. Choose at least one to continue"
56
+
39
57
40
58
class ClickArgs (Namespace ):
41
59
def __init__ (self , args : dict [str , Any ]) -> None :
@@ -75,50 +93,41 @@ def render(**kwargs: Any) -> ClickArgs | dict[str, Any]:
75
93
76
94
SCENES is an optional list of scenes in the file.
77
95
"""
78
- if kwargs ["save_as_gif" ]:
79
- logger .warning ("--save_as_gif is deprecated, please use --format=gif instead!" )
80
- kwargs ["format" ] = "gif"
81
-
82
- if kwargs ["save_pngs" ]:
83
- logger .warning ("--save_pngs is deprecated, please use --format=png instead!" )
84
- kwargs ["format" ] = "png"
85
-
86
- if kwargs ["show_in_file_browser" ]:
87
- logger .warning (
88
- "The short form of show_in_file_browser is deprecated and will be moved to support --format." ,
89
- )
96
+ warn_and_change_deprecated_args (kwargs )
90
97
91
98
click_args = ClickArgs (kwargs )
92
99
if kwargs ["jupyter" ]:
93
100
return click_args
94
101
95
102
config .digest_args (click_args )
96
103
file = Path (config .input_file )
104
+ scenes = solve_rendrered_scenes (file )
105
+
97
106
if config .renderer == RendererType .OPENGL :
98
107
from manim .renderer .opengl_renderer import OpenGLRenderer
99
108
100
109
try :
101
110
renderer = OpenGLRenderer ()
102
111
keep_running = True
103
112
while keep_running :
104
- for SceneClass in scene_classes_from_file ( file ) :
113
+ for SceneClass in scenes :
105
114
with tempconfig ({}):
106
115
scene = SceneClass (renderer )
107
116
rerun = scene .render ()
108
- if rerun or config [ " write_all" ] :
117
+ if rerun or config . write_all :
109
118
renderer .num_plays = 0
110
119
continue
111
120
else :
112
121
keep_running = False
113
122
break
114
- if config [ " write_all" ] :
123
+ if config . write_all :
115
124
keep_running = False
116
125
117
126
except Exception :
118
127
error_console .print_exception ()
119
128
sys .exit (1 )
120
129
else :
121
- for SceneClass in scene_classes_from_file ( file ) :
130
+ for SceneClass in scenes :
122
131
try :
123
132
with tempconfig ({}):
124
133
scene = SceneClass ()
@@ -128,34 +137,121 @@ def render(**kwargs: Any) -> ClickArgs | dict[str, Any]:
128
137
sys .exit (1 )
129
138
130
139
if config .notify_outdated_version :
131
- manim_info_url = "https://pypi.org/pypi/manim/json"
132
- warn_prompt = "Cannot check if latest release of manim is installed"
140
+ version_notification ()
133
141
134
- try :
135
- with urllib .request .urlopen (
136
- urllib .request .Request (manim_info_url ),
137
- timeout = 10 ,
138
- ) as response :
139
- response = cast (http .client .HTTPResponse , response )
140
- json_data = json .loads (response .read ())
141
- except urllib .error .HTTPError :
142
- logger .debug ("HTTP Error: %s" , warn_prompt )
143
- except urllib .error .URLError :
144
- logger .debug ("URL Error: %s" , warn_prompt )
145
- except json .JSONDecodeError :
146
- logger .debug (
147
- "Error while decoding JSON from %r: %s" , manim_info_url , warn_prompt
142
+ return kwargs
143
+
144
+
145
+ def version_notification () -> None :
146
+ ### NOTE TODO This has fundamental problem of connecting every time into internet
147
+ ### As many times Renders are executed during a day.
148
+ ### There should be a caching mechanisim that will safe simple timecode and result in
149
+ ### Cached file to be fetched in most of times.
150
+
151
+ manim_info_url = "https://pypi.org/pypi/manim/json"
152
+ warn_prompt = "Cannot check if latest release of manim is installed"
153
+
154
+ try :
155
+ with urllib .request .urlopen (
156
+ urllib .request .Request (manim_info_url ),
157
+ timeout = 10 ,
158
+ ) as response :
159
+ response = cast (http .client .HTTPResponse , response )
160
+ json_data = json .loads (response .read ())
161
+ except urllib .error .HTTPError :
162
+ logger .debug ("HTTP Error: %s" , warn_prompt )
163
+ except urllib .error .URLError :
164
+ logger .debug ("URL Error: %s" , warn_prompt )
165
+ except json .JSONDecodeError :
166
+ logger .debug (
167
+ "Error while decoding JSON from %r: %s" , manim_info_url , warn_prompt
168
+ )
169
+ except Exception :
170
+ logger .debug ("Something went wrong: %s" , warn_prompt )
171
+ else :
172
+ stable = json_data ["info" ]["version" ]
173
+
174
+ if stable != __version__ :
175
+ console .print (
176
+ f"You are using manim version [red]v{ __version__ } [/red], but version [green]v{ stable } [/green] is available." ,
177
+ )
178
+ console .print (
179
+ "You should consider upgrading via [yellow]pip install -U manim[/yellow]" ,
148
180
)
149
- except Exception :
150
- logger .debug ("Something went wrong: %s" , warn_prompt )
151
- else :
152
- stable = json_data ["info" ]["version" ]
153
- if stable != __version__ :
154
- console .print (
155
- f"You are using manim version [red]v{ __version__ } [/red], but version [green]v{ stable } [/green] is available." ,
156
- )
157
- console .print (
158
- "You should consider upgrading via [yellow]pip install -U manim[/yellow]" ,
159
- )
160
181
161
- return kwargs
182
+
183
+ def warn_and_change_deprecated_args (kwargs : dict [str , Any ]) -> None :
184
+ """Helper function to print info about deprecated functions
185
+ and mutate inserted dict to contain proper format
186
+ """
187
+ if kwargs ["save_as_gif" ]:
188
+ logger .warning ("--save_as_gif is deprecated, please use --format=gif instead!" )
189
+ kwargs ["format" ] = "gif"
190
+
191
+ if kwargs ["save_pngs" ]:
192
+ logger .warning ("--save_pngs is deprecated, please use --format=png instead!" )
193
+ kwargs ["format" ] = "png"
194
+
195
+ if kwargs ["show_in_file_browser" ]:
196
+ logger .warning (
197
+ "The short form of show_in_file_browser is deprecated and will be moved to support --format." ,
198
+ )
199
+
200
+
201
+ def get_scenes_to_render (scene_classes : list [type [Scene ]]) -> list [type [Scene ]]:
202
+ result = []
203
+ for scene_name in config .scene_names :
204
+ found = False
205
+ for scene_class in scene_classes :
206
+ if scene_class .__name__ == scene_name :
207
+ result .append (scene_class )
208
+ found = True
209
+ break
210
+ if not found and (scene_name != "" ):
211
+ logger .error (SCENE_NOT_FOUND_MESSAGE .format (scene_name ))
212
+ if result :
213
+ return result
214
+ if len (scene_classes ) == 1 :
215
+ config .scene_names = [scene_classes [0 ].__name__ ]
216
+ return [scene_classes [0 ]]
217
+
218
+ try :
219
+ console .print (f"{ MULTIPLE_SCENES } :\n " , style = "underline white" )
220
+ scene_indices = prompt_user_with_choice ([a .__name__ for a in scene_classes ])
221
+ except Exception as e :
222
+ logger .error (f"{ e } \n { INVALID_NUMBER_MESSAGE } " )
223
+ sys .exit (2 )
224
+
225
+ classes = [scene_classes [i ] for i in scene_indices ]
226
+
227
+ config .scene_names = [scene_class .__name__ for scene_class in classes ]
228
+ SceneFileWriter .force_output_as_scene_name = True
229
+
230
+ return classes
231
+
232
+
233
+ def solve_rendrered_scenes (file_path_input : Path | str ) -> list [type [Scene ]]:
234
+ """Return scenes from file path or create CLI prompt for input"""
235
+ from ...scene .scene import Scene
236
+
237
+ if str (file_path_input ) == "-" :
238
+ try :
239
+ code = code_input_prompt ()
240
+ module = module_from_text (code )
241
+ except Exception as e :
242
+ logger .error (f" Failed to create from input code: { e } " )
243
+ sys .exit (2 )
244
+
245
+ logger .info (INPUT_CODE_RENDER )
246
+ else :
247
+ module = module_from_file (Path (file_path_input ))
248
+
249
+ scenes = search_classes_from_module (module , Scene )
250
+
251
+ if not scenes :
252
+ logger .error (NO_SCENE_MESSAGE )
253
+ return []
254
+ elif config .write_all :
255
+ return scenes
256
+ else :
257
+ return get_scenes_to_render (scenes )
0 commit comments