text: #!/usr/bin/env python
  # -*- cpy-indent-level: 4; indent-tabs-mode: nil -*-
  # ex: set expandtab softtabstop=4 shiftwidth=4:
  #
  # Copyright (C) 2010,2012,2013,2014,2016  Contributor
  #
  # Licensed under the Apache License, Version 2.0 (the "License");
  # you may not use this file except in compliance with the License.
  # You may obtain a copy of the License at
  #
  #     http://www.apache.org/licenses/LICENSE-2.0
  #
  # Unless required by applicable law or agreed to in writing, software
  # distributed under the License is distributed on an "AS IS" BASIS,
  # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  # See the License for the specific language governing permissions and
  # limitations under the License.
  
  """
  Small utility to populate static objects (i.e. those that do not have plenary
  templates) in the database.
  """
  
  from __future__ import print_function
  
  import argparse
  import logging
  from shlex import shlex
  from inspect import isclass
  
  import utils
  utils.load_classpath()
  
  from sqlalchemy.exc import IntegrityError
  
  from aquilon.exceptions_ import ArgumentError, NotFoundException
  import aquilon.aqdb.model
  from aquilon.aqdb.db_factory import DbFactory
  
  
  # Add a nice error reporting function to shlex
  class Lexer(shlex):
      def __init__(self, filename):
          self.filename = filename
          self.macros = {}
  
          input = open(filename, 'rt')
          return shlex.__init__(self, input, posix=True)
  
      def error(self, message):
          raise ValueError("%s %s" % (self.error_leader(self.filename), message))
  
  
  def parse_object(session, lexer, lookup=False, verbose=False):
      """
      Parse an object definition
  
      If lookup is True, the object must exist; otherwise it is created.
      """
  
      token = lexer.get_token()
      if not token:
          return None
  
      # Handle macro creation and lookup
      if token == "@":
          token = lexer.get_token()
          if not token:
              lexer.error("macro name expected.")
  
          if lookup:
              if token not in lexer.macros:
                  lexer.error("macro @%s is not defined." % token)
              return lexer.macros[token]
          else:
              if token in lexer.macros:
                  lexer.error("macro @%s is already defined." % token)
              obj = parse_object(session, lexer, lookup=True, verbose=verbose)
              lexer.macros[token] = obj
              return obj
  
      if hasattr(aquilon.aqdb.model, token):
          cls = getattr(aquilon.aqdb.model, token)
      else:
          cls = None
      if not cls or not isclass(cls) or not issubclass(cls, aquilon.aqdb.model.Base):
          lexer.error("%s is not a database class." % token)
  
      token = lexer.get_token()
      if token != "(":
          lexer.error("'(' is expected.")
  
      # parse_params() may call parse_objectr() recursively
      params = parse_params(session, lexer, verbose=verbose)
  
      try:
          if lookup:
              obj = cls.get_unique(session, compel=True, **params)
          else:
              if verbose:
                  print("Adding %s(%r)." % (cls.__name__, params))
              try:
                  obj = cls(**params)
                  session.add(obj)
                  session.flush()
                  session.expire(obj)
              except IntegrityError as err:
                  lexer.error(err)
      except ArgumentError as err:
          lexer.error(err)
      except NotFoundException as err:
          lexer.error(err)
      return obj
  
  
  def parse_params(session, lexer, verbose=False):
      """ Parse an object parameter list """
  
      params = {}
  
      while True:
          name = lexer.get_token()
          if not name:
              lexer.error("field name expected.")
  
          token = lexer.get_token()
          if token != "=":
              lexer.error("'=' expected.")
  
          token = lexer.get_token()
  
          object_lookup = False
          if token == "@":
              object_lookup = True
          elif hasattr(aquilon.aqdb.model, token):
              cls = getattr(aquilon.aqdb.model, token)
              if isclass(cls) and issubclass(cls, aquilon.aqdb.model.Base):
                  object_lookup = True
  
          if object_lookup:
              lexer.push_token(token)
              value = parse_object(session, lexer, lookup=True, verbose=verbose)
          else:
              value = token
  
              # Try to recognize booleans and numbers
              if value == "True":
                  value = True
              elif value == "False":
                  value = False
              elif value == "None":
                  value = None
              else:
                  try:
                      value = int(value)
                  except ValueError:
                      try:
                          value = float(value)
                      except ValueError:
                          pass
  
          params[name] = value
  
          token = lexer.get_token()
          if token == ")":
              break
          elif token == ",":
              pass
          else:
              lexer.error("',' or ')' expected.")
      return params
  
  
  def load_from_file(session, filename, verbose=False):
      lexer = Lexer(filename)
      try:
          while parse_object(session, lexer, verbose=verbose) is not None:
              pass
      except ValueError as err:
          session.rollback()
          raise SystemExit(err)
      except Exception:
          print("%s caught exception, bailing out." %
                lexer.error_leader(lexer.filename))
          session.rollback()
          raise
  
      session.commit()
  
  if __name__ == '__main__':
      logging.basicConfig(levl=logging.ERROR)
  
      parser = argparse.ArgumentParser(description="Loads initial data to aqdb")
      parser.add_argument("-v", "--verbose", action="store_true",
                          help="Enable verbose mode; show objects as they are added")
      parser.add_argument("-d", "--debug", action="store_true",
                          help="Enable debug mode; show SQL queries")
      parser.add_argument("-f", "--file", action="store", required=True,
                          help="Name of the input file")
      opts = parser.parse_args()
  
      if opts.debug:
          log = logging.getLogger('sqlalchemy.engine')
          log.setLevel(logging.INFO)
  
      db = DbFactory(verbose=opts.verbose)
      aquilon.aqdb.model.Base.metadata.bind = db.engine
      session = db.Session()
  
      load_from_file(session, opts.file, verbose=opts.verbose)
  