potluck.file_utils
File-management utilities.
file_utils.py
1""" 2File-management utilities. 3 4file_utils.py 5""" 6 7import os 8import inspect 9 10from . import logging 11 12 13#---------# 14# Globals # 15#---------# 16 17INITIAL_CWD = os.getcwd() 18""" 19The working directory on launch (e.g., not a temporary sandbox 20directory). 21""" 22 23 24#---------------------# 25# Directories + paths # 26#---------------------# 27 28def potluck_src_dir(): 29 """ 30 Returns the absolute path to the directory where this file is 31 located. 32 """ 33 return os.path.abspath( 34 os.path.join(INITIAL_CWD, os.path.dirname(__file__)) 35 ) 36 37 38def get_spec_module_name(): 39 """ 40 Uses the inspect module to get the name of the specifications module, 41 assuming that we're in a function which was ultimately called from 42 that module, and that module is the only one in our current call 43 stack that ends with '.spec'. Returns 'unknown' if it can't find an 44 appropriate call frame in the current stack. 45 """ 46 cf = inspect.currentframe() 47 while ( 48 hasattr(cf, "f_back") 49 and not cf.f_globals.get("__name__", "unknown").endswith('.spec') 50 ): 51 cf = cf.f_back 52 53 if cf: 54 result = cf.f_globals.get("__name__", "unknown") 55 del cf 56 else: 57 result = "unknown" 58 59 return result 60 61 62def get_spec_file_name(): 63 """ 64 Uses the inspect module to get the path of the specifications file, 65 assuming that we're in a function which was ultimately called from 66 that module, and that module is the only one in our current call 67 stack whose filename ends with '/spec.py'. Returns 'unknown' if it 68 can't find an appropriate call frame in the current stack. 69 """ 70 cf = inspect.currentframe() 71 while ( 72 hasattr(cf, "f_back") 73 and not ( # noqa E502 74 cf.f_globals.get("__file__", "unknown")\ 75 .endswith(os.path.sep + 'spec.py') 76 ) 77 ): 78 cf = cf.f_back 79 80 if cf: 81 result = cf.f_globals.get("__file__", "unknown") 82 else: 83 result = "unknown" 84 85 del cf 86 87 return result 88 89 90def current_solution_path(): 91 """ 92 Uses `get_spec_file_name` to figure out where the current 93 specification file is, and assumes that the solution directory is 94 next to that file and named 'soln'. Returns the absolute path to 95 that directory, or raises a FileNotFoundError if it can't find it or 96 if `get_spec_file_name` fails. 97 """ 98 fn = get_spec_file_name() 99 if fn == "unknown": 100 raise FileNotFoundError("Couldn't find spec module filename.") 101 102 result = os.path.abspath(os.path.join(os.path.dirname(fn), "soln")) 103 if not os.path.exists(result): 104 raise FileNotFoundError( 105 "Expected solution directory '{}' does not exist.".format( 106 result 107 ) 108 ) 109 elif not os.path.isdir(result): 110 raise FileNotFoundError( 111 "Expected solution directory '{}' is not a directory.".format( 112 result 113 ) 114 ) 115 116 return result 117 118 119def current_starter_path(): 120 """ 121 Uses `get_spec_file_name` to figure out where the current 122 specification file is, and assumes that the starter directory is 123 next to that file and named 'starter'. Returns the absolute path to 124 that directory, or raises a FileNotFoundError if it can't find it or 125 if `get_spec_file_name` fails. 126 """ 127 fn = get_spec_file_name() 128 if fn == "unknown": 129 raise FileNotFoundError("Couldn't find spec module filename.") 130 131 result = os.path.abspath(os.path.join(os.path.dirname(fn), "starter")) 132 if not os.path.exists(result): 133 raise FileNotFoundError( 134 "Expected solution directory '{}' does not exist.".format( 135 result 136 ) 137 ) 138 elif not os.path.isdir(result): 139 raise FileNotFoundError( 140 "Expected solution directory '{}' is not a directory.".format( 141 result 142 ) 143 ) 144 145 return result 146 147 148def deduce_task_id(): 149 """ 150 Uses `get_spec_module_name` to deduce the task ID for the file we're 151 being called from. Returns "unknown" if it can't find the spec module 152 name, and logs a warning in that case. 153 """ 154 mname = get_spec_module_name() 155 if mname == "unknown": 156 cf = inspect.currentframe() 157 files = [] 158 while ( 159 hasattr(cf, "f_back") 160 and not cf.f_globals.get("__name__", "unknown").endswith('.spec') 161 ): 162 files.append(cf.f_globals.get("__file__", "???")) 163 164 if cf: 165 files.append(cf.f_globals.get("__file__", "???")) 166 167 logging.log( 168 ( 169 "Warning: unable to deduce correct task ID; results" 170 " cache may become corrupted!\nTraceback files:\n {}" 171 ).format('\n '.join(files)) 172 ) 173 return mname.split('.')[0] 174 175 176#-----------# 177# Utilities # 178#-----------# 179 180def unused_filename(orig_name): 181 """ 182 Given a desired filename, adds a numerical suffix to the filename 183 which makes it unique. If the file doesn't already exist, it returns 184 the given name without any suffix. If the given filename already has 185 a numerical suffix, it will be incremented until no file by that name 186 exists. 187 """ 188 # If the file doesn't exist, it's already unused 189 if not os.path.exists(orig_name): 190 return orig_name 191 192 # Split the filename part 193 dirs, name = os.path.split(orig_name) 194 195 # Split the base + extension 196 base, ext = os.path.splitext(name) 197 198 # Get bits of base 199 bits = base.split('-') 200 last_part = bits[-1] 201 first_stuff = '-'.join(bits[:-1]) 202 203 # If last part is a numeric suffix already... 204 if last_part.isdigit(): 205 next_digit = int(last_part) + 1 206 new_name = first_stuff + '-' + str(next_digit) + ext 207 return unused_filename(os.path.join(dirs, new_name)) 208 else: 209 return unused_filename(os.path.join(dirs, base + "-1" + ext))
The working directory on launch (e.g., not a temporary sandbox directory).
29def potluck_src_dir(): 30 """ 31 Returns the absolute path to the directory where this file is 32 located. 33 """ 34 return os.path.abspath( 35 os.path.join(INITIAL_CWD, os.path.dirname(__file__)) 36 )
Returns the absolute path to the directory where this file is located.
39def get_spec_module_name(): 40 """ 41 Uses the inspect module to get the name of the specifications module, 42 assuming that we're in a function which was ultimately called from 43 that module, and that module is the only one in our current call 44 stack that ends with '.spec'. Returns 'unknown' if it can't find an 45 appropriate call frame in the current stack. 46 """ 47 cf = inspect.currentframe() 48 while ( 49 hasattr(cf, "f_back") 50 and not cf.f_globals.get("__name__", "unknown").endswith('.spec') 51 ): 52 cf = cf.f_back 53 54 if cf: 55 result = cf.f_globals.get("__name__", "unknown") 56 del cf 57 else: 58 result = "unknown" 59 60 return result
Uses the inspect module to get the name of the specifications module, assuming that we're in a function which was ultimately called from that module, and that module is the only one in our current call stack that ends with '.spec'. Returns 'unknown' if it can't find an appropriate call frame in the current stack.
63def get_spec_file_name(): 64 """ 65 Uses the inspect module to get the path of the specifications file, 66 assuming that we're in a function which was ultimately called from 67 that module, and that module is the only one in our current call 68 stack whose filename ends with '/spec.py'. Returns 'unknown' if it 69 can't find an appropriate call frame in the current stack. 70 """ 71 cf = inspect.currentframe() 72 while ( 73 hasattr(cf, "f_back") 74 and not ( # noqa E502 75 cf.f_globals.get("__file__", "unknown")\ 76 .endswith(os.path.sep + 'spec.py') 77 ) 78 ): 79 cf = cf.f_back 80 81 if cf: 82 result = cf.f_globals.get("__file__", "unknown") 83 else: 84 result = "unknown" 85 86 del cf 87 88 return result
Uses the inspect module to get the path of the specifications file, assuming that we're in a function which was ultimately called from that module, and that module is the only one in our current call stack whose filename ends with '/spec.py'. Returns 'unknown' if it can't find an appropriate call frame in the current stack.
91def current_solution_path(): 92 """ 93 Uses `get_spec_file_name` to figure out where the current 94 specification file is, and assumes that the solution directory is 95 next to that file and named 'soln'. Returns the absolute path to 96 that directory, or raises a FileNotFoundError if it can't find it or 97 if `get_spec_file_name` fails. 98 """ 99 fn = get_spec_file_name() 100 if fn == "unknown": 101 raise FileNotFoundError("Couldn't find spec module filename.") 102 103 result = os.path.abspath(os.path.join(os.path.dirname(fn), "soln")) 104 if not os.path.exists(result): 105 raise FileNotFoundError( 106 "Expected solution directory '{}' does not exist.".format( 107 result 108 ) 109 ) 110 elif not os.path.isdir(result): 111 raise FileNotFoundError( 112 "Expected solution directory '{}' is not a directory.".format( 113 result 114 ) 115 ) 116 117 return result
Uses get_spec_file_name
to figure out where the current
specification file is, and assumes that the solution directory is
next to that file and named 'soln'. Returns the absolute path to
that directory, or raises a FileNotFoundError if it can't find it or
if get_spec_file_name
fails.
120def current_starter_path(): 121 """ 122 Uses `get_spec_file_name` to figure out where the current 123 specification file is, and assumes that the starter directory is 124 next to that file and named 'starter'. Returns the absolute path to 125 that directory, or raises a FileNotFoundError if it can't find it or 126 if `get_spec_file_name` fails. 127 """ 128 fn = get_spec_file_name() 129 if fn == "unknown": 130 raise FileNotFoundError("Couldn't find spec module filename.") 131 132 result = os.path.abspath(os.path.join(os.path.dirname(fn), "starter")) 133 if not os.path.exists(result): 134 raise FileNotFoundError( 135 "Expected solution directory '{}' does not exist.".format( 136 result 137 ) 138 ) 139 elif not os.path.isdir(result): 140 raise FileNotFoundError( 141 "Expected solution directory '{}' is not a directory.".format( 142 result 143 ) 144 ) 145 146 return result
Uses get_spec_file_name
to figure out where the current
specification file is, and assumes that the starter directory is
next to that file and named 'starter'. Returns the absolute path to
that directory, or raises a FileNotFoundError if it can't find it or
if get_spec_file_name
fails.
149def deduce_task_id(): 150 """ 151 Uses `get_spec_module_name` to deduce the task ID for the file we're 152 being called from. Returns "unknown" if it can't find the spec module 153 name, and logs a warning in that case. 154 """ 155 mname = get_spec_module_name() 156 if mname == "unknown": 157 cf = inspect.currentframe() 158 files = [] 159 while ( 160 hasattr(cf, "f_back") 161 and not cf.f_globals.get("__name__", "unknown").endswith('.spec') 162 ): 163 files.append(cf.f_globals.get("__file__", "???")) 164 165 if cf: 166 files.append(cf.f_globals.get("__file__", "???")) 167 168 logging.log( 169 ( 170 "Warning: unable to deduce correct task ID; results" 171 " cache may become corrupted!\nTraceback files:\n {}" 172 ).format('\n '.join(files)) 173 ) 174 return mname.split('.')[0]
Uses get_spec_module_name
to deduce the task ID for the file we're
being called from. Returns "unknown" if it can't find the spec module
name, and logs a warning in that case.
181def unused_filename(orig_name): 182 """ 183 Given a desired filename, adds a numerical suffix to the filename 184 which makes it unique. If the file doesn't already exist, it returns 185 the given name without any suffix. If the given filename already has 186 a numerical suffix, it will be incremented until no file by that name 187 exists. 188 """ 189 # If the file doesn't exist, it's already unused 190 if not os.path.exists(orig_name): 191 return orig_name 192 193 # Split the filename part 194 dirs, name = os.path.split(orig_name) 195 196 # Split the base + extension 197 base, ext = os.path.splitext(name) 198 199 # Get bits of base 200 bits = base.split('-') 201 last_part = bits[-1] 202 first_stuff = '-'.join(bits[:-1]) 203 204 # If last part is a numeric suffix already... 205 if last_part.isdigit(): 206 next_digit = int(last_part) + 1 207 new_name = first_stuff + '-' + str(next_digit) + ext 208 return unused_filename(os.path.join(dirs, new_name)) 209 else: 210 return unused_filename(os.path.join(dirs, base + "-1" + ext))
Given a desired filename, adds a numerical suffix to the filename which makes it unique. If the file doesn't already exist, it returns the given name without any suffix. If the given filename already has a numerical suffix, it will be incremented until no file by that name exists.