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))
INITIAL_CWD = '/home/pmwh/projects/potluck'

The working directory on launch (e.g., not a temporary sandbox directory).

def potluck_src_dir():
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.

def get_spec_module_name():
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.

def get_spec_file_name():
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.

def current_solution_path():
 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.

def current_starter_path():
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.

def deduce_task_id():
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.

def unused_filename(orig_name):
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.