Пример #1
0
class TestSettingsManager(TestBase):
  
  #== Util ==#
  
  s = None
  """SettingsManager for main testing config file."""
  
  def setUp(self):
    self.s = SettingsManager('unit/settings/various.json', self.get_dir())
  
  #== Tests ==#
  
  def test_missing(self):
    """Missing config files must throw an error."""
    self.expect_error(lambda:SettingsManager('does_not_exist.json'), 'Invalid path to settings file')
  
  def test_override(self):
    """User properties must override defaults."""
    self.assertEqual(self.s.require('gal_db_path'), 'foo.bar')
  
  def test_require(self):
    """Retrieval of required non-empty properties."""
    self.expect_error(lambda: self.s.require('12345'), 'missing key',
                      'Require throws error for missing key')
    self.assertEqual(self.s.require('data_dir'), 'data')
  
  def test_catlist(self):
    """Retrieval of lists."""
    self.assertEqual(self.s.require('cats_unconditional', list), ['Foo','*Bar  '])
  
  def test_default_string(self):
    """String is expected by default."""
    self.expect_error(lambda:self.s.require('cats_unconditional'),
                      "Config file needed <type")
  
  def test_abspath(self):
    """Computation of absolute path from absolute and relative paths."""
    self.assertEqual(self.s.abspath('test/abs'), '/tmp')
    
    actual = self.s.abspath('test/rel')
    expected = os.path.join(self.get_dir(), 'unit/settings/beyond')
    self.assertEqual(os.path.realpath(actual), os.path.realpath(expected))
Пример #2
0
class KPAWebGen(object):
  """Core code for gallery generator."""
  
  #== Setup ==#

  # A sequence is an array of 1 or more actions.
  # Actions can be specified by names.
  
  def __init__(self):
    # source of lookup table
    actions = [('read', self.act_read),
               ('localweb', self.act_localweb),
               ('publish', self.act_publish)]
    # save off the ordered names and the lookup table 
    self.action_names = zip(*actions)[0]
    self.action_lookup = dict(actions)
    # TODO: Python subproject shouldn't know about global directory structure
    root = '../../..'
    self.clj_path = ResourceManager.locate(root + '/clj')

  action_names = None
  """Names of program actions, in order."""
  action_lookup = None
  """Dict of action names to functions."""
  sequence_all = 'all'
  """Specifies that all actions should be taken (in order.)"""

  clj_path = None
  """Location of Clojure jar."""
  settings = None
  """SettingsManager object."""
  
  # String -> List<String>
  def get_sequence(self, seq_name):
    """Expand requested sequence into action names. If invalid sequence name,
    throw error with message about allowed names."""
    if seq_name == self.sequence_all:
      return self.action_names
    if seq_name not in self.action_names:
      ls = ", ".join(list(self.action_names)+[self.sequence_all])
      raise Exception("Sequence must be one of: %s" % (ls,))
    return [seq_name]
  
  # String List<String> ->
  def dispatch(self, config_loc, sequence):
    """Dispatch on action names."""
    self.settings = SettingsManager(config_loc)
    for name in sequence:
      print("Running action: %s" % name, file=sys.stdout)
      self.action_lookup[name]()

  def invoke_clojure_action(self, task):
    # We have to use lein instead of launching an uberjar since some of
    # the integration tests are in Clojure, and therefore need to call
    # the .py script...
    # Once everything is in Clojure, this won't be an issue.
    subprocess.check_call(['lein', 'trampoline', 'run',
                           '--task', task,
                           '--config', self.settings.config['_config_path'],
                           '--debug', str(Global.debug).lower()],
                           shell=False, cwd=self.clj_path)
  
  #== Actions ==#
  
  def act_read(self):
    """Update our DB with the latest KPA data."""
    did = Updater(self.settings.abspath('kpa_dir'),
                  self.settings.require('shadow_path')).update(self.force_read)
    if not did:
      print("Skipping read since DB has not changed.", file=sys.stdout)
  
  def act_localweb(self):
    """Generate local website."""
    self.invoke_clojure_action('localweb')
  
  def act_publish(self):
    """Publish to remote website."""
    self.invoke_clojure_action('publish-s3')
  
  #== Interfaces ==#

  force_read = False

  def main(self, args):
    """Validate arguments (minus script name) and call core code."""
    logging.debug('Main args: %s' % args)
    
    if len(args) < 2:
      if len(args) != 1 or args[0] != '--help':
        print("Wrong number of arguments.", file=sys.stderr)
      self.die_usage()
    config_loc = args[0]
    sequence_name = args[1]
    if len(args) == 3 and args[2] == '--force-read':
      self.force_read = True
    
    try:
      sequence = self.get_sequence(sequence_name)
    except Exception as e:
      KPAWebGen.die_top("Could not determine requested action: " + str(e),
                        Constants.ERR_ACTION_INVALID)
    
    try:
      self.dispatch(config_loc, sequence)
    except Exception as e:
      msg = "Error(%s): %s" % (e.__class__.__name__, e)
      KPAWebGen.die_top(msg, Constants.ERR_UNKNOWN_REASON)
  
  @staticmethod
  def die_usage():
    usage = """Usage:
kpawebgen --help
kpawebgen config-file sequence [options]

Options:
--force-read
  Update the shadow DB even if it dopesn't appear to need it."""
    KPAWebGen.die_top(usage, Constants.ERR_BAD_ARGS, isExc=False)
  
  @staticmethod
  def die_top(msgs=None, code=Constants.ERR_NORMAL_EXIT, isExc=True):
    """Die at the top level. Do not call from core code."""
    if Global.debug and isExc:
      traceback.print_exc(file=sys.stdout)
    if isinstance(msgs, basestring):
      msgs = [msgs]
    if msgs:
      for m in msgs:
        print(m, file=sys.stderr)
    sys.exit(code)