2626from subprocess import CalledProcessError , check_call
2727from tempfile import NamedTemporaryFile
2828
29- from jinja2 import Environment , FileSystemLoader , TemplateNotFound
29+ from jinja2 import Environment , FileSystemLoader , TemplateNotFound , UndefinedError
3030
3131VERSION = '0.1.0'
3232
@@ -150,35 +150,44 @@ def generate_exercise(env, spec_path, exercise, check=False):
150150 additional_tests = load_additional_tests (slug )
151151 spec ["additional_cases" ] = additional_tests
152152 template_path = os .path .join (slug , '.meta' , 'template.j2' )
153- try :
154- template = env .get_template (template_path )
155- tests_path = os .path .join (
156- exercise , f'{ to_snake (slug )} _test.py'
157- )
158- spec ["has_error_case" ] = has_error_case (spec ["cases" ])
159- rendered = template .render (** spec )
160- with NamedTemporaryFile ('w' , delete = False ) as tmp :
161- tmp .write (rendered )
162- format_file (tmp .name )
163- if check :
164- try :
165- if not filecmp .cmp (tmp .name , tests_path ):
166- logger .error (f'{ slug } : check failed; tests must be regenerated with bin/generate_tests.py' )
167- sys .exit (1 )
168- finally :
169- os .remove (tmp .name )
170- else :
171- shutil .move (tmp .name , tests_path )
172- print (f'{ slug } generated at { tests_path } ' )
173- except TemplateNotFound as e :
174- logger .debug (str (e ))
175- logger .info (f'{ slug } : no template found; skipping' )
153+ template = env .get_template (template_path )
154+ tests_path = os .path .join (
155+ exercise , f'{ to_snake (slug )} _test.py'
156+ )
157+ spec ["has_error_case" ] = has_error_case (spec ["cases" ])
158+ logger .info (f'{ slug } : attempting render' )
159+ rendered = template .render (** spec )
160+ with NamedTemporaryFile ('w' , delete = False ) as tmp :
161+ tmp .write (rendered )
162+ format_file (tmp .name )
163+ if check :
164+ try :
165+ if not filecmp .cmp (tmp .name , tests_path ):
166+ logger .error (f'{ slug } : check failed; tests must be regenerated with bin/generate_tests.py' )
167+ return False
168+ finally :
169+ os .remove (tmp .name )
170+ else :
171+ shutil .move (tmp .name , tests_path )
172+ print (f'{ slug } generated at { tests_path } ' )
173+ except (TypeError , UndefinedError ) as e :
174+ logger .debug (str (e ))
175+ logger .error (f'{ slug } : generation failed' )
176+ return False
177+ except TemplateNotFound as e :
178+ logger .debug (str (e ))
179+ logger .info (f'{ slug } : no template found; skipping' )
176180 except FileNotFoundError as e :
177181 logger .debug (str (e ))
178182 logger .info (f'{ slug } : no canonical data found; skipping' )
183+ return True
179184
180185
181- def generate (exercise_glob , spec_path = DEFAULT_SPEC_LOCATION , check = False , ** kwargs ):
186+ def generate (
187+ exercise_glob , spec_path = DEFAULT_SPEC_LOCATION ,
188+ stop_on_failure = False , check = False ,
189+ ** kwargs
190+ ):
182191 """
183192 Primary entry point. Generates test files for all exercises matching exercise_glob
184193 """
@@ -187,8 +196,14 @@ def generate(exercise_glob, spec_path=DEFAULT_SPEC_LOCATION, check=False, **kwar
187196 env .filters ['to_snake' ] = to_snake
188197 env .filters ['camel_case' ] = camel_case
189198 env .tests ['error_case' ] = error_case
199+ result = True
190200 for exercise in glob (os .path .join ('exercises' , exercise_glob )):
191- generate_exercise (env , spec_path , exercise , check )
201+ if not generate_exercise (env , spec_path , exercise , check ):
202+ result = False
203+ if stop_on_failure :
204+ break
205+ if not result :
206+ sys .exit (1 )
192207
193208
194209if __name__ == '__main__' :
@@ -204,6 +219,7 @@ def generate(exercise_glob, spec_path=DEFAULT_SPEC_LOCATION, check=False, **kwar
204219 )
205220 parser .add_argument ('-v' , '--verbose' , action = 'store_true' )
206221 parser .add_argument ('-p' , '--spec-path' , default = DEFAULT_SPEC_LOCATION )
222+ parser .add_argument ('--stop-on-failure' , action = 'store_true' )
207223 parser .add_argument ('--check' , action = 'store_true' )
208224 opts = parser .parse_args ()
209225 if opts .verbose :
0 commit comments