27
27
has_metadata )
28
28
from ..utils .filemanip import (md5 , hash_infile , FileNotFoundError ,
29
29
hash_timestamp )
30
- from ..utils .misc import is_container , trim
30
+ from ..utils .misc import is_container , trim , str2bool
31
31
from .. import config , logging
32
32
33
33
iflogger = logging .getLogger ('interface' )
@@ -602,6 +602,10 @@ def _get_filecopy_info(self):
602
602
"""
603
603
raise NotImplementedError
604
604
605
+ @property
606
+ def version (self ):
607
+ raise NotImplementedError
608
+
605
609
606
610
class BaseInterfaceInputSpec (TraitedSpec ):
607
611
ignore_exception = traits .Bool (False , desc = "Print an error message instead \
@@ -628,6 +632,7 @@ class BaseInterface(Interface):
628
632
629
633
"""
630
634
input_spec = BaseInterfaceInputSpec
635
+ _version = None
631
636
632
637
def __init__ (self , ** inputs ):
633
638
if not self .input_spec :
@@ -787,6 +792,26 @@ def _check_mandatory_inputs(self):
787
792
transient = None ).items ():
788
793
self ._check_requires (spec , name , getattr (self .inputs , name ))
789
794
795
+ def _check_input_version_requirements (self ):
796
+ """ Raises an exception on version mismatch
797
+ """
798
+ version = str (self .version )
799
+ if not version :
800
+ return
801
+ # check minimum version
802
+ names = self .inputs .trait_names (** dict (min_ver = lambda t : t is not None ))
803
+ for name in names :
804
+ min_ver = str (self .inputs .traits ()[name ].min_ver )
805
+ if min_ver > version :
806
+ raise Exception ('Input %s (%s) (version %s < required %s)' %
807
+ (name , self .__class__ .__name__ , version , min_ver ))
808
+ names = self .inputs .trait_names (** dict (max_ver = lambda t : t is not None ))
809
+ for name in names :
810
+ max_ver = str (self .inputs .traits ()[name ].max_ver )
811
+ if max_ver < version :
812
+ raise Exception ('Input %s (%s) (version %s > required %s)' %
813
+ (name , self .__class__ .__name__ , version , max_ver ))
814
+
790
815
def _run_interface (self , runtime ):
791
816
""" Core function that executes interface
792
817
"""
@@ -809,6 +834,7 @@ def run(self, **inputs):
809
834
"""
810
835
self .inputs .set (** inputs )
811
836
self ._check_mandatory_inputs ()
837
+ self ._check_input_version_requirements ()
812
838
interface = self .__class__
813
839
# initialize provenance tracking
814
840
env = deepcopy (os .environ .data )
@@ -883,6 +909,14 @@ def aggregate_outputs(self, runtime=None, needed_outputs=None):
883
909
raise error
884
910
return outputs
885
911
912
+ @property
913
+ def version (self ):
914
+ if self ._version is None :
915
+ if str2bool (config .get ('execution' , 'stop_on_unknown_version' )):
916
+ raise ValueError ('Interface %s has no version information' %
917
+ self .__class__ .__name__ )
918
+ return self ._version
919
+
886
920
887
921
class Stream (object ):
888
922
"""Function to capture stdout and stderr streams with timestamps
@@ -1026,6 +1060,7 @@ class must be instantiated with a command argument
1026
1060
1027
1061
input_spec = CommandLineInputSpec
1028
1062
_cmd = None
1063
+ _version = None
1029
1064
1030
1065
def __init__ (self , command = None , ** inputs ):
1031
1066
super (CommandLine , self ).__init__ (** inputs )
@@ -1069,6 +1104,32 @@ def help(cls, returnhelp=False):
1069
1104
else :
1070
1105
print allhelp
1071
1106
1107
+ def _get_environ (self ):
1108
+ out_environ = {}
1109
+ try :
1110
+ display_var = config .get ('execution' , 'display_variable' )
1111
+ out_environ = {'DISPLAY' : display_var }
1112
+ except NoOptionError :
1113
+ pass
1114
+ iflogger .debug (out_environ )
1115
+ if isdefined (self .inputs .environ ):
1116
+ out_environ .update (self .inputs .environ )
1117
+ return out_environ
1118
+
1119
+ def version_from_command (self , flag = '-v' ):
1120
+ cmdname = self .cmd .split ()[0 ]
1121
+ if self ._exists_in_path (cmdname ):
1122
+ env = deepcopy (os .environ .data )
1123
+ out_environ = self ._get_environ ()
1124
+ env .update (out_environ )
1125
+ proc = subprocess .Popen (' ' .join ((cmdname , flag )),
1126
+ shell = True ,
1127
+ env = env ,
1128
+ stdout = subprocess .PIPE ,
1129
+ stderr = subprocess .PIPE ,
1130
+ )
1131
+ o , e = proc .communicate ()
1132
+ return o
1072
1133
1073
1134
def _run_interface (self , runtime ):
1074
1135
"""Execute command via subprocess
@@ -1085,15 +1146,7 @@ def _run_interface(self, runtime):
1085
1146
setattr (runtime , 'stdout' , None )
1086
1147
setattr (runtime , 'stderr' , None )
1087
1148
setattr (runtime , 'cmdline' , self .cmdline )
1088
- out_environ = {}
1089
- try :
1090
- display_var = config .get ('execution' , 'display_variable' )
1091
- out_environ = {'DISPLAY' : display_var }
1092
- except NoOptionError :
1093
- pass
1094
- iflogger .debug (out_environ )
1095
- if isdefined (self .inputs .environ ):
1096
- out_environ .update (self .inputs .environ )
1149
+ out_environ = self ._get_environ ()
1097
1150
runtime .environ .update (out_environ )
1098
1151
if not self ._exists_in_path (self .cmd .split ()[0 ]):
1099
1152
raise IOError ("%s could not be found on host %s" % (self .cmd .split ()[0 ],
0 commit comments