Skip to content

Commit 13239cc

Browse files
authored
Merge pull request #72 from zakv/save-git-info
Save git info
2 parents 83e10e5 + 4716ceb commit 13239cc

File tree

1 file changed

+107
-14
lines changed

1 file changed

+107
-14
lines changed

labscript/labscript.py

Lines changed: 107 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import sys
1717
import subprocess
1818
import keyword
19+
import threading
1920
from inspect import getcallargs
2021
from functools import wraps
2122

@@ -31,6 +32,8 @@
3132

3233
import labscript_utils.h5_lock, h5py
3334
import labscript_utils.properties
35+
from labscript_utils.labconfig import LabConfig
36+
from labscript_utils.filewatcher import FileWatcher
3437

3538
# This imports the default Qt library that other labscript suite code will
3639
# import as well, since it all uses qtutils. By having a Qt library already
@@ -69,7 +72,10 @@
6972
startupinfo.dwFlags |= 1 #subprocess.STARTF_USESHOWWINDOW # This variable isn't defined, but apparently it's equal to one.
7073
else:
7174
startupinfo = None
72-
75+
76+
# Extract settings from labconfig
77+
_SAVE_HG_INFO = LabConfig().getboolean('labscript', 'save_hg_info', fallback=True)
78+
_SAVE_GIT_INFO = LabConfig().getboolean('labscript', 'save_git_info', fallback=False)
7379

7480
class config(object):
7581
suppress_mild_warnings = True
@@ -2995,8 +3001,90 @@ def generate_connection_table(hdf5_file):
29953001
else:
29963002
master_pseudoclock_name = compiler.master_pseudoclock.name
29973003
dataset.attrs['master_pseudoclock'] = master_pseudoclock_name
2998-
2999-
3004+
3005+
# Create a dictionary for caching results from vcs commands. The keys will be
3006+
# the paths to files that are saved during save_labscripts(). The values will be
3007+
# a list of tuples of the form (command, info, err); see the "Returns" section
3008+
# of the _run_vcs_commands() docstring for more info. Also create a FileWatcher
3009+
# instance for tracking when vcs results need updating. The callback will
3010+
# replace the outdated cache entry with a new list of updated vcs commands and
3011+
# outputs.
3012+
_vcs_cache = {}
3013+
_vcs_cache_rlock = threading.RLock()
3014+
def _file_watcher_callback(name, info, event):
3015+
with _vcs_cache_rlock:
3016+
_vcs_cache[name] = _run_vcs_commands(name)
3017+
3018+
_file_watcher = FileWatcher(_file_watcher_callback)
3019+
3020+
def _run_vcs_commands(path):
3021+
"""Run some VCS commands on a file and return their output.
3022+
3023+
The function is used to gather up version control system information so that
3024+
it can be stored in the hdf5 files of shots. This is for convenience and
3025+
compliments the full copy of the file already included in the shot file.
3026+
3027+
Whether hg and git commands are run is controlled by the `save_hg_info`
3028+
and `save_git_info` options in the `[labscript]` section of the labconfig.
3029+
3030+
Args:
3031+
path (str): The path with file name and extension of the file on which
3032+
the commands will be run. The working directory will be set to the
3033+
directory containing the specified file.
3034+
3035+
Returns:
3036+
results (list of (tuple, str, str)): A list of tuples, each
3037+
containing information related to one vcs command of the form
3038+
(command, info, err). The first entry in that tuple is itself a
3039+
tuple of strings which was passed to subprocess.Popen() in order to
3040+
run the command. Then info is a string that contains the text
3041+
printed to stdout by that command, and err contains the text printed
3042+
to stderr by the command.
3043+
"""
3044+
# Gather together a list of commands to run.
3045+
module_directory, module_filename = os.path.split(path)
3046+
vcs_commands = []
3047+
if compiler.save_hg_info:
3048+
hg_commands = [
3049+
['log', '--limit', '1'],
3050+
['status'],
3051+
['diff'],
3052+
]
3053+
for command in hg_commands:
3054+
command = tuple(['hg'] + command + [module_filename])
3055+
vcs_commands.append((command, module_directory))
3056+
if compiler.save_git_info:
3057+
git_commands = [
3058+
['branch', '--show-current'],
3059+
['describe', '--tags', '--always', 'HEAD'],
3060+
['rev-parse', 'HEAD'],
3061+
['diff', 'HEAD', module_filename],
3062+
]
3063+
for command in git_commands:
3064+
command = tuple(['git'] + command)
3065+
vcs_commands.append((command, module_directory))
3066+
3067+
# Now go through and start running the commands.
3068+
process_list = []
3069+
for command, module_directory in vcs_commands:
3070+
process = subprocess.Popen(
3071+
command,
3072+
cwd=module_directory,
3073+
stdout=subprocess.PIPE,
3074+
stderr=subprocess.PIPE,
3075+
startupinfo=startupinfo,
3076+
)
3077+
process_list.append((command, process))
3078+
3079+
# Gather up results from the commands issued.
3080+
results = []
3081+
for command, process in process_list:
3082+
info, err = process.communicate()
3083+
info = info.decode('utf-8')
3084+
err = err.decode('utf-8')
3085+
results.append((command, info, err))
3086+
return results
3087+
30003088
def save_labscripts(hdf5_file):
30013089
"""Writes the script files for the compiled shot to the shot file.
30023090
@@ -3028,18 +3116,21 @@ def save_labscripts(hdf5_file):
30283116
# Doesn't seem to want to double count files if you just import the contents of a file within a module
30293117
continue
30303118
hdf5_file.create_dataset(save_path, data=open(path).read())
3031-
if compiler.save_hg_info:
3032-
hg_commands = [['log', '--limit', '1'], ['status'], ['diff']]
3033-
for command in hg_commands:
3034-
process = subprocess.Popen(['hg'] + command + [os.path.split(path)[1]], cwd=os.path.split(path)[0],
3035-
stdout=subprocess.PIPE, stderr=subprocess.PIPE, startupinfo=startupinfo)
3036-
info, err = process.communicate()
3037-
if info or err:
3038-
hdf5_file[save_path].attrs['hg ' + str(command[0])] = info.decode('utf-8') + '\n' + err.decode('utf-8')
3119+
with _vcs_cache_rlock:
3120+
already_cached = path in _vcs_cache
3121+
if not already_cached:
3122+
# Add file to watch list and create its entry in the cache.
3123+
_file_watcher.add_file(path)
3124+
_file_watcher_callback(path, None, None)
3125+
with _vcs_cache_rlock:
3126+
# Save the cached vcs output to the file.
3127+
for command, info, err in _vcs_cache[path]:
3128+
attribute_str = command[0] + ' ' + command[1]
3129+
hdf5_file[save_path].attrs[attribute_str] = (info + '\n' + err)
30393130
except ImportError:
30403131
pass
30413132
except WindowsError if os.name == 'nt' else None:
3042-
sys.stderr.write('Warning: Cannot save Mercurial data for imported scripts. Check that the hg command can be run from the command line.\n')
3133+
sys.stderr.write('Warning: Cannot save version control data for imported scripts. Check that the hg and/or git command can be run from the command line.\n')
30433134

30443135

30453136
def write_device_properties(hdf5_file):
@@ -3409,7 +3500,8 @@ def labscript_cleanup():
34093500
compiler.wait_delay = 0
34103501
compiler.time_markers = {}
34113502
compiler._PrimaryBLACS = None
3412-
compiler.save_hg_info = True
3503+
compiler.save_hg_info = _SAVE_HG_INFO
3504+
compiler.save_git_info = _SAVE_GIT_INFO
34133505
compiler.shot_properties = {}
34143506

34153507
class compiler(object):
@@ -3431,7 +3523,8 @@ class compiler(object):
34313523
wait_delay = 0
34323524
time_markers = {}
34333525
_PrimaryBLACS = None
3434-
save_hg_info = True
3526+
save_hg_info = _SAVE_HG_INFO
3527+
save_git_info = _SAVE_GIT_INFO
34353528
shot_properties = {}
34363529

34373530
# safety measure in case cleanup is called before init

0 commit comments

Comments
 (0)