ACC SHELL

Path : /usr/lib/python/site-packages/scout/
File Upload :
Current File : //usr/lib/python/site-packages/scout/__init__.py

# The scout project
# -*- coding: utf-8 -*-
# Copyright (c) 2008 Pavol Rusnak, Michal Vyskocil
# This file is distributed under the terms of the MIT License
# see the comment at the end of this file

import fnmatch
import os
import sys
import sqlite3
from optparse import OptionParser, Option, IndentedHelpFormatter, OptionValueError
from ConfigParser import SafeConfigParser
import gettext, locale

class SysConfig(object):
    data_path = '/usr/share/scout'
    data_suffix = '.db'
    config_file = 'repos.conf'
    module_path = None
    i18n_path   = '/usr/share/locale/'

    @classmethod
    def setup(cls, path):
        cls.module_path = path

    def __str__(self):
        ret = "%s {" % self.__class__
        for attr in ("data_path", "data_suffix", "config_file", "module_path", "i18n_path"):
            ret += "%s : %s" % (attr, getattr(self, attr))
        ret = ret +  "}"
        return ret

class DevConfig(SysConfig):

    @classmethod
    def setup(cls, path):
        cls.module_path = path
        cls.i18n_path = os.path.join(path, '../i18n')

class ConfigFactory(object):
    """ """

    __SYSCONFIG = SysConfig
    __DEVCONFIG = DevConfig

    @staticmethod
    def get_scout_path():
        return os.path.dirname(__file__)

    @staticmethod
    def is_installed():
        import distutils.sysconfig
        path = ConfigFactory.get_scout_path()
        pl   = distutils.sysconfig.get_python_lib()

        return path.find(pl) == 0

    @staticmethod
    def create_config_class():
        ret = ConfigFactory.__SYSCONFIG
        if not ConfigFactory.is_installed(): ret = ConfigFactory.__DEVCONFIG
        ret.setup(ConfigFactory.get_scout_path())
        return ret

Config = ConfigFactory.create_config_class()

class DefaultLang(object):

    def __init__(self, textdomain = 'scout', unicode = True):
        self._textdomain = textdomain
        self._unicode    = unicode
        lc, encoding = locale.getdefaultlocale()
        if not lc:
            lc = 'C'
        self._trans = gettext.translation(
                self._textdomain,
                Config.i18n_path,
                languages = [lc, 'C'],
                fallback = True
                )

    def install(self):
        self._trans.install(unicode = self._unicode, names = 'ngettext')
        return self

    def gettext(self, msg):
        return self._trans.ugettext(msg)

    def ngettext(self, singluar, plural, n):
        return self._trans.ungettext(singluar, plural, n)

    def __str__(self):
        return "%s {textdomain: %s, unicode: %s} " % (self.__class__, self._textdomain, self._unicode)

class NullLang(DefaultLang):

    def __init__(self, textdomain = 'scout', unicode = True):
        self._trans = gettext.NullTranslations()
        self._unicode = unicode

default_lang = DefaultLang()
null_lang    = NullLang()
default_lang.install()
locale.setlocale(locale.LC_ALL)

# the auxiliary classes, which extend the optparse classes to be usefull for scout command line parsing
class HelpOptionFound(Exception):
    pass

class ExceptionHelpOption(Option):

    ACTIONS = Option.ACTIONS + ("help2", )

    def take_action(self, action, dest, opt, value, values, parser):
        if action == "help2":
            raise HelpOptionFound()
        else:
            Option.take_action(action, dest, opt, value, values, parser)

class ModuleListFormatter(IndentedHelpFormatter):
    """ Same as IndentedHelpFormatter, but the epilog is not a string, but the reference to a function"""

    def format_epilog(self, epilog):
        if epilog:
            return "\n" + epilog() + "\n"
        else:
            return ""

class ScoutOptionParser(OptionParser):
    """
    The modified option parser for scout. The three main differences are:

        - redefined _add_help_option - this adds an ExceptionHelpOption, so the
          help option will raise an HelpOptionFound exception

        - redefined print_help method - do not encode the output, because it
          causes some errors

        - redefined _add_version_option

    """

    def _add_help_option(self):
        self.add_option(
                ExceptionHelpOption(
                    "-h", "--help",
                    action = "help2",
                    help = _("show this help message and exit")
                    ))

    def _add_version_option(self):
        self.add_option(
                "--version",
                action = "version",
                help = _("show program's version number and exit")
                )

class CoreOptionParser(object):
    """
    This is a command-line parser for core. Just prints help for available
    modules and should return one a name of one of them.

    This is an accestor of older CommandLineParser with better design - usage
    of the optparse module, instead of the own parsing - and better support for
    unittesting.

    Instance (read-only) attributes:
        - prog:         a program name
        - module:       a name of selected module
        - module_args:  an arguments for modules
        - format:       an global option - output format
        - help:         a formatted help string

    Example:
        parser = CoreOptionParser(list_of_available_formats, list_of_available_modules)
        try:
            parser.parse_args()
        except HelpOptionFound help:
            parser.print_help()

        print "Selected module: %s" % (parser.module)
        print "Arguments for module: %s" % (parser.module_args)
    """

    def __init__(self, formats, modules = None):

        self._prog = os.path.basename(sys.argv[0])

        self._modules = list()
        self.add_module(modules)

        #defaults
        self._format = "table"

        #parser
        self._parser = ScoutOptionParser(
                prog = self._prog,
                usage = _("Usage: %prog [global_opts] module [local_opts] search_term"),
                epilog = self._help_modules,
                formatter = ModuleListFormatter()
                )

        self._parser.add_option(
                "-f", "--format",
                help = _("select the output format (%s, default is %s)") % (', '.join(formats), self._format),
                default = self._format,
                type = "choice",
                choices = formats
                )
        self._parser.add_option(
                "-l", "--list",
                help = _("list of available modules"),
                action = "store_true",
                dest = "listing",
                default = False
                )

    def add_module(self, modules):
        if modules != None:
            if not hasattr(modules, "__iter__"):
                modules = (modules, )
            for m in modules:
                self._modules.append(m)
            self._modules.sort()
        return self

    def _help_modules(self):
        ret = _("Available modules:\n")
        maxlen = len(max((m[0] for m in self._modules), key = len))
        for m in self._modules:
            ret += "    %s : %s\n" % (m[0].ljust(maxlen), m[1])
        return ret

    def _split_argument_line(self, args):
        # Splits the argument line to the tupe - (core_args, module_args)
        # ['scout',  '-f',  'xml',  'python',  'optparse'] -> (['scout'], ['-f'], ['xml']), (['python'], ['optparse'])
        # if none of the module name is defined, the module_args was empty
        module_i = -1
        for i, arg in enumerate(args):
            if arg in self.module_names:
                module_i = i
                break
        if module_i == -1:
            return (args, [])
        return (args[:module_i], args[module_i:])

    def parse_args(self, args = None):
        """
        parse the arguments from sys.argv, or user defined ones!

        return - the Options class instance with options from command line and a name of module
        but it may raise:
            - HelpOptionFound: when the help string was found
            - OptionValueError: when the name of module was not found in command line
        for bad format argument it still use an optparse's sys.exit() method
        """
        # this line is necessary, the args = sys.argv[1:] in function definition seems doesn't works
        if not args:
            args = sys.argv[1:]
        # fix the usage of the command python scout-cmd.py ...
        if len(args) == 1 and args[0].find("scout") != -1:
            args = args[1:]

        if len(args) == 0:
            raise HelpOptionFound() # if none of the argument was defined, show help

        core_args, self._module_args = self._split_argument_line(args)

        # try to load the module name
        module = None
        if len(self._module_args) != 0:
            module = self._module_args[0]

        opts, args = self._parser.parse_args(core_args)

        # no HelpOptionFound raised, the module name is mandatory
        # FIXME: this would be rewritten
        if not opts.listing and len(self._module_args) == 0:
            msg = _("The name of module is mandatory. Use %s --help") % (self._prog)
            raise OptionValueError(msg)

        return Options(self.__opts2dict(opts), args = {'module' : module})

    def print_help(self, file = sys.stderr):
        self._parser.print_help(file)
        return self

    def error(self, msg):
        self._parser.error(msg.encode(sys.stderr.encoding, "replace"))
        return self

    def __opts2dict(self, opts):
        ret = {}
        for opt in [opt for opt in self._parser.option_list if opt.dest != None]:
            ret[opt.dest] = getattr(opts, opt.dest)
        return ret

    # ------------------------------ read-only properties ------------------------------
    def __get_prog(self):
        return self._parser.prog
    prog = property(__get_prog)

    def __get_module_args(self):
        return self._module_args[1:]
    module_args = property(__get_module_args)

    def __get_module_names(self):
        return [m[0] for m in self._modules]
    module_names = property(__get_module_names)

    def __get_help(self):
        return self._parser.format_help()
    help = property(__get_help)

class Options(object):
    """
    This class contains the options for modules. The options are accessible as attributes, or as dictionary keys:

    o = Options(...)
    o.foo
    o["foo"]
    """

    def __init__(self, opts, args = {}):
        """
        A constructor:
          opts - options from parser - {opt : value, opt2 : value}
          args - the dictionary {name : value, name : value}
        """

        self.__dict__.update(opts)
        self.__dict__.update(args)

    def __getattr__(self, name):
        return self.__dict__[name]

    def __getitem__(self, name):
        return self.__getattr__(name)

    def __str__(self):
        ret = "Options {"
        for key in self.__dict__:
            ret += "%s : %s, " % (key, self.__dict__[key])
        ret = ret + "}"
        return ret

class ModuleLoader(object):
    """
    The basic module loader - it allows to load a module from specified directory(ies)
    """

    def __init__(self, dirs = None):
        self._modules = dict()
        self._not_imported_modules = list()
        if dirs != None: self.import_from(dirs)

    def import_from(self, dirs):
        # make an non-iter item as iter
        if not hasattr(dirs, "__iter__"):
            dirs = (dirs, )
        for directory in dirs:
            self._import(directory)

    def _import(self, directory):
        if not os.path.isdir(directory):
            raise AttributeError(_("%s is not a directory") % directory)
        sys.path.insert(0, directory)
        for module_file in os.listdir(directory):
            module_name, ext = os.path.splitext(module_file)
            if ext == '.py' and not (module_name == '__init__' or module_name == 'foo'):
                try:
                    module = __import__(module_name)
                    if not hasattr(module, 'ScoutModule'):
                        del module
                    else:
                        self._modules[module.ScoutModule.name] = module
                except ImportError, ierr:
                    err_msg = ""
                    if len(ierr.args) > 0:
                        err_msg = ierr.args[0]
                    self._not_imported_modules.append((module_name, err_msg))

    def __getitem__(self, name):
        """ x.__getitem__(y) <==> x[y] """
        return self._modules[name]

    def __contains__(self, name):
        """ D.__contains__(k) -> True if D has a key k, else False """
        return name in self._modules

    def __get_modules(self):
        return self._modules.values()
    modules = property(__get_modules)

    def __get_module_names(self):
        return [m.ScoutModule.name for m in self.modules]
    module_names = property(__get_module_names)

    def __get_not_imported_modules(self):
        return self._not_imported_modules
    not_imported_modules = property(__get_not_imported_modules)

    def __str__(self):
        return "ModuleLoader %s" % (self.module_names, )


class Database(object):

    cursor = None

    def __init__(self, dbname):
        if dbname == ':memory:':
            dbfile = dbname
        else:
            if dbname.startswith('/'):
                # absolute path
                dbfile = dbname
            else:
                # only db name
                dbfile = Config.data_path + '/' + dbname + Config.data_suffix

        try:
            self.conn = sqlite3.connect(dbfile)
        except:
            print _("Cannot not open database file '%s'") % dbfile
            self.conn = None

    def __del__(self):
        self.end()
        if self.conn != None:
            self.conn.close()

    def begin(self):
        if self.conn == None:
            return False
        self.cursor = self.conn.cursor()
        return True

    def execute(self, query, *args, **kwargs):
        # it is not possible to use both a args and a keyword args
        # self.cursor should also not be None at this point
        assert(len(args) == 0 or len(kwargs) == 0 or self.cursor == None)

        if len(args) == 0 and len(kwargs) == 0:
            self.cursor.execute(query)
        elif len(args) != 0:
            self.cursor.execute(query, args)
        else:
            self.cursor.execute(query, kwargs)

    def end(self):
        if self.cursor != None:
            self.cursor.close()
            self.cursor = None

    def query(self, query, *args, **kwargs):
        """
        an abstract database query - but more clever

        query - a query to proceed

        there're two kinds of placeholders (like as original DB/API execute, but in more Pythonic way)
        - question marks (qmark style):
          query("SELECT ham, spam FROM foo WHERE bar = ? and baz = ?", bar, baz)
        - named placeholders (named style)
          query("SELECT ham, spam FROM foo WHERE bar = :bar and baz = :baz", bar = '42', baz = '42')

        Note: its not possible to combine this two types of placeholders in one call!

        return
        (1) if the length of the result is one, returns one value
        (2) if the column in the result is the only one, returns a list of values
        (3) else returns a list of tuples
        """
        # TODO: do not return an sqlite3 Errors

        if not self.begin():
            return None

        self.execute(query, *args, **kwargs)

        # create clever result
        ret = list()
        for row in self.cursor:
            if len(row) == 1:           #(2)
                ret.append(row[0])
            else:                       #(3)
                ret.append(row)
        if len(ret) == 1:               #(1)
            ret = ret[0]

        self.end()

        return ret

class TableFormatter(object):

    @classmethod
    def format(cls, result, vertical_delimiter = ' | ', node_delimiter = '-+-', horizontal_delimiter = '-'):
        """
        This method produces an output as a table. It's a base style of output and is recomended for a commandline use.

        The TableFormatter.format method has three optional arguments to improve the output:
        vertical_delimiter   - the delimiter between rows of the table, default  ' | '
        node_delimiter       - the delimiter between nodes of the table, default '-+-'
        horizontal_delimiter - the delimiter between head of the table and a body, default '-'

        The defult output is
         repository | package                   | jar           | class
        ------------+---------------------------+---------------+--------------------------------
         suse110    | geronimo                  | log4j-1.2.8   | org.apache.log4j.xml.XMLLayout
        """
        #FIXME: this code probably doesn't work with a variable length of the delimiters!
        ret = ""
        if result.is_empty():
            return ret
        col_width = [ len(unicode(x)) for x in result.get_long_names() ]
        for i in range(0, len(result.get_long_names())):
            for row in result.get_rows():
                if len(unicode(row[i])) > col_width[i]:
                    col_width[i] = len(unicode(row[i]))
        ret += ' ' + vertical_delimiter.join( map( unicode.ljust, result.get_long_names(), col_width) ) + '\n'
        ret += horizontal_delimiter + node_delimiter.join( [ horizontal_delimiter * x for x in col_width ] ) + horizontal_delimiter + '\n'
        for row in result.get_rows():
            ret += ' ' + vertical_delimiter.join( map( unicode.ljust, map(unicode, row), col_width ) ) + '\n'
        return ret

class CSVFormatter(object):

    @classmethod
    def format(cls, result, record_delimiter = ';', quote_mark = '"'):
        """
        This method produces an output in a CSV format. This style is recomended for the scripting use.

        The CSVFormatter.format method has two optional arguments to improve the output:
        record_delimiter       - the delimiter between records, default ';'
        quote_mark             - the quote used for the records, if the quote mark is in the records, is duplicated, default '"'

        The defult output is
        "repo";"pkg";"jar";"class"
        "repository";"package";"jar";"class"
        "suse110";"geronimo";"log4j-1.2.8";"org.apache.log4j.xml.XMLLayout"
        """
        record_delimiter = quote_mark + record_delimiter + quote_mark
        ret = ""
        if result.is_empty():
            return ret
        ret += quote_mark + record_delimiter.join( [ s.replace(quote_mark, 2*quote_mark) for s in result.get_long_names() ] ) + quote_mark + '\n'
        for row in result.rows:
            ret += quote_mark + record_delimiter.join([ s.replace(quote_mark, 2*quote_mark) for s in row ] ) + quote_mark + '\n'
        return ret

class XMLFormatter(object):

    @classmethod
    def format(cls, result, result_tag = 'result', head_tag = 'head', row_tag = 'row'):
        """
        This method produces an output in a XML format. This style is recomended for the communication between scout and a external systems.

        The XMLFormatter.format method has three optional arguments to improve the output:
        result_tag      - the name of the tag result, default 'result'
        head_tag        - the name of the tag head, default 'head'
        row_tag         - the name of the tag row, default 'row'

        The defult output is
        <result>
            <head>
                <repo>repository</repo>
                <pkg>package</pkg>
                <jar>jar</jar>
                <class>class</class>
            </head>
            <row>
                <repo>suse110</repo>
                <pkg>geronimo</pkg>
                <jar>log4j-1.2.8</jar>
                <class>org.apache.log4j.xml.XMLLayout</class>
            </row>
        </result>
        """

        ret = '<?xml version="1.0" encoding="UTF-8"?>\n'
        ret += '<%s>' % result_tag + '\n'
        ret += '  <%s>' % head_tag + '\n'
        short_names = result.get_short_names()
        for i in range(0, len(short_names)):
            ret += '    <%s>%s</%s>' % (short_names[i], result.get_long_names()[i], short_names[i]) + '\n'
        ret += '  </%s>' % head_tag + '\n'
        for row in result.rows:
            ret += '  <%s>' % row_tag + '\n'
            for i in range(0, len(short_names)):
                ret += '    <%s>%s</%s>' % (short_names[i], row[i], short_names[i]) + '\n'
            ret += '  </%s>' % row_tag + '\n'
        ret += '</%s>' % result_tag + '\n'
        return ret


class Result(object):

    def __init__(self, cols1, cols2 = None):
        self.cols1 = cols1
        if cols2 != None:
            self.cols2 = cols2
        else:
            self.cols2 = cols1
        self.rows = list()

    def add_row(self, row):
        if row != None:
            self.rows.append(list(row))

    def add_rows(self, rows):
        if rows != None and isinstance(rows, list):
            for row in rows:
                self.rows.append(list(row))

    def get_short_names(self):
        return self.cols1

    def get_long_names(self):
        return [ default_lang.gettext(x) for x in self.cols2 ]

    def get_table(self):
        ret = [self.cols1, self.cols2]
        ret.extend(self.rows)
        return ret

    def get_rows(self):
        return self.rows

    def is_empty(self):
        return len(self.rows) == 0

    def format(self, formatter = TableFormatter, **kwargs):
        return formatter.format(self, **kwargs)

class StringResult(Result):

    def __init__(self, string):
        assert(isinstance(string, (str, unicode)))
        self._string = string

    def is_empty(self):
        return self._string == None or len(self._string) == 0

    def format(self, *args, **kwargs):
        return self._string

class RepoConfigReader(object):
    """Class to read of an configuration of the repositories. This class
    internally uses SafeConfigParser for ConfigParser, but doesn't provide the
    same API"""

    def __init__(self):
        self.parser = SafeConfigParser()

    def read(self, file_name = None):
        """Read the config files and parse them. The files is loaded from
        directory ${Config.data_path}.  This should be suppressed by file_names
        argument.

        - file_name: is optional argument with a file name. The main purpose is
          for debugging.  """
        if file_name != None:
            self.parser.read([file_name, ])
        else:
            self.parser.read([os.path.join(Config.data_path, Config.config_file), ])
        return self

    def repos(self):
        return self.parser.sections()

    def has_repo(self, repo):
        return repo in self.parser.sections()

    def name(self, repo):
        return self.parser.get(repo, 'name')

    def baseurl(self, repo):
        return self.parser.get(repo, 'baseurl')

class RepoList(object):

    def __init__(self, modulename, repo_list = []):
        self._modulename = modulename
        self._repos = list(repo_list)
        self._load_available_repos()
        self._repos_conf = RepoConfigReader().read()

    # set repositories according to data files /usr/share/scout/<modulename>-*.db
    def _load_available_repos(self):
        for filedb in os.listdir(Config.data_path):
            if fnmatch.fnmatch(filedb, self._modulename + '-*' + Config.data_suffix):
                self._repos.append(filedb[len(self._modulename)+1:-len(Config.data_suffix)])

    def format_available_repos(self):
        ret = _("Available repositories:\n")
        if len(self._repos) == 0:
            ret += '- none -\n'
            return ret
        maxlen = len(max(self._repos, key = lambda x: x != None and len(x) or 0))
        for opt in self._repos:
            if opt == None:
                continue
            if self._repos_conf.has_repo(opt):
                ret += opt.ljust(maxlen) + ' - ' + self._repos_conf.name(opt) + '\n'
                if self._repos_conf.baseurl(opt) != '':
                    ret += ' '*maxlen + '   * ' + self._repos_conf.baseurl(opt) + '\n'
            else:
                ret += opt + '\n'
        return ret

    def add_repo(self, repo):
        self._repos.append(repo)
        return self

    # -------------------- read-only properties
    def __get_repos(self):
        return self._repos
    repos = property(__get_repos)

    def __str__(self):
        return "RepoList %s" % self.repos

class Parser(object):

    def __init__(self, modulename, repos):
        self.modulename = modulename
        usage = _("Usage: %%prog %s [options] search_term") % modulename
        self.parser = ScoutOptionParser(usage = usage.replace("%%", "%"))
        self.parser.add_option('-l', '--listrepos', action = "store_true", help = _("list available repositories"), dest = "listrepo")
        self.parser.add_option('-p', '--package', action = "store_true", help = _("inverse search by package name"), dest = "inversesearch")
        self.parser.add_option('-r', '--repo', type = 'choice', help = _("select repository to search"), default = None, choices = repos)
        self._repos = repos

    # deprecated!!!
    def add_repo(self, repo):
        opt = self.parser.get_option('-r')
        opt.choices.append(repo)

    # deprecated!!!
    def add_repos(self, repos):
        opt = self.parser.get_option('-r')
        for repo in repos:
            opt.choices.append(repo)

    # deprecated!!!
    def parse(self, args = None):
        (self.options, self.args) = self.parser.parse_args(args)
        if self.do_list():
            print self._repos.format_available_repos()
            return False
        if len(self.args) == 0:
            self.parser.print_help()
            return False
        return True

    def parse_args(self, args):
        opts, args = self.parser.parse_args(args)

        # FIXME: this would be rewritten
        if not opts.listrepo and len(args) == 0:
            raise HelpOptionFound()

        query = None
        if len(args) > 0:
            query = args[0]
        return Options(self.__opts2dict(opts), args = {'query' : query})

    # FIXME: remove!!
    def get_repos(self):
        if self.options.repo:
            repos = [self.options.repo]
        elif self.parser.get_option('-r'):
            repos = [x for x in self.parser.get_option('-r').choices if x != None]
        else:
            repos = []
        if len(repos) == 0:
            print _("No repositories found ...")
            return None
        return repos

    def do_list(self):
        return self.options.listrepo

    def print_help(self, output = sys.stderr):
        self.parser.print_help(output)

    def __opts2dict(self, opts):
        ret = {}
        for opt in [opt for opt in self.parser.option_list if opt.dest != None]:
            ret[opt.dest] = getattr(opts, opt.dest)
        return ret

class BaseScoutModule(object):
    """
    The base for all other scout modules. All of modules must be a subclass of
    this one. The base defined only an __init__ method, which binds the class
    attributes to instance ones.

    The class attributes:
     - name:    a name of the module (this is an identifier)
     - desc:    a short description (will be printed on --help)
     - sql:     the sql command (not yet parametrized)
     - result_list(2): the result lists for the Result object

    An convient use
    class MyOwnScoutModule(BaseScoutModule):

        def __init__(self, *args, **kwargs):
            super(self.__class__, self).__init__()
            # my own initialization

        def main(self, module_args = None):
            # my own implementation
    """

    name = "name"
    desc = "desc"
    sql  = "SQL"
    sqli = "SQLi"
    null_lang.install()
    result_list  = [_("repo"), _("pkg"), _("module")]
    result_list2 = [_("repository"), _("package"), _("module")]
    default_lang.install()

    def __init__(self):
        cls = self.__class__
        self._cls = cls
        for attr in ("name", "desc", "sql", "sqli", "result_list", "result_list2"):
            setattr(self, "_%s" % (attr), getattr(cls, attr))

        self._repo_list = None
        self._parser    = None

    def main(self):
        raise NotImplementedError("BaseScoutModule is not intended for usage, please reimplement the main method in your own subclass")

    # ----------- command available everywhere --------
    def do_repo_list(self):
        """
        Return an Result object with list of available repositories for this module
        """
        return StringResult(self._repo_list.format_available_repos())


class SimpleScoutModule(BaseScoutModule):

    def __init__(self):
        super(SimpleScoutModule, self).__init__()

        self._repo_list = RepoList(self._cls.name)
        self._parser    = Parser(self._cls.name, self._repo_list.repos)

    def getDatabase(self, repo):
        return Database(self._name + '-' + repo)

    def main(self, module_args = None):
        args = None
        try:
            args = self._parser.parse_args(module_args)
        except HelpOptionFound:
            self._parser.print_help()
            sys.exit(1)

        if args.listrepo:
            return self.do_repo_list()

        repos = self._repo_list.repos
        if args.repo:
            repos = args.repo

        return self.do_query(self.prepare_term(args.query), repos, args.inversesearch)

    # ---------- commands ----------

    def do_query(self, query, repos, inversesearch = False):
        """
        Do a query. Arguments:
        query - the searched term
        repos - a list of repositories, which will be searched
                (default all avaliable repositories)
        """
        result = Result( self._result_list, self._result_list2)
        if repos == None:
            return None
        if not hasattr(repos, '__iter__'):
            repos = (repos, )
        for repo in repos:
            result.add_rows(self._query(repo, query, inversesearch))
        return result

    def _query(self, repo, term, inversesearch = False):
        db = self.getDatabase(repo)
        if not inversesearch:
            r = db.query(self._sql, '%%%s%%' % term)
        else:
            r = db.query(self._sqli, '%%%s%%' % term)
        if isinstance(r, list):
            return [ [repo] + list(x) for x in r ]
        else:
            return [ [repo] + list(r) ]
        return r

    def __str__(self):
        ret = "%s {" % self.__class__
        for attr in ("name", "desc", "sql", "result_list", "result_list2"):
            ret += "%s : %s" % (attr, getattr(self, attr))
        ret = ret +  "}"
        return ret

    # some modules should reimplement it for prepare of the search term
    def prepare_term(self, term):
        return term

class ScoutCore(object):

    out_formatters = {
            'csv' :     CSVFormatter,
            'xml' :     XMLFormatter,
            'table' :   TableFormatter,
            }

    @classmethod
    def load_all_modules(cls):
        cls.ml = ModuleLoader(Config.module_path)
        cls.modules = ((m.ScoutModule.name, m.ScoutModule.desc) for m in cls.ml.modules)

    @classmethod
    def run(cls):

        clp = CoreOptionParser(cls.out_formatters.keys(), cls.modules)
        args = None
        try:
            args = clp.parse_args()
        except HelpOptionFound:
            clp.print_help()
            sys.exit(1)
        except OptionValueError, ove:
            clp.error(ove.msg)

            print ove.msg
            sys.exit(2)

        if args.listing:
            return "\n".join(cls.ml.module_names)

        module = cls.ml[args.module]
        result = module.ScoutModule().main(clp.module_args)

        if result != None:
            try:
                return (result.is_empty(), result.format(formatter = cls.out_formatters[args.format]))
            except KeyError, kerr:
                raise SystemExit(_("Cannot find a formatter for a %s") % args.format)
        else:
            return (True, None)

# Copyright (c) 2008 Pavol Rusnak, Michal Vyskocil
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

ACC SHELL 2018