Skip to content

Commit

Permalink
Fix #159. Allow config files to be output from external commands.
Browse files Browse the repository at this point in the history
* Convention: a filename of $(...) is actually a process specification.
* The process is executed in the current directory of the containing config file.
* The standard output of the process is used as the config file content and parsed.
* The process specification is handed to the platform shell for execution.
  • Loading branch information
adobeDan committed May 3, 2017
1 parent a5d88f2 commit c127325
Showing 1 changed file with 40 additions and 19 deletions.
59 changes: 40 additions & 19 deletions user_sync/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import logging
import os
import re
import subprocess
import types

import keyring
Expand Down Expand Up @@ -641,6 +642,7 @@ def load_sub_config(cls, filename):
def load_other_config(cls, filename):
'''
same as load_root_config, but does no post-processing.
:type filename: str
'''
return cls.load_from_yaml(filename, {})

Expand All @@ -667,25 +669,41 @@ def load_from_yaml(cls, filename, path_keys):
does the key have a default value so that must be added to
the dictionary if there is not already a value found.
'''
cls.filepath = os.path.abspath(filename)
cls.filename = os.path.split(cls.filepath)[1]
cls.dirpath = os.path.dirname(cls.filepath)
if not os.path.isfile(cls.filepath):
raise AssertionException('No such configuration file: %s' % (cls.filepath,))

# read the dict from the YAML file
try:
with open(filename, 'r', 1) as input_file:
yml = yaml.load(input_file)
except IOError as e:
# if a file operation error occurred while loading the
# configuration file, swallow up the exception and re-raise this
# as an configuration loader exception.
raise AssertionException('Error reading configuration file: %s' % e)
except yaml.error.MarkedYAMLError as e:
# same as above, but indicate this problem has to do with
# parsing the configuration file.
raise AssertionException('Error parsing configuration file: %s' % e)
if filename.startswith('$(') and filename.endswith(')'):
# it's a command line to execute and read standard output
dir_end = filename.index(']')
if filename.startswith('$([') and dir_end > 0:
dir = filename[3:dir_end]
cmd = filename[dir_end+1:-1]
else:
dir = os.path.abspath(".")
cmd = filename[3:-1]
try:
bytes = subprocess.check_output(cmd, cwd=dir, shell=True)
yml = yaml.load(bytes)
except subprocess.CalledProcessError as e:
raise AssertionException("Error executing process '%s' in dir '%s': %s" % (cmd, dir, e))
except yaml.error.MarkedYAMLError as e:
raise AssertionException('Error parsing process YAML data: %s' % e)
else:
# it's a pathname to a configuration file to read
cls.filepath = os.path.abspath(filename)
if not os.path.isfile(cls.filepath):
raise AssertionException('No such configuration file: %s' % (cls.filepath,))
cls.filename = os.path.split(cls.filepath)[1]
cls.dirpath = os.path.dirname(cls.filepath)
try:
with open(filename, 'r', 1) as input_file:
yml = yaml.load(input_file)
except IOError as e:
# if a file operation error occurred while loading the
# configuration file, swallow up the exception and re-raise this
# as an configuration loader exception.
raise AssertionException('Error reading configuration file: %s' % e)
except yaml.error.MarkedYAMLError as e:
# same as above, but indicate this problem has to do with
# parsing the configuration file.
raise AssertionException('Error parsing configuration file: %s' % e)

# process the content of the dict
for path_key, options in path_keys.iteritems():
Expand Down Expand Up @@ -773,6 +791,9 @@ def relative_path(cls, val, must_exist):
if not isinstance(val, types.StringTypes):
raise AssertionException("Expected pathname for setting %s in config file %s" %
(cls.key_path, cls.filename))
if val.startswith('$(') and val.endswith(')'):
# this presumes
return "$([" + cls.dirpath + "]" + val[2:-1] + ")"
if cls.dirpath and not os.path.isabs(val):
val = os.path.abspath(os.path.join(cls.dirpath, val))
if must_exist and not os.path.isfile(val):
Expand Down

0 comments on commit c127325

Please sign in to comment.