2121import logging
2222import os
2323import re
24+ import subprocess
2425import types
2526
2627import keyring
@@ -641,6 +642,7 @@ def load_sub_config(cls, filename):
641642 def load_other_config (cls , filename ):
642643 '''
643644 same as load_root_config, but does no post-processing.
645+ :type filename: str
644646 '''
645647 return cls .load_from_yaml (filename , {})
646648
@@ -667,25 +669,41 @@ def load_from_yaml(cls, filename, path_keys):
667669 does the key have a default value so that must be added to
668670 the dictionary if there is not already a value found.
669671 '''
670- cls .filepath = os .path .abspath (filename )
671- cls .filename = os .path .split (cls .filepath )[1 ]
672- cls .dirpath = os .path .dirname (cls .filepath )
673- if not os .path .isfile (cls .filepath ):
674- raise AssertionException ('No such configuration file: %s' % (cls .filepath ,))
675-
676- # read the dict from the YAML file
677- try :
678- with open (filename , 'r' , 1 ) as input_file :
679- yml = yaml .load (input_file )
680- except IOError as e :
681- # if a file operation error occurred while loading the
682- # configuration file, swallow up the exception and re-raise this
683- # as an configuration loader exception.
684- raise AssertionException ('Error reading configuration file: %s' % e )
685- except yaml .error .MarkedYAMLError as e :
686- # same as above, but indicate this problem has to do with
687- # parsing the configuration file.
688- raise AssertionException ('Error parsing configuration file: %s' % e )
672+ if filename .startswith ('$(' ) and filename .endswith (')' ):
673+ # it's a command line to execute and read standard output
674+ dir_end = filename .index (']' )
675+ if filename .startswith ('$([' ) and dir_end > 0 :
676+ dir = filename [3 :dir_end ]
677+ cmd = filename [dir_end + 1 :- 1 ]
678+ else :
679+ dir = os .path .abspath ("." )
680+ cmd = filename [3 :- 1 ]
681+ try :
682+ bytes = subprocess .check_output (cmd , cwd = dir , shell = True )
683+ yml = yaml .load (bytes )
684+ except subprocess .CalledProcessError as e :
685+ raise AssertionException ("Error executing process '%s' in dir '%s': %s" % (cmd , dir , e ))
686+ except yaml .error .MarkedYAMLError as e :
687+ raise AssertionException ('Error parsing process YAML data: %s' % e )
688+ else :
689+ # it's a pathname to a configuration file to read
690+ cls .filepath = os .path .abspath (filename )
691+ if not os .path .isfile (cls .filepath ):
692+ raise AssertionException ('No such configuration file: %s' % (cls .filepath ,))
693+ cls .filename = os .path .split (cls .filepath )[1 ]
694+ cls .dirpath = os .path .dirname (cls .filepath )
695+ try :
696+ with open (filename , 'r' , 1 ) as input_file :
697+ yml = yaml .load (input_file )
698+ except IOError as e :
699+ # if a file operation error occurred while loading the
700+ # configuration file, swallow up the exception and re-raise this
701+ # as an configuration loader exception.
702+ raise AssertionException ('Error reading configuration file: %s' % e )
703+ except yaml .error .MarkedYAMLError as e :
704+ # same as above, but indicate this problem has to do with
705+ # parsing the configuration file.
706+ raise AssertionException ('Error parsing configuration file: %s' % e )
689707
690708 # process the content of the dict
691709 for path_key , options in path_keys .iteritems ():
@@ -773,6 +791,9 @@ def relative_path(cls, val, must_exist):
773791 if not isinstance (val , types .StringTypes ):
774792 raise AssertionException ("Expected pathname for setting %s in config file %s" %
775793 (cls .key_path , cls .filename ))
794+ if val .startswith ('$(' ) and val .endswith (')' ):
795+ # this presumes
796+ return "$([" + cls .dirpath + "]" + val [2 :- 1 ] + ")"
776797 if cls .dirpath and not os .path .isabs (val ):
777798 val = os .path .abspath (os .path .join (cls .dirpath , val ))
778799 if must_exist and not os .path .isfile (val ):
0 commit comments