16
16
import sys
17
17
import subprocess
18
18
import keyword
19
+ import threading
19
20
from inspect import getcallargs
20
21
from functools import wraps
21
22
31
32
32
33
import labscript_utils .h5_lock , h5py
33
34
import labscript_utils .properties
35
+ from labscript_utils .labconfig import LabConfig
36
+ from labscript_utils .filewatcher import FileWatcher
34
37
35
38
# This imports the default Qt library that other labscript suite code will
36
39
# import as well, since it all uses qtutils. By having a Qt library already
69
72
startupinfo .dwFlags |= 1 #subprocess.STARTF_USESHOWWINDOW # This variable isn't defined, but apparently it's equal to one.
70
73
else :
71
74
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 )
73
79
74
80
class config (object ):
75
81
suppress_mild_warnings = True
@@ -2995,8 +3001,90 @@ def generate_connection_table(hdf5_file):
2995
3001
else :
2996
3002
master_pseudoclock_name = compiler .master_pseudoclock .name
2997
3003
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
+
3000
3088
def save_labscripts (hdf5_file ):
3001
3089
"""Writes the script files for the compiled shot to the shot file.
3002
3090
@@ -3028,18 +3116,21 @@ def save_labscripts(hdf5_file):
3028
3116
# Doesn't seem to want to double count files if you just import the contents of a file within a module
3029
3117
continue
3030
3118
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 )
3039
3130
except ImportError :
3040
3131
pass
3041
3132
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 ' )
3043
3134
3044
3135
3045
3136
def write_device_properties (hdf5_file ):
@@ -3409,7 +3500,8 @@ def labscript_cleanup():
3409
3500
compiler .wait_delay = 0
3410
3501
compiler .time_markers = {}
3411
3502
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
3413
3505
compiler .shot_properties = {}
3414
3506
3415
3507
class compiler (object ):
@@ -3431,7 +3523,8 @@ class compiler(object):
3431
3523
wait_delay = 0
3432
3524
time_markers = {}
3433
3525
_PrimaryBLACS = None
3434
- save_hg_info = True
3526
+ save_hg_info = _SAVE_HG_INFO
3527
+ save_git_info = _SAVE_GIT_INFO
3435
3528
shot_properties = {}
3436
3529
3437
3530
# safety measure in case cleanup is called before init
0 commit comments