Exemplo n.º 1
0
class BasePlugin(object):  # pylint: disable=too-many-instance-attributes
    """
  a base class for plugins
  """
    def __init__(self, name, sname, modpath, basepath, fullimploc):  # pylint: disable=too-many-arguments
        """
    initialize the instance
    The following things should not be done in __init__ in a plugin
      Interacting with anything in the api except api.add, api.overload
          or dependency.add
    """
        # Examples:
        #  name : 'Actions' - from plugin file variable NAME (long name)
        #  sname : 'actions' - from plugin file variable SNAME (short name)
        #  modpath : '/client/actions.py' - path relative to the plugins directory
        #  basepath : '/home/src/games/bastproxy/bp/plugins' - the full path to the
        #                                                         plugins directory
        #  fullimploc : 'plugins.client.actions' - import location

        self.author = ''
        self.purpose = ''
        self.version = 0
        self.priority = 100
        self.name = name
        self.sname = sname
        self.dependencies = []
        self.versionfuncs = {}
        self.reloaddependents = False
        self.summarytemplate = "%20s : %s"
        self.canreload = True
        self.canreset = True
        self.resetflag = True
        self.api = API()
        self.firstactiveprio = None
        self.loadedtime = time.time()
        self.savedir = os.path.join(self.api.BASEPATH, 'data', 'plugins',
                                    self.sname)
        try:
            os.makedirs(self.savedir)
        except OSError:
            pass
        self.savefile = os.path.join(self.api.BASEPATH, 'data', 'plugins',
                                     self.sname, 'settingvalues.txt')
        self.modpath = modpath
        self.basepath = basepath
        self.fullimploc = fullimploc
        self.pluginfile = os.path.join(basepath, modpath[1:])
        self.pluginlocation = os.path.normpath(
            os.path.join(self.api.BASEPATH, 'plugins') + \
              os.sep + os.path.dirname(self.modpath))

        self.package = fullimploc.split('.')[1]

        self.settings = {}
        self.settingvalues = PersistentDictEvent(self, self.savefile, 'c')

        self._dump_shallow_attrs = ['api']

        self.api.overload('dependency', 'add', self._api_dependencyadd)
        self.api.overload('setting', 'add', self._api_settingadd)
        self.api.overload('setting', 'gets', self._api_settinggets)
        self.api.overload('setting', 'change', self._api_settingchange)
        self.api.overload('api', 'add', self._api_add)

    def _loadcommands(self):
        """
    load the commands
    """
        parser = argp.ArgumentParser(
            add_help=False,
            formatter_class=argp.RawDescriptionHelpFormatter,
            description=textwrap.dedent("""
          change a setting in the plugin

          if there are no arguments or 'list' is the first argument then
          it will list the settings for the plugin"""))
        parser.add_argument('name',
                            help='the setting name',
                            default='list',
                            nargs='?')
        parser.add_argument('value',
                            help='the new value of the setting',
                            default='',
                            nargs='?')
        self.api('commands.add')('set',
                                 self._cmd_set,
                                 parser=parser,
                                 group='Base',
                                 showinhistory=False)

        if self.canreset:
            parser = argp.ArgumentParser(add_help=False,
                                         description='reset the plugin')
            self.api('commands.add')('reset',
                                     self._cmd_reset,
                                     parser=parser,
                                     group='Base')

        parser = argp.ArgumentParser(add_help=False,
                                     description='save the plugin state')
        self.api('commands.add')('save',
                                 self._cmd_save,
                                 parser=parser,
                                 group='Base')

        parser = argp.ArgumentParser(add_help=False,
                                     description='show plugin stats')
        self.api('commands.add')('stats',
                                 self._cmd_stats,
                                 parser=parser,
                                 group='Base')

        parser = argp.ArgumentParser(add_help=False,
                                     description='inspect a plugin')
        parser.add_argument('-m',
                            "--method",
                            help="get code for a method",
                            default='')
        parser.add_argument(
            '-o',
            "--object",
            help="show an object of the plugin, can be method or variable",
            default='')
        parser.add_argument('-s',
                            "--simple",
                            help="show a simple output",
                            action="store_true")
        self.api('commands.add')('inspect',
                                 self._cmd_inspect,
                                 parser=parser,
                                 group='Base')

        parser = argp.ArgumentParser(
            add_help=False, description='show help info for this plugin')
        parser.add_argument('-a',
                            "--api",
                            help="show functions this plugin has in the api",
                            action="store_true")
        parser.add_argument('-c',
                            "--commands",
                            help="show commands in this plugin",
                            action="store_true")
        self.api('commands.add')('help',
                                 self._cmd_help,
                                 parser=parser,
                                 group='Base')

        parser = argp.ArgumentParser(add_help=False,
                                     description='list functions in the api')
        parser.add_argument('api',
                            help='api to get details of',
                            default='',
                            nargs='?')
        self.api('commands.add')('api',
                                 self._cmd_api,
                                 parser=parser,
                                 group='Base')

    def load(self):
        """
    load stuff, do most things here
    """
        self.settingvalues.pload()

        if '_version' in self.settingvalues and \
            self.settingvalues['_version'] != self.version:
            self._updateversion(self.settingvalues['_version'], self.version)

        self.api('log.adddtype')(self.sname)

        self._loadcommands()

        self.api('events.register')('%s_plugin_loaded' % self.sname,
                                    self.__afterload)

        self.api('events.register')('muddisconnect', self.__disconnect)
        self.api('events.register')('plugin_%s_savestate' % self.sname,
                                    self.__savestate)

        self.resetflag = False

    def _updateversion(self, oldversion, newversion):
        """
    update plugin data
    """
        if oldversion != newversion and newversion > oldversion:
            for i in range(oldversion + 1, newversion + 1):
                self.api('send.msg')('%s: upgrading to version %s' %
                                     (self.sname, i),
                                     secondary='upgrade')
                if i in self.versionfuncs:
                    self.versionfuncs[i]()
                else:
                    self.api('send.msg')(
                        '%s: no function to upgrade to version %s' %
                        (self.sname, i),
                        secondary='upgrade')

        self.settingvalues.sync()

    def _cmd_inspect(self, args):  # pylint: disable=too-many-branches
        """
    show the plugin as it currently is in memory
    """
        from libs.objectdump import dumps as dumper

        tmsg = []
        if args['method']:
            try:
                tmeth = getattr(self, args['method'])
                tmsg.append(inspect.getsource(tmeth))
            except AttributeError:
                tmsg.append('There is no method named %s' % args['method'])

        elif args['object']:
            tobj = args['object']
            key = None
            if ':' in tobj:
                tobj, key = tobj.split(':')

            obj = getattr(self, tobj)
            if obj:
                if key:
                    if key not in obj:
                        try:
                            key = int(key)
                        except ValueError:
                            pass
                    if key in obj:
                        obj = obj[key]
                if args['simple']:
                    tvars = pprint.pformat(obj)
                else:
                    tvars = dumper(obj)
                tmsg.append(tvars)

        else:
            if args['simple']:
                tvars = pprint.pformat(vars(self))
            else:
                tvars = dumper(self)

            tmsg.append('@M' + '-' * 60 + '@x')
            tmsg.append('Variables')
            tmsg.append('@M' + '-' * 60 + '@x')
            tmsg.append(tvars)
            tmsg.append('@M' + '-' * 60 + '@x')
            tmsg.append('Methods')
            tmsg.append('@M' + '-' * 60 + '@x')
            tmsg.append(
                pprint.pformat(inspect.getmembers(self, inspect.ismethod)))

        return True, tmsg

    def _cmd_api(self, args):
        """
    list functions in the api for a plugin
    """
        tmsg = []
        if args['api']:
            tmsg.extend(
                self.api('api.detail')("%s.%s" % (self.sname, args['api'])))
        else:
            apilist = self.api('api.list')(self.sname)
            if not apilist:
                tmsg.append('nothing in the api')
            else:
                tmsg.extend(apilist)

        return True, tmsg

    def __afterload(self, args):  # pylint: disable=unused-argument
        """
    do something after the load function is run
    """
        # go through each variable and raise var_%s_changed
        self.settingvalues.raiseall()

        mud = self.api('managers.getm')('mud')

        if mud and mud.connected:
            if self.api('api.has')('connect.firstactive'):
                if self.api('connect.firstactive')():
                    self.afterfirstactive()
            else:
                self.api('events.register')('firstactive',
                                            self.afterfirstactive,
                                            prio=self.firstactiveprio)
        else:
            self.api('events.register')('firstactive',
                                        self.afterfirstactive,
                                        prio=self.firstactiveprio)

    def __disconnect(self, args=None):  # pylint: disable=unused-argument
        """
    re-register to firstactive on disconnect
    """
        self.api('send.msg')('baseplugin, disconnect')
        self.api('events.register')('firstactive', self.afterfirstactive)

    def afterfirstactive(self, args=None):  # pylint: disable=unused-argument
        """
    if we are connected do
    """
        self.api('send.msg')('baseplugin, firstactive')
        if self.api('events.isregistered')('firstactive',
                                           self.afterfirstactive):
            self.api('events.unregister')('firstactive', self.afterfirstactive)

    # get the vaule of a setting
    def _api_settinggets(self, setting):
        """  get the value of a setting
    @Ysetting@w = the setting value to get

    this function returns the value of the setting, None if not found"""
        try:
            return self.api('utils.verify')(self.settingvalues[setting],
                                            self.settings[setting]['stype'])
        except KeyError:
            return None

    # add a plugin dependency
    def _api_dependencyadd(self, dependency):
        """  add a depencency
    @Ydependency@w    = the name of the plugin that will be a dependency

    this function returns no values"""
        if dependency not in self.dependencies:
            self.dependencies.append(dependency)

    # change the value of a setting
    def _api_settingchange(self, setting, value):
        """  change a setting
    @Ysetting@w    = the name of the setting to change
    @Yvalue@w      = the value to set it as

    this function returns True if the value was changed, False otherwise"""
        if value == 'default':
            value = self.settings[setting]['default']
        if setting in self.settings:
            self.settingvalues[setting] = self.api('utils.verify')(
                value, self.settings[setting]['stype'])
            self.settingvalues.sync()
            return True

        return False

    def getstats(self):
        """
    get the stats for the plugin
    """
        stats = {}
        stats['Base Sizes'] = {}
        stats['Base Sizes']['showorder'] = ['Class', 'Variables', 'Api']
        stats['Base Sizes']['Variables'] = '%s bytes' % \
                                          sys.getsizeof(self.settingvalues)
        stats['Base Sizes']['Class'] = '%s bytes' % sys.getsizeof(self)
        stats['Base Sizes']['Api'] = '%s bytes' % sys.getsizeof(self.api)

        return stats

    def _cmd_stats(self, args=None):  # pylint: disable=unused-argument
        """
    @G%(name)s@w - @B%(cmdname)s@w
    show stats, memory, profile, etc.. for this plugin
    @CUsage@w: stats
    """
        stats = self.getstats()
        tmsg = []
        for header in stats:
            tmsg.append(self.api('utils.center')(header, '=', 60))
            for subtype in stats[header]['showorder']:
                tmsg.append('%-20s : %s' % (subtype, stats[header][subtype]))

        return True, tmsg

    def unload(self, _=None):
        """
    unload stuff
    """
        self.api('send.msg')('unloading %s' % self.name)

        # remove anything out of the api
        self.api('api.remove')(self.sname)

        #save the state
        self.savestate()

    def __savestate(self, _=None):
        """
    save the settings state
    """
        self.settingvalues.sync()

    def savestate(self, _=None):
        """
    save all settings for the plugin
    do not overload!

    attach to the plugin_<pluginname>_savestate event
    """
        self.api('events.eraise')('plugin_%s_savestate' % self.sname)

    def _cmd_set(self, args):
        """
    @G%(name)s@w - @B%(cmdname)s@w
    List or set vars
    @CUsage@w: var @Y<varname>@w @Y<varvalue>@w
      @Ysettingname@w    = The setting to set
      @Ysettingvalue@w   = The value to set it to
      if there are no arguments or 'list' is the first argument then
      it will list the settings for the plugin
    """
        msg = []
        if args['name'] == 'list':
            return True, self._listvars()
        elif args['name'] and args['value']:
            var = args['name']
            val = args['value']
            if var in self.settings:
                if 'readonly' in self.settings[var] \
                      and self.settings[var]['readonly']:
                    return True, ['%s is a readonly setting' % var]
                else:
                    try:
                        self.api('setting.change')(var, val)
                        tvar = self.settingvalues[var]
                        if self.settings[var]['nocolor']:
                            tvar = tvar.replace('@', '@@')
                        elif self.settings[var]['stype'] == 'color':
                            tvar = '%s%s@w' % (val, val.replace('@', '@@'))
                        elif self.settings[var]['stype'] == 'timelength':
                            tvar = self.api('utils.formattime')(
                                self.api('utils.verify')(val, 'timelength'))
                        return True, ['%s is now set to %s' % (var, tvar)]
                    except ValueError:
                        msg = ['Cannot convert %s to %s' % \
                                          (val, self.settings[var]['stype'])]
                        return True, msg
                return True, self._listvars()
            else:
                msg = ['plugin setting %s does not exist' % var]
        return False, msg

    def _cmd_save(self, args):  # pylint: disable=unused-argument
        """
    @G%(name)s@w - @B%(cmdname)s@w
    save plugin state
    @CUsage@w: save
    """
        self.savestate()
        return True, ['Plugin settings saved']

    def _cmd_help(self, args):
        """
    test command
    """
        msg = []

        if '.__init__' in self.fullimploc:
            imploc = self.fullimploc.replace('.__init__', '')
        else:
            imploc = self.fullimploc

        msg.extend(sys.modules[imploc].__doc__.split('\n'))
        if args['commands']:
            cmdlist = self.api('commands.list')(self.sname)
            if cmdlist:
                msg.extend(self.api('commands.list')(self.sname))
                msg.append('@G' + '-' * 60 + '@w')
                msg.append('')
        if args['api']:
            apilist = self.api('api.list')(self.sname)
            if apilist:
                msg.append('API functions in %s' % self.sname)
                msg.append('@G' + '-' * 60 + '@w')
                msg.extend(self.api('api.list')(self.sname))
        return True, msg

    def _listvars(self):
        """
    return a list of strings that list all settings
    """
        tmsg = []
        if not self.settingvalues:
            tmsg.append('There are no settings defined')
        else:
            tform = '%-15s : %-15s - %s'
            for i in self.settings:
                val = self.settingvalues[i]
                if 'nocolor' in self.settings[i] and self.settings[i][
                        'nocolor']:
                    val = val.replace('@', '@@')
                elif self.settings[i]['stype'] == 'color':
                    val = '%s%s@w' % (val, val.replace('@', '@@'))
                elif self.settings[i]['stype'] == 'timelength':
                    val = self.api('utils.formattime')(
                        self.api('utils.verify')(val, 'timelength'))
                tmsg.append(tform % (i, val, self.settings[i]['help']))
        return tmsg

    # add a setting to the plugin
    def _api_settingadd(self, name, default, stype, shelp, **kwargs):
        """  remove a command
    @Yname@w     = the name of the setting
    @Ydefault@w  = the default value of the setting
    @Ystype@w    = the type of the setting
    @Yshelp@w    = the help associated with the setting
    Keyword Arguments
      @Ynocolor@w    = if True, don't parse colors when showing value
      @Yreadonly@w   = if True, can't be changed by a client

    this function returns no values"""

        if 'nocolor' in kwargs:
            nocolor = kwargs['nocolor']
        else:
            nocolor = False
        if 'readonly' in kwargs:
            readonly = kwargs['readonly']
        else:
            readonly = False
        if name not in self.settingvalues:
            self.settingvalues[name] = default
        self.settings[name] = {
            'default': default,
            'help': shelp,
            'stype': stype,
            'nocolor': nocolor,
            'readonly': readonly
        }

    def _cmd_reset(self, _=None):
        """
    @G%(name)s@w - @B%(cmdname)s@w
      reset the plugin
      @CUsage@w: reset
    """
        if self.canreset:
            self.reset()
            return True, ['Plugin reset']

        return True, ['This plugin cannot be reset']

    def ischangedondisk(self):
        """
    check to see if the file this plugin is based on has changed on disk
    """
        ftime = os.path.getmtime(self.pluginfile)
        if ftime > self.loadedtime:
            return True

        return False

    def reset(self):
        """
    internal function to reset data
    """
        if self.canreset:
            self.resetflag = True
            self.settingvalues.clear()
            for i in self.settings:
                self.settingvalues[i] = self.settings[i]['default']
            self.settingvalues.sync()
            self.resetflag = False

    # add a function to the api
    def _api_add(self, name, func):
        """
    add a command to the api
    """
        # we call the non overloaded versions
        self.api.add(self.sname, name, func)
Exemplo n.º 2
0
        if tcommand[-1] != '\n':
          tcommand = tcommand + '\n'
        API('send.msg')('sending %s to the mud' % tcommand.strip(),
                        primary='inputparse')
        API('events.eraise')('to_mud_event',
                             {'data':tcommand,
                              'dtype':'fromclient',
                              'history':history})

# send data directly to the mud
def api_tomud(data):
  """ send data directly to the mud

  This does not go through the interpreter
    @Ydata@w     = the data to send

  this function returns no values
  """
  if data[-1] != '\n':
    data = data + '\n'
  API('events.eraise')('to_mud_event',
                       {'data':data,
                        'dtype':'fromclient'})

API.add('send', 'msg', api_msg)
API.add('send', 'error', api_error)
API.add('send', 'traceback', api_traceback)
API.add('send', 'client', api_client)
API.add('send', 'mud', api_tomud)
API.add('send', 'execute', api_execute)
Exemplo n.º 3
0
                    tcommand = tcommand + '\n'
                API('send.msg')('sending %s to the mud' % tcommand.strip(),
                                primary='inputparse')
                API('events.eraise')('to_mud_event', {
                    'data': tcommand,
                    'dtype': 'fromclient',
                    'history': history
                })


# send data directly to the mud
def api_tomud(data):
    """ send data directly to the mud

  This does not go through the interpreter
    @Ydata@w     = the data to send

  this function returns no values
  """
    if data[-1] != '\n':
        data = data + '\n'
    API('events.eraise')('to_mud_event', {'data': data, 'dtype': 'fromclient'})


API.add('send', 'msg', api_msg)
API.add('send', 'error', api_error)
API.add('send', 'traceback', api_traceback)
API.add('send', 'client', api_client)
API.add('send', 'mud', api_tomud)
API.add('send', 'execute', api_execute)
Exemplo n.º 4
0
class BasePlugin(object):
  # pylint: disable=too-many-public-methods,too-many-instance-attributes
  """
  a base class for plugins
  """
  def __init__(self, name, sname, modpath, basepath, fullimploc):
    # pylint: disable=too-many-arguments
    """
    initialize the instance
    The following things should not be done in __init__ in a plugin
      Interacting with anything in the api except api.add, api.overload
          or dependency.add
    """
    self.author = ''
    self.purpose = ''
    self.version = 0
    self.priority = 100
    self.name = name
    self.sname = sname
    self.dependencies = []
    self.versionfuncs = {}
    self.reloaddependents = False
    self.canreload = True
    self.resetflag = True
    self.api = API()
    self.loadedtime = time.time()
    self.savedir = os.path.join(self.api.BASEPATH, 'data',
                                'plugins', self.sname)
    try:
      os.makedirs(self.savedir)
    except OSError:
      pass
    self.savefile = os.path.join(self.api.BASEPATH, 'data',
                                 'plugins', self.sname, 'settingvalues.txt')
    self.modpath = modpath
    self.basepath = basepath
    self.fullimploc = fullimploc
    self.pluginfile = os.path.join(basepath, modpath[1:])
    self.pluginlocation = os.path.normpath(
        os.path.join(self.api.BASEPATH, 'plugins') + \
          os.sep + os.path.dirname(self.modpath))

    self.package = fullimploc.split('.')[1]

    self.settings = {}
    self.settingvalues = PersistentDictEvent(self, self.savefile, 'c')

    self._dump_shallow_attrs = ['api']

    self.api.overload('dependency', 'add', self.api_dependencyadd)
    self.api.overload('setting', 'add', self.api_settingadd)
    self.api.overload('setting', 'gets', self.api_settinggets)
    self.api.overload('setting', 'change', self.api_settingchange)
    self.api.overload('api', 'add', self.api_add)

  def load(self):
    """
    load stuff, do most things here
    """
    self.settingvalues.pload()

    if '_version' in self.settingvalues and \
        self.settingvalues['_version'] != self.version:
      self.updateversion(self.settingvalues['_version'], self.version)

    self.api.get('log.adddtype')(self.sname)

    parser = argparse.ArgumentParser(
        add_help=False,
        formatter_class=argparse.RawDescriptionHelpFormatter,
        description=textwrap.dedent("""
          change a setting in the plugin

          if there are no arguments or 'list' is the first argument then
          it will list the settings for the plugin"""))
    parser.add_argument('name',
                        help='the setting name',
                        default='list',
                        nargs='?')
    parser.add_argument('value',
                        help='the new value of the setting',
                        default='',
                        nargs='?')
    self.api.get('commands.add')('set',
                                 self.cmd_set,
                                 parser=parser,
                                 group='Base',
                                 history=False)

    parser = argparse.ArgumentParser(add_help=False,
                                     description='reset the plugin')
    self.api.get('commands.add')('reset',
                                 self.cmd_reset,
                                 parser=parser,
                                 group='Base')

    parser = argparse.ArgumentParser(add_help=False,
                                     description='save the plugin state')
    self.api.get('commands.add')('save',
                                 self.cmd_save,
                                 parser=parser,
                                 group='Base')

    parser = argparse.ArgumentParser(add_help=False,
                                     description='show plugin stats')
    self.api.get('commands.add')('stats',
                                 self.cmd_stats,
                                 parser=parser,
                                 group='Base')

    parser = argparse.ArgumentParser(add_help=False,
                                     description='inspect a plugin')
    parser.add_argument('-m',
                        "--method",
                        help="get code for a method",
                        default='')
    parser.add_argument('-o',
                        "--object",
                        help="show an object of the plugin, can be method or variable",
                        default='')
    parser.add_argument('-s',
                        "--simple",
                        help="show a simple output",
                        action="store_true")
    self.api.get('commands.add')('inspect',
                                 self.cmd_inspect,
                                 parser=parser,
                                 group='Base')

    parser = argparse.ArgumentParser(add_help=False,
                                     description='show help info for this plugin')
    parser.add_argument('-a',
                        "--api",
                        help="show functions this plugin has in the api",
                        action="store_true")
    parser.add_argument('-c',
                        "--commands",
                        help="show commands in this plugin",
                        action="store_true")
    self.api.get('commands.add')('help',
                                 self.cmd_help,
                                 parser=parser,
                                 group='Base')

    parser = argparse.ArgumentParser(add_help=False,
                                     description='list functions in the api')
    parser.add_argument('api',
                        help='api to get details of',
                        default='',
                        nargs='?')
    self.api.get('commands.add')('api',
                                 self.cmd_api,
                                 parser=parser,
                                 group='Base')

    self.api.get('events.register')('%s_plugin_loaded' % self.sname,
                                    self.afterload)

    self.api.get('events.register')('muddisconnect', self.disconnect)

    self.resetflag = False

  def updateversion(self, oldversion, newversion):
    """
    update plugin data
    """
    if oldversion != newversion and newversion > oldversion:
      for i in range(oldversion + 1, newversion + 1):
        self.api.get('send.msg')(
            '%s: upgrading to version %s' % (self.sname, i),
            secondary='upgrade')
        if i in self.versionfuncs:
          self.versionfuncs[i]()
        else:
          self.api.get('send.msg')(
              '%s: no function to upgrade to version %s' % (self.sname, i),
              secondary='upgrade')

    self.settingvalues.sync()

  def cmd_inspect(self, args):
    # pylint: disable=too-many-branches
    """
    show the plugin as it currently is in memory
    """
    from libs.objectdump import dumps as dumper

    tmsg = []
    if args['method']:
      try:
        tmeth = getattr(self, args['method'])
        tmsg.append(inspect.getsource(tmeth))
      except AttributeError:
        tmsg.append('There is no method named %s' % args['method'])

    elif args['object']:
      tobj = args['object']
      key = None
      if ':' in tobj:
        tobj, key = tobj.split(':')

      obj = getattr(self, tobj)
      if obj:
        if key:
          if key not in obj:
            try:
              key = int(key)
            except ValueError:
              pass
          if key in obj:
            obj = obj[key]
        if args['simple']:
          tvars = pprint.pformat(obj)
        else:
          tvars = dumper(obj)
        tmsg.append(tvars)

    else:
      if args['simple']:
        tvars = pprint.pformat(vars(self))
      else:
        tvars = dumper(self)

      tmsg.append('@M' + '-' * 60 + '@x')
      tmsg.append('Variables')
      tmsg.append('@M' + '-' * 60 + '@x')
      tmsg.append(tvars)
      tmsg.append('@M' + '-' * 60 + '@x')
      tmsg.append('Methods')
      tmsg.append('@M' + '-' * 60 + '@x')
      tmsg.append(pprint.pformat(inspect.getmembers(self, inspect.ismethod)))

    return True, tmsg

  def cmd_api(self, args):
    """
    list functions in the api for a plugin
    """
    tmsg = []
    if args['api']:
      tmsg.extend(self.api.get('api.detail')("%s.%s" % (self.sname,
                                                        args['api'])))
    else:
      apilist = self.api.get('api.list')(self.sname)
      if not apilist:
        tmsg.append('nothing in the api')
      else:
        tmsg.extend(apilist)

    return True, tmsg

  def afterload(self, args):
    # pylint: disable=unused-argument
    """
    do something after the load function is run
    """
    proxy = self.api.get('managers.getm')('proxy')

    if proxy and proxy.connected:
      if self.api.get('api.has')('connect.firstactive'):
        if self.api.get('connect.firstactive')():
          self.afterfirstactive()
      else:
        self.api.get('events.register')('firstactive', self.afterfirstactive)
    else:
      self.api.get('events.register')('firstactive', self.afterfirstactive)

  def disconnect(self, args=None):
    # pylint: disable=unused-argument
    """
    re-register to firstactive on disconnect
    """
    self.api.get('send.msg')('baseplugin, disconnect')
    self.api.get('events.register')('firstactive', self.afterfirstactive)

  # get the vaule of a setting
  def api_settinggets(self, setting):
    """  get the value of a setting
    @Ysetting@w = the setting value to get

    this function returns the value of the setting, None if not found"""
    try:
      return self.api.get('utils.verify')(self.settingvalues[setting],
                                          self.settings[setting]['stype'])
    except KeyError:
      return None

  # add a plugin dependency
  def api_dependencyadd(self, dependency):
    """  add a depencency
    @Ydependency@w    = the name of the plugin that will be a dependency

    this function returns no values"""
    if dependency not in self.dependencies:
      self.dependencies.append(dependency)

  # change the value of a setting
  def api_settingchange(self, setting, value):
    """  change a setting
    @Ysetting@w    = the name of the setting to change
    @Yvalue@w      = the value to set it as

    this function returns True if the value was changed, False otherwise"""
    if value == 'default':
      value = self.settings[setting]['default']
    if setting in self.settings:
      self.settingvalues[setting] = self.api.get('utils.verify')(
          value,
          self.settings[setting]['stype'])
      self.settingvalues.sync()
      return True

    return False

  def getstats(self):
    """
    get the stats for the plugin
    """
    stats = {}
    stats['Base Sizes'] = {}
    stats['Base Sizes']['showorder'] = ['Class', 'Variables', 'Api']
    stats['Base Sizes']['Variables'] = '%s bytes' % \
                                      sys.getsizeof(self.settingvalues)
    stats['Base Sizes']['Class'] = '%s bytes' % sys.getsizeof(self)
    stats['Base Sizes']['Api'] = '%s bytes' % sys.getsizeof(self.api)

    return stats

  def cmd_stats(self, args=None):
    # pylint: disable=unused-argument
    """
    @G%(name)s@w - @B%(cmdname)s@w
    show stats, memory, profile, etc.. for this plugin
    @CUsage@w: stats
    """
    stats = self.getstats()
    tmsg = []
    for header in stats:
      tmsg.append(self.api.get('utils.center')(header, '=', 60))
      for subtype in stats[header]['showorder']:
        tmsg.append('%-20s : %s' % (subtype, stats[header][subtype]))

    return True, tmsg

  def unload(self, _=None):
    """
    unload stuff
    """
    self.api.get('send.msg')('unloading %s' % self.name)

    # remove anything out of the api
    self.api.get('api.remove')(self.sname)

    #save the state
    self.savestate()

  def savestate(self):
    """
    save the state
    """
    self.settingvalues.sync()

  def cmd_set(self, args):
    """
    @G%(name)s@w - @B%(cmdname)s@w
    List or set vars
    @CUsage@w: var @Y<varname>@w @Y<varvalue>@w
      @Ysettingname@w    = The setting to set
      @Ysettingvalue@w   = The value to set it to
      if there are no arguments or 'list' is the first argument then
      it will list the settings for the plugin
    """
    msg = []
    if args['name'] == 'list':
      return True, self.listvars()
    elif args['name'] and args['value']:
      var = args['name']
      val = args['value']
      if var in self.settings:
        if 'readonly' in self.settings[var] \
              and self.settings[var]['readonly']:
          return True, ['%s is a readonly setting' % var]
        else:
          try:
            self.api.get('setting.change')(var, val)
            tvar = self.settingvalues[var]
            if self.settings[var]['nocolor']:
              tvar = tvar.replace('@', '@@')
            elif self.settings[var]['stype'] == 'color':
              tvar = '%s%s@w' % (val, val.replace('@', '@@'))
            elif self.settings[var]['stype'] == 'timelength':
              tvar = self.api.get('utils.formattime')(
                  self.api.get('utils.verify')(val, 'timelength'))
            return True, ['%s is now set to %s' % (var, tvar)]
          except ValueError:
            msg = ['Cannot convert %s to %s' % \
                              (val, self.settings[var]['stype'])]
            return True, msg
        return True, self.listvars()
      else:
        msg = ['plugin setting %s does not exist' % var]
    return False, msg

  def cmd_save(self, args):
    # pylint: disable=unused-argument
    """
    @G%(name)s@w - @B%(cmdname)s@w
    save plugin state
    @CUsage@w: save
    """
    self.savestate()
    return True, ['Plugin settings saved']

  def cmd_help(self, args):
    """
    test command
    """
    msg = []

    msg.extend(sys.modules[self.fullimploc].__doc__.split('\n'))
    if args['commands']:
      cmdlist = self.api.get('commands.list')(self.sname)
      if cmdlist:
        msg.extend(self.api.get('commands.list')(self.sname))
        msg.append('@G' + '-' * 60 + '@w')
        msg.append('')
    if args['api']:
      apilist = self.api.get('api.list')(self.sname)
      if apilist:
        msg.append('API functions in %s' % self.sname)
        msg.append('@G' + '-' * 60 + '@w')
        msg.extend(self.api.get('api.list')(self.sname))
    return True, msg

  def listvars(self):
    """
    return a list of strings that list all settings
    """
    tmsg = []
    if len(self.settingvalues) == 0:
      tmsg.append('There are no settings defined')
    else:
      tform = '%-15s : %-15s - %s'
      for i in self.settings:
        val = self.settingvalues[i]
        if 'nocolor' in self.settings[i] and self.settings[i]['nocolor']:
          val = val.replace('@', '@@')
        elif self.settings[i]['stype'] == 'color':
          val = '%s%s@w' % (val, val.replace('@', '@@'))
        elif self.settings[i]['stype'] == 'timelength':
          val = self.api.get('utils.formattime')(
              self.api.get('utils.verify')(val, 'timelength'))
        tmsg.append(tform % (i, val, self.settings[i]['help']))
    return tmsg

  # add a setting to the plugin
  def api_settingadd(self, name, default, stype, shelp, **kwargs):
    """  remove a command
    @Yname@w     = the name of the setting
    @Ydefault@w  = the default value of the setting
    @Ystype@w    = the type of the setting
    @Yshelp@w    = the help associated with the setting
    Keyword Arguments
      @Ynocolor@w    = if True, don't parse colors when showing value
      @Yreadonly@w   = if True, can't be changed by a client

    this function returns no values"""

    if 'nocolor' in kwargs:
      nocolor = kwargs['nocolor']
    else:
      nocolor = False
    if 'readonly' in kwargs:
      readonly = kwargs['readonly']
    else:
      readonly = False
    if name not in self.settingvalues:
      self.settingvalues[name] = default
    self.settings[name] = {
        'default':default,
        'help':shelp,
        'stype':stype,
        'nocolor':nocolor,
        'readonly':readonly
    }

  def cmd_reset(self, _=None):
    """
    @G%(name)s@w - @B%(cmdname)s@w
      reset the plugin
      @CUsage@w: reset
    """
    self.reset()
    return True, ['Plugin reset']

  def ischangedondisk(self):
    """
    check to see if the file this plugin is based on has changed on disk
    """
    ftime = os.path.getmtime(self.pluginfile)
    if ftime > self.loadedtime:
      return True

    return False

  def reset(self):
    """
    internal function to reset data
    """
    self.resetflag = True
    self.settingvalues.clear()
    for i in self.settings:
      self.settingvalues[i] = self.settings[i]['default']
    self.settingvalues.sync()
    self.resetflag = False

  def afterfirstactive(self, _=None):
    """
    if we are connected do
    """
    self.api.get('send.msg')('baseplugin, firstactive')
    self.api.get('events.unregister')('firstactive', self.afterfirstactive)

  # add a function to the api
  def api_add(self, name, func):
    """
    add a command to the api
    """
    # we call the non overloaded versions
    self.api.add(self.sname, name, func)
Exemplo n.º 5
0
class PluginMgr(object):
    # pylint: disable=too-many-public-methods
    """
  a class to manage plugins
  """
    def __init__(self):
        """
    initialize the instance
    """
        self.plugins = {}
        self.pluginl = {}
        self.pluginm = {}
        self.pluginp = {}
        self.options = {}
        self.plugininfo = {}

        index = __file__.rfind(os.sep)
        if index == -1:
            self.basepath = "." + os.sep
        else:
            self.basepath = __file__[:index]

        self.api = API()
        self.savefile = os.path.join(self.api.BASEPATH, 'data', 'plugins',
                                     'loadedplugins.txt')
        self.loadedplugins = PersistentDict(self.savefile, 'c')
        self.sname = 'plugins'
        self.lname = 'Plugin Manager'

        self.api.add(self.sname, 'isinstalled', self.api_isinstalled)
        self.api.add(self.sname, 'getp', self.api_getp)
        self.api.add(self.sname, 'module', self.api_getmodule)
        self.api.add(self.sname, 'allplugininfo', self.api_allplugininfo)
        self.api.add(self.sname, 'savestate', self.savestate)

    # return the dictionary of all plugins
    def api_allplugininfo(self):
        """
    return the plugininfo dictionary
    """
        return self.plugininfo

    def findplugin(self, name):
        """
    find a plugin file
    """
        if '.' in name:
            tlist = name.split('.')
            name = tlist[-1]
            del tlist[-1]
            npath = os.sep.join(tlist)

        _module_list = find_files(self.basepath, name + ".py")

        if len(_module_list) == 1:
            return _module_list[0], self.basepath
        else:
            for i in _module_list:
                if npath in i:
                    return i, self.basepath

        return '', ''

    def findloadedplugin(self, plugin):
        """
    find a plugin
    """
        if plugin and plugin in self.plugins:
            return plugin

        fullimploc = 'plugins.' + plugin
        for tplugin in self.plugins:
            if self.plugins[tplugin].fullimploc == fullimploc:
                return tplugin

        return None

    # get a plugin instance
    def api_getmodule(self, pluginname):
        """  returns the module of a plugin
    @Ypluginname@w  = the plugin to check for"""
        if pluginname in self.pluginm:
            return self.pluginm[pluginname]

        return None

    # get a plugin instance
    def api_getp(self, pluginname):
        """  get a plugin instance
    @Ypluginname@w  = the plugin to get for"""

        if isinstance(pluginname, basestring):
            if pluginname in self.plugins:
                return self.plugins[pluginname]
            if pluginname in self.pluginl:
                return self.pluginl[pluginname]
            if pluginname in self.pluginm:
                return self.pluginm[pluginname]
            if pluginname in self.pluginp:
                return self.pluginp[pluginname]
        elif isinstance(pluginname, BasePlugin):
            return pluginname

        return None

    # check if a plugin is installed
    def api_isinstalled(self, pluginname):
        """  check if a plugin is installed
    @Ypluginname@w  = the plugin to check for"""
        if pluginname in self.plugins or pluginname in self.pluginl:
            return True
        return False

    def loaddependencies(self, pluginname, dependencies):
        """
    load a list of modules
    """
        for i in dependencies:
            if i in self.plugins or i in self.pluginl:
                continue

            self.api.get('send.msg')('%s: loading dependency %s' %
                                     (pluginname, i), pluginname)

            name, path = self.findplugin(i)
            if name:
                modpath = name.replace(path, '')
                self.load_module(modpath, path, force=True)

    def getnotloadedplugins(self):
        """
    create a message of all not loaded plugins
    """
        msg = []
        badplugins = self.updateallplugininfo()
        for modpath in sorted(self.plugininfo.keys()):
            sname = self.plugininfo[modpath]['sname']
            fullimploc = self.plugininfo[modpath]['fullimploc']
            if sname not in self.plugins:
                msg.append("%-20s : %-25s %-10s %-5s %s@w" % \
                          (fullimploc.replace('plugins.', ''),
                           self.plugininfo[modpath]['name'],
                           self.plugininfo[modpath]['author'],
                           self.plugininfo[modpath]['version'],
                           self.plugininfo[modpath]['purpose']))
        if len(msg) > 0:
            msg.insert(0, '-' * 75)
            msg.insert(0, "%-20s : %-25s %-10s %-5s %s@w" % \
                                ('Location', 'Name', 'Author', 'Vers', 'Purpose'))
            msg.insert(0, 'The following plugins are not loaded')

        if badplugins:
            msg.append('')
            msg.append('The following files would not import')
            for bad in badplugins:
                msg.append(bad.replace('plugins.', ''))

        return msg

    def getchangedplugins(self):
        """
    create a message of plugins that are changed on disk
    """
        msg = []

        plugins = sorted(self.plugins.values(),
                         key=operator.attrgetter('package'))
        packageheader = []

        msg.append("%-10s : %-25s %-10s %-5s %s@w" % \
                            ('Short Name', 'Name', 'Author', 'Vers', 'Purpose'))
        msg.append('-' * 75)
        for tpl in plugins:
            if tpl.ischangedondisk():
                if tpl.package not in packageheader:
                    if len(packageheader) > 0:
                        msg.append('')
                    packageheader.append(tpl.package)
                    limp = 'plugins.%s' % tpl.package
                    mod = __import__(limp)
                    try:
                        desc = getattr(mod, tpl.package).DESCRIPTION
                    except AttributeError:
                        desc = ''
                    msg.append('@GPackage: %s%s@w' % \
                            (tpl.package, ' - ' + desc if desc else ''))
                    msg.append('@G' + '-' * 75 + '@w')
                msg.append("%-10s : %-25s %-10s %-5s %s@w" % \
                          (tpl.sname, tpl.name,
                           tpl.author, tpl.version, tpl.purpose))

        return msg

    def getpackageplugins(self, package):
        """
    create a message of plugins in a package
    """
        msg = []

        plist = []
        for plugin in self.plugins.values():
            if plugin.package == package:
                plist.append(plugin)

        if len(plist) > 0:
            plugins = sorted(plist, key=operator.attrgetter('sname'))
            limp = 'plugins.%s' % package
            mod = __import__(limp)
            try:
                desc = getattr(mod, package).DESCRIPTION
            except AttributeError:
                desc = ''
            msg.append('@GPackage: %s%s@w' % \
                  (package, ' - ' + desc if desc else ''))
            msg.append('@G' + '-' * 75 + '@w')
            msg.append("%-10s : %-25s %-10s %-5s %s@w" % \
                                ('Short Name', 'Name',
                                 'Author', 'Vers', 'Purpose'))
            msg.append('-' * 75)

            for tpl in plugins:
                msg.append("%-10s : %-25s %-10s %-5s %s@w" % \
                          (tpl.sname, tpl.name,
                           tpl.author, tpl.version, tpl.purpose))
        else:
            msg.append('That is not a valid package')

        return msg

    def getallplugins(self):
        """
    create a message of all plugins
    """
        msg = []

        plugins = sorted(self.plugins.values(),
                         key=operator.attrgetter('package'))
        packageheader = []
        msg.append("%-10s : %-25s %-10s %-5s %s@w" % \
                            ('Short Name', 'Name', 'Author', 'Vers', 'Purpose'))
        msg.append('-' * 75)
        for tpl in plugins:
            if tpl.package not in packageheader:
                if len(packageheader) > 0:
                    msg.append('')
                packageheader.append(tpl.package)
                limp = 'plugins.%s' % tpl.package
                mod = __import__(limp)
                try:
                    desc = getattr(mod, tpl.package).DESCRIPTION
                except AttributeError:
                    desc = ''
                msg.append('@GPackage: %s%s@w' % \
                    (tpl.package, ' - ' + desc if desc else ''))
                msg.append('@G' + '-' * 75 + '@w')
            msg.append("%-10s : %-25s %-10s %-5s %s@w" % \
                        (tpl.sname, tpl.name,
                         tpl.author, tpl.version, tpl.purpose))
        return msg

    def cmd_list(self, args):
        """
    @G%(name)s@w - @B%(cmdname)s@w
      List plugins
      @CUsage@w: list
    """
        msg = []

        if args['notloaded']:
            msg.extend(self.getnotloadedplugins())
        elif args['changed']:
            msg.extend(self.getchangedplugins())
        elif args['package']:
            msg.extend(self.getpackageplugins(args['package']))
        else:
            msg.extend(self.getallplugins())
        return True, msg

    def cmd_load(self, args):
        """
    @G%(name)s@w - @B%(cmdname)s@w
      Load a plugin
      @CUsage@w: load @Yplugin@w
        @Yplugin@w    = the name of the plugin to load
               use the name without the .py
    """
        tmsg = []
        plugin = args['plugin']
        if plugin:

            fname = plugin.replace('.', os.sep)
            _module_list = find_files(self.basepath, fname + ".py")

            if len(_module_list) > 1:
                tmsg.append('There is more than one module that matches: %s' % \
                                                                      plugin)
            elif len(_module_list) == 0:
                tmsg.append('There are no modules that match: %s' % plugin)
            else:
                modpath = _module_list[0].replace(self.basepath, '')
                sname, reason = self.load_module(modpath, self.basepath, True)
                if sname:
                    if reason == 'already':
                        tmsg.append('Module %s is already loaded' % sname)
                    else:
                        tmsg.append('Load complete: %s - %s' % \
                                                      (sname, self.plugins[sname].name))
                else:
                    tmsg.append('Could not load: %s' % plugin)
            return True, tmsg
        else:
            return False, ['@Rplease specify a plugin@w']

    def cmd_unload(self, args):
        """
    @G%(name)s@w - @B%(cmdname)s@w
      unload a plugin
      @CUsage@w: unload @Yplugin@w
        @Yplugin@w    = the shortname of the plugin to load
    """
        tmsg = []
        plugina = args['plugin']

        if not plugina:
            return False, ['@Rplease specify a plugin@w']

        plugin = self.findloadedplugin(plugina)

        if plugin and plugin in self.plugins:
            if self.plugins[plugin].canreload:
                if self.unload_module(self.plugins[plugin].fullimploc):
                    tmsg.append("Unloaded: %s" % plugin)
                else:
                    tmsg.append("Could not unload:: %s" % plugin)
            else:
                tmsg.append("That plugin can not be unloaded")
            return True, tmsg
        elif plugin:
            tmsg.append('plugin %s does not exist' % plugin)
            return True, tmsg

        return False, ['@Rplease specify a plugin@w']

    def cmd_reload(self, args):
        """
    @G%(name)s@w - @B%(cmdname)s@w
      reload a plugin
      @CUsage@w: reload @Yplugin@w
        @Yplugin@w    = the shortname of the plugin to reload
    """
        tmsg = []
        plugina = args['plugin']

        if not plugina:
            return False, ['@Rplease specify a plugin@w']

        plugin = self.findloadedplugin(plugina)

        if plugin and plugin in self.plugins:
            if self.plugins[plugin].canreload:
                tret, _ = self.reload_module(plugin, True)
                if tret and tret != True:
                    tmsg.append("Reload complete: %s" %
                                self.plugins[tret].fullimploc)
                    return True, tmsg
            else:
                tmsg.append("That plugin cannot be reloaded")
                return True, tmsg
        else:
            tmsg.append('plugin %s does not exist' % plugin)
            return True, tmsg

        return False, tmsg

    def load_modules(self, tfilter):
        """
    load modules in all directories under plugins
    """
        _module_list = find_files(self.basepath, tfilter)
        _module_list.sort()

        load = False

        for fullpath in _module_list:
            modpath = fullpath.replace(self.basepath, '')
            force = False
            if modpath in self.loadedplugins:
                force = True
            modname, dummy = self.load_module(modpath,
                                              self.basepath,
                                              force=force,
                                              runload=load)

            if modname == 'log':
                self.api.get('log.adddtype')(self.sname)
                self.api.get('log.console')(self.sname)
                self.api.get('log.adddtype')('upgrade')
                self.api.get('log.console')('upgrade')

        if not load:
            testsort = sorted(self.plugins.values(),
                              key=operator.attrgetter('priority'))
            for i in testsort:
                try:
                    #check dependencies here
                    self.loadplugin(i)
                except Exception:  # pylint: disable=broad-except
                    self.api.get('send.traceback')(
                        "load: had problems running the load method for %s." \
                                    % i.fullimploc)
                    del sys.modules[i.fullimploc]

    def updateallplugininfo(self):
        """
    find plugins that are not in self.plugininfo
    """
        _module_list = find_files(self.basepath, '*.py')
        _module_list.sort()

        self.plugininfo = {}
        badplugins = []

        for fullpath in _module_list:
            modpath = fullpath.replace(self.basepath, '')

            imploc, modname = get_module_name(modpath)

            if not modname.startswith("_"):
                fullimploc = "plugins" + '.' + imploc
                if fullimploc in sys.modules:
                    self.plugininfo[modpath] = {}
                    self.plugininfo[modpath]['sname'] = self.pluginp[
                        modpath].sname
                    self.plugininfo[modpath]['name'] = self.pluginp[
                        modpath].name
                    self.plugininfo[modpath]['purpose'] = self.pluginp[
                        modpath].purpose
                    self.plugininfo[modpath]['author'] = self.pluginp[
                        modpath].author
                    self.plugininfo[modpath]['version'] = self.pluginp[
                        modpath].version
                    self.plugininfo[modpath]['modpath'] = modpath
                    self.plugininfo[modpath]['fullimploc'] = fullimploc

                else:
                    try:
                        _module = __import__(fullimploc)
                        _module = sys.modules[fullimploc]

                        self.plugininfo[modpath] = {}
                        self.plugininfo[modpath]['sname'] = _module.SNAME
                        self.plugininfo[modpath]['name'] = _module.NAME
                        self.plugininfo[modpath]['purpose'] = _module.PURPOSE
                        self.plugininfo[modpath]['author'] = _module.AUTHOR
                        self.plugininfo[modpath]['version'] = _module.VERSION
                        self.plugininfo[modpath]['modpath'] = modpath
                        self.plugininfo[modpath]['fullimploc'] = fullimploc

                        del sys.modules[fullimploc]

                    except Exception:  # pylint: disable=broad-except
                        badplugins.append(fullimploc)

        return badplugins

    def load_module(self, modpath, basepath, force=False, runload=True):
        # pylint: disable=too-many-branches
        """
    load a single module
    """
        if basepath in modpath:
            modpath = modpath.replace(basepath, '')

        imploc, modname = get_module_name(modpath)

        if modname.startswith("_"):
            return False, 'dev'

        try:
            fullimploc = "plugins" + '.' + imploc
            if fullimploc in sys.modules:
                return sys.modules[fullimploc].SNAME, 'already'

            self.api.get('send.msg')('importing %s' % fullimploc, self.sname)
            _module = __import__(fullimploc)
            _module = sys.modules[fullimploc]
            self.api.get('send.msg')('imported %s' % fullimploc, self.sname)
            load = True

            if 'AUTOLOAD' in _module.__dict__ and not force:
                if not _module.AUTOLOAD:
                    load = False
            elif 'AUTOLOAD' not in _module.__dict__:
                load = False

            if modpath not in self.plugininfo:
                self.plugininfo[modpath] = {}
                self.plugininfo[modpath]['sname'] = _module.SNAME
                self.plugininfo[modpath]['name'] = _module.NAME
                self.plugininfo[modpath]['purpose'] = _module.PURPOSE
                self.plugininfo[modpath]['author'] = _module.AUTHOR
                self.plugininfo[modpath]['version'] = _module.VERSION
                self.plugininfo[modpath]['modpath'] = modpath
                self.plugininfo[modpath]['fullimploc'] = fullimploc

            if load:
                if "Plugin" in _module.__dict__:
                    self.add_plugin(_module, modpath, basepath, fullimploc,
                                    runload)

                else:
                    self.api.get('send.msg')('Module %s has no Plugin class' % \
                                                        _module.NAME,
                                             self.sname)

                _module.__dict__["proxy_import"] = 1

                return _module.SNAME, 'Loaded'
            else:
                if fullimploc in sys.modules:
                    del sys.modules[fullimploc]
                self.api.get('send.msg')(
                    'Not loading %s (%s) because autoload is False' % \
                                            (_module.NAME, fullimploc),
                    self.sname)
            return True, 'not autoloaded'
        except Exception:  # pylint: disable=broad-except
            if fullimploc in sys.modules:
                del sys.modules[fullimploc]

            self.api.get('send.traceback')(
                "Module '%s' refuses to import/load." % fullimploc)
            return False, 'error'

    def unload_module(self, fullimploc):
        """
    unload a module
    """
        if fullimploc in sys.modules:

            _module = sys.modules[fullimploc]
            try:
                if "proxy_import" in _module.__dict__:
                    self.api.get('send.client')('unload: unloading %s' %
                                                fullimploc)
                    if "unload" in _module.__dict__:
                        try:
                            _module.unload()
                        except Exception:  # pylint: disable=broad-except
                            self.api.get('send.traceback')(
                                "unload: module %s didn't unload properly." %
                                fullimploc)

                    if not self.remove_plugin(_module.SNAME):
                        self.api.get('send.client')(
                            'could not remove plugin %s' % fullimploc)

                del sys.modules[fullimploc]
                self.api.get('send.client')("unload: unloaded %s." %
                                            fullimploc)

            except Exception:  # pylint: disable=broad-except
                self.api.get('send.traceback')(
                    "unload: had problems unloading %s." % fullimploc)
                return False

        return True

    def reload_module(self, modname, force=False):
        """
    reload a module
    """
        if modname in self.plugins:
            plugin = self.plugins[modname]
            fullimploc = plugin.fullimploc
            basepath = plugin.basepath
            modpath = plugin.modpath
            sname = plugin.sname
            try:
                reloaddependents = plugin.reloaddependents
            except Exception:  # pylint: disable=broad-except
                reloaddependents = False
            plugin = None
            if not self.unload_module(fullimploc):
                return False, ''

            if modpath and basepath:
                retval = self.load_module(modpath, basepath, force)
                if retval and reloaddependents:
                    self.reloaddependents(sname)
                return retval

        else:
            return False, ''

    def reloaddependents(self, reloadedplugin):
        """
    reload all dependents
    """
        testsort = sorted(self.plugins.values(),
                          key=operator.attrgetter('priority'))
        for plugin in testsort:
            if plugin.sname != reloadedplugin:
                if reloadedplugin in plugin.dependencies:
                    self.api.get('send.msg')('reloading dependent %s of %s' % \
                                (plugin.sname, reloadedplugin))
                    plugin.savestate()
                    self.reload_module(plugin.sname, True)

    def loadplugin(self, plugin):
        """
    check dependencies and run the load function
    """
        self.api.get('send.msg')('loading dependencies for %s' % \
                                      plugin.fullimploc,
                                 self.sname)
        self.loaddependencies(plugin.sname, plugin.dependencies)
        self.api.get('send.client')("load: loading %s with priority %s" % \
           (plugin.fullimploc, plugin.priority))
        self.api.get('send.msg')('loading %s (%s: %s)' % \
                  (plugin.fullimploc, plugin.sname, plugin.name),
                                 self.sname)
        plugin.load()
        self.api.get('send.client')("load: loaded %s" % plugin.fullimploc)
        self.api.get('send.msg')('loaded %s (%s: %s)' % \
                  (plugin.fullimploc, plugin.sname, plugin.name),
                                 self.sname)

        self.api.get('events.eraise')('%s_plugin_loaded' % plugin.sname, {})
        self.api.get('events.eraise')('plugin_loaded', {
            'plugin': plugin.sname
        })

    def add_plugin(self, module, modpath, basepath, fullimploc, load=True):
        # pylint: disable=too-many-arguments
        """
    add a plugin to be managed
    """
        module.__dict__["lyntin_import"] = 1
        plugin = module.Plugin(module.NAME, module.SNAME, modpath, basepath,
                               fullimploc)
        plugin.author = module.AUTHOR
        plugin.purpose = module.PURPOSE
        plugin.version = module.VERSION
        try:
            plugin.priority = module.PRIORITY
        except AttributeError:
            pass
        if plugin.name in self.pluginl:
            self.api.get('send.msg')('Plugin %s already exists' % plugin.name,
                                     self.sname)
            return False
        if plugin.sname in self.plugins:
            self.api.get('send.msg')('Plugin %s already exists' % plugin.sname,
                                     self.sname)
            return False

        if load:
            try:
                #check dependencies here
                self.loadplugin(plugin)
            except Exception:  # pylint: disable=broad-except
                self.api.get('send.traceback')(
                    "load: had problems running the load method for %s." \
                                                        % fullimploc)
                del sys.modules[fullimploc]
                return False
        self.pluginl[plugin.name] = plugin
        self.plugins[plugin.sname] = plugin
        self.pluginm[plugin.sname] = module
        self.pluginp[modpath] = plugin
        self.loadedplugins[modpath] = True
        self.loadedplugins.sync()

        return True

    def remove_plugin(self, pluginname):
        """
    remove a plugin
    """
        plugin = None
        if pluginname in self.plugins:
            plugin = self.plugins[pluginname]
            try:
                plugin.unload()
                self.api.get('events.eraise')('%s_plugin_unload' %
                                              plugin.sname, {})
                self.api.get('events.eraise')('plugin_unloaded', {
                    'name': plugin.sname
                })
                self.api.get('send.msg')('Plugin %s unloaded' % plugin.sname,
                                         self.sname, plugin.sname)
            except Exception:  # pylint: disable=broad-except
                self.api.get('send.traceback')(
                    "unload: had problems running the unload method for %s." \
                                          % plugin.sname)
                return False

            del self.plugins[plugin.sname]
            del self.pluginl[plugin.name]
            del self.pluginm[plugin.sname]
            del self.loadedplugins[plugin.modpath]
            self.loadedplugins.sync()

            plugin = None

            return True
        else:
            return False

    # save all plugins
    def savestate(self):
        """
    save all plugins
    """
        for i in self.plugins:
            self.plugins[i].savestate()

    def load(self):
        """
    load various things
    """
        self.load_modules("*.py")

        parser = argparse.ArgumentParser(add_help=False,
                                         description="list plugins")
        parser.add_argument('-n',
                            "--notloaded",
                            help="list plugins that are not loaded",
                            action="store_true")
        parser.add_argument(
            '-c',
            "--changed",
            help="list plugins that are load but are changed on disk",
            action="store_true")
        parser.add_argument('package',
                            help='the to list',
                            default='',
                            nargs='?')
        self.api.get('commands.add')('list',
                                     self.cmd_list,
                                     lname='Plugin Manager',
                                     parser=parser)

        parser = argparse.ArgumentParser(add_help=False,
                                         description="load a plugin")
        parser.add_argument('plugin',
                            help='the plugin to load, don\'t include the .py',
                            default='',
                            nargs='?')
        self.api.get('commands.add')('load',
                                     self.cmd_load,
                                     lname='Plugin Manager',
                                     parser=parser)

        parser = argparse.ArgumentParser(add_help=False,
                                         description="unload a plugin")
        parser.add_argument('plugin',
                            help='the plugin to unload',
                            default='',
                            nargs='?')
        self.api.get('commands.add')('unload',
                                     self.cmd_unload,
                                     lname='Plugin Manager',
                                     parser=parser)

        parser = argparse.ArgumentParser(add_help=False,
                                         description="reload a plugin")
        parser.add_argument('plugin',
                            help='the plugin to reload',
                            default='',
                            nargs='?')
        self.api.get('commands.add')('reload',
                                     self.cmd_reload,
                                     lname='Plugin Manager',
                                     parser=parser)

        self.api.get('commands.default')('list', self.sname)
        self.api.get('events.register')('savestate',
                                        self.savestate,
                                        plugin=self.sname)

        self.api.get('timers.add')('save',
                                   self.savestate,
                                   60,
                                   nodupe=True,
                                   log=False)