Skip to content

Commit 0f3f187

Browse files
committed
Results from git/hg are now cached for faster shot compilation.
1 parent 6022d97 commit 0f3f187

File tree

1 file changed

+102
-29
lines changed

1 file changed

+102
-29
lines changed

labscript/labscript.py

Lines changed: 102 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
import labscript_utils.h5_lock, h5py
3333
import labscript_utils.properties
3434
from labscript_utils.labconfig import LabConfig
35+
from labscript_utils.filewatcher import FileWatcher
3536

3637
# This imports the default Qt library that other labscript suite code will
3738
# import as well, since it all uses qtutils. By having a Qt library already
@@ -70,7 +71,10 @@
7071
startupinfo.dwFlags |= 1 #subprocess.STARTF_USESHOWWINDOW # This variable isn't defined, but apparently it's equal to one.
7172
else:
7273
startupinfo = None
73-
74+
75+
# Extract settings from labconfig
76+
_SAVE_HG_INFO = LabConfig().getboolean('labscript', 'save_hg_info', fallback=True)
77+
_SAVE_GIT_INFO = LabConfig().getboolean('labscript', 'save_git_info', fallback=False)
7478

7579
class config(object):
7680
suppress_mild_warnings = True
@@ -2174,8 +2178,88 @@ def generate_connection_table(hdf5_file):
21742178
else:
21752179
master_pseudoclock_name = compiler.master_pseudoclock.name
21762180
dataset.attrs['master_pseudoclock'] = master_pseudoclock_name
2177-
2178-
2181+
2182+
# Create a dictionary for caching results from vcs commands. The keys will be
2183+
# the paths to files that are saved during save_labscripts(). The values will be
2184+
# a list of tuples of the form (command, info, err); see the "Returns" section
2185+
# of the _run_vcs_commands() docstring for more info. Also create a FileWatcher
2186+
# instance for tracking when vcs results need updating. The callback will
2187+
# replace the outdated cache entry with a new list of updated vcs commands and
2188+
# outputs.
2189+
_vcs_cache = {}
2190+
def _file_watcher_callback(name, info, event):
2191+
_vcs_cache[name] = _run_vcs_commands(name)
2192+
2193+
_file_watcher = FileWatcher(_file_watcher_callback)
2194+
2195+
def _run_vcs_commands(path):
2196+
"""Run some VCS commands on a file and return their output.
2197+
2198+
The function is used to gather up version control system information so that
2199+
it can be stored in the hdf5 files of shots. This is for convenience and
2200+
compliments the full copy of the file already included in the shot file.
2201+
2202+
Whether hg and git commands are run is controlled by the `save_hg_info`
2203+
and `save_git_info` options in the `[labscript]` section of the labconfig.
2204+
2205+
Args:
2206+
path (str): The path with file name and extension of the file on which
2207+
the commands will be run. The working directory will be set to the
2208+
directory containing the specified file.
2209+
2210+
Returns:
2211+
results (list of (tuple, str, str)): A list of tuples, each
2212+
containing information related to one vcs command of the form
2213+
(command, info, err). The first entry in that tuple is itself a
2214+
tuple of strings which was passed to subprocess.Popen() in order to
2215+
run the command. Then info is a string that contains the text
2216+
printed to stdout by that command, and err contains the text printed
2217+
to stderr by the command.
2218+
"""
2219+
# Gather together a list of commands to run.
2220+
module_directory, module_filename = os.path.split(path)
2221+
vcs_commands = []
2222+
if compiler.save_hg_info:
2223+
hg_commands = [
2224+
['log', '--limit', '1'],
2225+
['status'],
2226+
['diff'],
2227+
]
2228+
for command in hg_commands:
2229+
command = tuple(['hg'] + command + [module_filename])
2230+
vcs_commands.append((command, module_directory))
2231+
if compiler.save_git_info:
2232+
git_commands = [
2233+
['branch', '--show-current'],
2234+
['describe', '--tags', '--always', 'HEAD'],
2235+
['rev-parse', 'HEAD'],
2236+
['diff', 'HEAD', module_filename],
2237+
]
2238+
for command in git_commands:
2239+
command = tuple(['git'] + command)
2240+
vcs_commands.append((command, module_directory))
2241+
2242+
# Now go through and start running the commands.
2243+
process_list = []
2244+
for command, module_directory in vcs_commands:
2245+
process = subprocess.Popen(
2246+
command,
2247+
cwd=module_directory,
2248+
stdout=subprocess.PIPE,
2249+
stderr=subprocess.PIPE,
2250+
startupinfo=startupinfo,
2251+
)
2252+
process_list.append((command, process))
2253+
2254+
# Gather up results from the commands issued.
2255+
results = []
2256+
for command, process in process_list:
2257+
info, err = process.communicate()
2258+
info = info.decode('utf-8')
2259+
err = err.decode('utf-8')
2260+
results.append((command, info, err))
2261+
return results
2262+
21792263
def save_labscripts(hdf5_file):
21802264
if compiler.labscript_file is not None:
21812265
script_text = open(compiler.labscript_file).read()
@@ -2199,28 +2283,17 @@ def save_labscripts(hdf5_file):
21992283
# Doesn't seem to want to double count files if you just import the contents of a file within a module
22002284
continue
22012285
hdf5_file.create_dataset(save_path, data=open(path).read())
2202-
if compiler.save_hg_info:
2203-
hg_commands = [['log', '--limit', '1'], ['status'], ['diff']]
2204-
process_list = []
2205-
for command in hg_commands:
2206-
process = subprocess.Popen(['hg'] + command + [os.path.split(path)[1]], cwd=os.path.split(path)[0],
2207-
stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo)
2208-
process_list.append(process)
2209-
for process, command in zip(process_list, hg_commands):
2210-
info, err = process.communicate()
2211-
if info or err:
2212-
hdf5_file[save_path].attrs['hg ' + str(command[0])] = info.decode('utf-8') + '\n' + err.decode('utf-8')
2213-
if compiler.save_git_info:
2214-
module_filename = os.path.split(path)[1]
2215-
git_commands = [['branch', '--show-current'], ['describe', '--tags', '--always', 'HEAD'], ['rev-parse', 'HEAD'], ['diff', 'HEAD', module_filename]]
2216-
process_list = []
2217-
for command in git_commands:
2218-
process = subprocess.Popen(['git'] + command, cwd=os.path.split(path)[0], stdout=subprocess.PIPE,
2219-
stderr=subprocess.PIPE, startupinfo=startupinfo)
2220-
process_list.append(process)
2221-
for process, command in zip(process_list, git_commands):
2222-
info, err = process.communicate()
2223-
hdf5_file[save_path].attrs['git ' + str(command[0])] = info.decode('utf-8') + '\n' + err.decode('utf-8')
2286+
if path not in _file_watcher.files:
2287+
# Add file to watch list and create its entry in the cache.
2288+
_file_watcher.add_file(path)
2289+
_file_watcher_callback(path, None, None)
2290+
# Get a reference to the current results list in case
2291+
# another thread updates _vcs_cache[path] to point at a new
2292+
# list during this loop.
2293+
results = _vcs_cache[path]
2294+
for command, info, err in results:
2295+
attribute_str = command[0] + ' ' + command[1]
2296+
hdf5_file[save_path].attrs[attribute_str] = (info + '\n' + err)
22242297
except ImportError:
22252298
pass
22262299
except WindowsError if os.name == 'nt' else None:
@@ -2554,8 +2627,8 @@ def labscript_cleanup():
25542627
compiler.wait_delay = 0
25552628
compiler.time_markers = {}
25562629
compiler._PrimaryBLACS = None
2557-
compiler.save_hg_info = LabConfig().getboolean('labscript', 'save_hg_info', fallback=True)
2558-
compiler.save_git_info = LabConfig().getboolean('labscript', 'save_git_info', fallback=False)
2630+
compiler.save_hg_info = _SAVE_HG_INFO
2631+
compiler.save_git_info = _SAVE_GIT_INFO
25592632
compiler.shot_properties = {}
25602633

25612634
class compiler(object):
@@ -2575,8 +2648,8 @@ class compiler(object):
25752648
wait_delay = 0
25762649
time_markers = {}
25772650
_PrimaryBLACS = None
2578-
save_hg_info = LabConfig().getboolean('labscript', 'save_hg_info', fallback=True)
2579-
save_git_info = LabConfig().getboolean('labscript', 'save_git_info', fallback=False)
2651+
save_hg_info = _SAVE_HG_INFO
2652+
save_git_info = _SAVE_GIT_INFO
25802653
shot_properties = {}
25812654

25822655
# safety measure in case cleanup is called before init

0 commit comments

Comments
 (0)