55
66import argparse
77import os
8+ import shutil
89import sys
910import traceback
1011from wvpy .jtools import convert_py_file_to_notebook , convert_notebook_file_to_py
1112
1213
1314def main () -> int :
14- parser = argparse .ArgumentParser (description = "Convert between .py and .ipynb (can have suffix, or guess suffix)" )
15+ parser = argparse .ArgumentParser (description = "Convert between .py and .ipynb or back (can have suffix, or guess suffix)" )
1516 parser .add_argument ('--quiet' , action = 'store_true' , help = 'delete input file' )
1617 parser .add_argument ('--delete' , action = 'store_true' , help = 'delete input file' )
1718 parser .add_argument (
@@ -26,67 +27,79 @@ def main() -> int:
2627 assert len (set (args .infile )) == len (args .infile )
2728 assert isinstance (args .quiet , bool )
2829 assert isinstance (args .delete , bool )
30+ # set up the work request
31+ base_names_seen = set ()
2932 input_suffices_seen = set ()
3033 tasks = []
3134 other_suffix = {'.py' : '.ipynb' , '.ipynb' : '.py' }
3235 for input_file_name in args .infile :
3336 assert isinstance (input_file_name , str )
3437 assert len (input_file_name ) > 0
35- suffix_seen = 'error' # placeholder
38+ suffix_seen = 'error' # placeholder/sentinel
39+ base_name = input_file_name
3640 if input_file_name .endswith ('.py' ):
3741 suffix_seen = '.py'
42+ base_name = input_file_name .removesuffix (suffix_seen )
3843 elif input_file_name .endswith ('.ipynb' ):
3944 suffix_seen = '.ipynb'
45+ base_name = input_file_name .removesuffix (suffix_seen )
4046 else :
4147 py_exists = os .path .exists (input_file_name + '.py' )
4248 ipynb_exists = os .path .exists (input_file_name + '.ipynb' )
4349 if py_exists == ipynb_exists :
44- raise ValueError (" if no suffix is specified, then exactly one of the .py or ipynb file forms must be present" )
50+ raise ValueError (f' { base_name } : if no suffix is specified, then exactly one of the .py or ipynb file forms must be present' )
4551 if py_exists :
4652 suffix_seen = '.py'
4753 else :
4854 suffix_seen = '.ipynb'
4955 input_file_name = input_file_name + suffix_seen
50- assert suffix_seen in other_suffix .keys ()
51- input_suffices_seen .add (suffix_seen )
52- if len (input_suffices_seen ) != 1 :
53- raise ValueError (f"saw more than one input suffix: { input_suffices_seen } " )
5456 assert os .path .exists (input_file_name )
55- output_file_name = input_file_name .removesuffix (suffix_seen ) + other_suffix [suffix_seen ]
56- if os .path .exists (output_file_name ):
57- if os .path .getmtime (output_file_name ) > os .path .getmtime (input_file_name ):
58- raise ValueError (f"output { output_file_name } is already newer than input f{ input_file_name } " )
57+ assert suffix_seen in other_suffix .keys () # expected suffix
58+ assert base_name not in base_names_seen # each base file name only used once
59+ base_names_seen .add (base_name )
60+ input_suffices_seen .add (suffix_seen )
61+ if len (input_suffices_seen ) != 1 : # only one direction of conversion in batch job
62+ raise ValueError (f"conversion job may only have one input suffix: { input_suffices_seen } " )
63+ output_file_name = base_name + other_suffix [suffix_seen ]
5964 tasks .append ((input_file_name , output_file_name ))
60- if len (input_suffices_seen ) != 1 :
61- raise ValueError (f"expected only one input suffix: { input_suffices_seen } " )
6265 # do the work
6366 for input_file_name , output_file_name in tasks :
67+ if not args .quiet :
68+ print (f'from "{ input_file_name } " to "{ output_file_name } "' )
69+ # back up result target if present
70+ if os .path .exists (output_file_name ):
71+ output_backup_file = f'{ output_file_name } ~'
72+ if not args .quiet :
73+ print (f' copying previous output target "{ output_file_name } " to "{ output_backup_file } "' )
74+ shutil .copy2 (output_file_name , output_backup_file )
75+ # convert
6476 if input_file_name .endswith ('.py' ):
6577 if not args .quiet :
66- print (f"converting { input_file_name } to { output_file_name } " )
78+ print (f" converting Python { input_file_name } to Jupyter notebook { output_file_name } " )
6779 convert_py_file_to_notebook (
6880 py_file = input_file_name ,
6981 ipynb_file = output_file_name ,
7082 )
7183 elif input_file_name .endswith ('.ipynb' ):
7284 if not args .quiet :
73- print (f" converting { input_file_name } to { output_file_name } " )
85+ print (f' converting Jupyter notebook " { input_file_name } " to Python " { output_file_name } "' )
7486 convert_notebook_file_to_py (
7587 ipynb_file = input_file_name ,
7688 py_file = output_file_name )
7789 else :
7890 raise ValueError ("input file name must end with .py or .ipynb" )
79- # do any deletions
80- if args .delete :
81- for input_file_name , output_file_name in tasks :
91+ # do any deletions
92+ if args .delete :
8293 input_backup_file = f'{ input_file_name } ~'
8394 if not args .quiet :
84- print (f"moving { input_file_name } to { input_backup_file } " )
95+ print (f" moving input { input_file_name } to { input_backup_file } " )
8596 try :
8697 os .remove (input_backup_file )
8798 except FileNotFoundError :
8899 pass
89100 os .rename (input_file_name , input_backup_file )
101+ if not args .quiet :
102+ print ()
90103 return 0
91104
92105
0 commit comments