Skip to content

Plugin info update #176

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
Jan 12, 2017
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
PluginInfo is now file based
  • Loading branch information
Ayuto committed Jan 2, 2017
commit af91fbe7d157846adb0960b91827df6724c0cfc9
40 changes: 23 additions & 17 deletions addons/source-python/packages/source-python/plugins/info.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
class PluginInfo(dict):
"""Store information for a plugin."""

def __init__(self, verbose_name=None, author=None, description=None,
def __init__(self, name, verbose_name=None, author=None, description=None,
version=None, url=None, permissions=None, public_convar=True,
display_in_listing=None, **kwargs):
"""Initialize the instance.

:param str name:
Name of the plugin on the file system.
:param str verbose_name:
A verbose name for the plugin (e.g. GunGame).
:param str author:
Expand All @@ -45,9 +47,10 @@ def __init__(self, verbose_name=None, author=None, description=None,
description of the permission.
:param public_convar:
If set to ``True``, a public convar will be generated based on the
plugin name, version and description. Set it to ``False`` if you
don't want a public convar or set it to your own
:class:`cvars.public.PublicConVar` instance.
plugin name, verbose name and version. Set it to ``False`` if you
don't want a public convar or set it to a dictionary containing
the parameters to create a :class:`cvars.public.PublicConvar`
instance.
:param list display_in_listing:
A list that contains custom attributes that should appear in the
plugin listing (e.g. sp plugin list).
Expand All @@ -57,6 +60,7 @@ def __init__(self, verbose_name=None, author=None, description=None,
:attr:`display_in_listing`.
"""
super().__init__(**kwargs)
self.name = name
self._verbose_name = verbose_name
self.author = author
self.description = description
Expand All @@ -74,20 +78,22 @@ def __init__(self, verbose_name=None, author=None, description=None,

self.display_in_listing = [] if display_in_listing is None else display_in_listing

# A reference to its LoadedPlugin instance. Will be set by the
# LoadedPlugin instance itself
self.plugin = None

def _create_public_convar(self):
"""Create a public convar if :attr:`public_convar` is set to True."""
if self.public_convar is not True:
return

self.public_convar = PublicConVar(
'{}_version'.format(self.plugin.plugin_name),
self.version,
'{} version.'.format(self.verbose_name)
)
name = '{}_version'.format(self.name)
description = '{} version.'.format(self.verbose_name)
if self.public_convar is True:
self.public_convar = PublicConVar(
name,
self.version,
description
)
elif isinstance(self.public_convar, dict):
self.public_convar = PublicConVar(
self.public_convar.pop('name', name),
self.public_convar.pop('value', self.version),
self.public_convar.pop('description', description),
**self.public_convar)

def get_verbose_name(self):
"""Return the verbose name of the plugin.
Expand All @@ -97,7 +103,7 @@ def get_verbose_name(self):
:rtype: str
"""
if self._verbose_name is None:
return self.plugin.plugin_name.replace('_', ' ').title()
return self.name.replace('_', ' ').title()

return self._verbose_name

Expand Down
43 changes: 16 additions & 27 deletions addons/source-python/packages/source-python/plugins/instance.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,21 @@ class LoadedPlugin(object):
translations = None
prefix = None

def __init__(self, plugin_name, base_import):
"""Called when a plugin's instance is initialized."""
def __init__(self, plugin_name, manager):
"""Called when a plugin's instance is initialized.

:param str plugin_name:
Name of the plugin to load.
:param PluginManager manager:
A plugin manager instance.
"""
self.manager = manager
self.file_path = None
self.import_name = None
self.globals = None
self.base_import = base_import
self.plugin_name = plugin_name
self.directory = self.manager.get_plugin_directory(plugin_name)
self.file_path = self.directory / plugin_name + '.py'

# Fall back to the default logger if none was set
if self.logger is None:
Expand All @@ -64,10 +72,6 @@ def __init__(self, plugin_name, base_import):
self.logger.log_message(self.prefix + self.translations[
'Loading'].get_string(plugin=plugin_name))

# Get the plugin's main file
self.file_path = PLUGIN_PATH.joinpath(*tuple(
base_import.split('.')[:~0] + [plugin_name, plugin_name + '.py']))

# Does the plugin's main file exist?
if not self.file_path.isfile():

Expand All @@ -81,8 +85,9 @@ def __init__(self, plugin_name, base_import):
# is not added to the PluginManager
raise PluginFileNotFoundError

# Get the base import
self.import_name = base_import + plugin_name + '.' + plugin_name
# Get the import name
self.import_name = (self.manager.base_import + plugin_name +
'.' + plugin_name)

# Import the plugin
self._plugin = import_module(self.import_name)
Expand All @@ -92,21 +97,5 @@ def __init__(self, plugin_name, base_import):
x: getattr(self._plugin, x) for x in dir(self._plugin)}

# Add this instance to the plugin info for easier access
info = self.info
if info is not None:
info.plugin = self
info._create_public_convar()

@property
def info(self):
"""Return the plugin's PluginInfo instance.

If no PluginInfo was found, None will be returned.

:rtype: PluginInfo
"""
for obj in self.globals.values():
if isinstance(obj, PluginInfo):
return obj

return None
self.info = self.manager._create_plugin_info(self.plugin_name)
self.info._create_public_convar()
94 changes: 87 additions & 7 deletions addons/source-python/packages/source-python/plugins/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@
# Python Imports
# Collections
from collections import OrderedDict
# Configobj
from configobj import ConfigObj
from configobj import Section
# Sys
import sys

Expand All @@ -20,10 +23,13 @@
# Listeners
from listeners import on_plugin_loaded_manager
from listeners import on_plugin_unloaded_manager
# Paths
from paths import PLUGIN_PATH
# Plugins
from plugins import plugins_logger
from plugins import _plugin_strings
from plugins.errors import PluginFileNotFoundError
from plugins.info import PluginInfo


# =============================================================================
Expand Down Expand Up @@ -55,8 +61,6 @@ def __init__(self, base_import=''):
super().__init__()
self.logger = None
self.translations = None

# Store the base import path
self._base_import = base_import

# Does the object have a logger set?
Expand All @@ -73,7 +77,7 @@ def __missing__(self, plugin_name):
try:

# Get the plugin's instance
instance = self.instance(plugin_name, self.base_import)
instance = self.instance(plugin_name, self)

# Does the plugin have a load function?
if 'load' in instance.globals:
Expand Down Expand Up @@ -164,27 +168,103 @@ def base_import(self):
"""
return self._base_import

@property
def plugins_directory(self):
"""Return the directory where the plugins are stored.

:rtype: path.Path
"""
return PLUGIN_PATH.joinpath(*tuple(self.base_import.split('.')[:~0]))

def is_loaded(self, plugin_name):
"""Return whether or not a plugin is loaded.

:rtype: bool
"""
return plugin_name in self

def plugin_exists(self, plugin_name):
"""Return whether of not a plugin exists.

:rtype: bool
"""
return self.get_plugin_directory(plugin_name).isdir()

def get_plugin_instance(self, plugin_name):
"""Return a plugin's instance, if it is loaded.

:rtype: LoadedPlugin
"""
# Is the plugin loaded?
if plugin_name in self:

# Return the plugin's instance
return self[plugin_name]

# Return None if the plugin is not loaded
return None

def get_plugin_directory(self, plugin_name):
"""Return the directory of the given plugin.

:rtype: path.Path
"""
return self.plugins_directory / plugin_name

def get_plugin_info(self, plugin_name):
"""Return information about the given plugin.

:rtype: PluginInfo
"""
plugin = self.get_plugin_instance(plugin_name)
if plugin is not None:
return plugin.info

return self._create_plugin_info(plugin_name)

def _create_plugin_info(self, plugin_name):
"""Create a new :class:`plugins.info.PluginInfo` instance.

:param str plugin_name:
Name of the plugin whose plugin info should be created.
:rtype: PluginInfo
"""
if not self.plugin_exists(plugin_name):
raise ValueError(
'Plugin "{}" does not exist.'.format(plugin_name))

info_file = self.get_plugin_directory(plugin_name) / 'info.ini'
if not info_file.isfile():
# Just return an "empty" PluginInfo instance. We don't have more
# information.
return PluginInfo(plugin_name)

info = ConfigObj(info_file)
return PluginInfo(
plugin_name,
info.pop('verbose_name', None),
info.pop('author', None),
info.pop('description', None),
info.pop('version', None),
info.pop('url', None),
tuple(info.pop('permissions', dict()).items()),
self._get_public_convar_from_info_file(info),
self._get_display_in_listing_from_info_file(info),
**info.dict()
)

@staticmethod
def _get_public_convar_from_info_file(info):
data = info.pop('public_convar', True)
if isinstance(data, Section):
return data.dict()

return data

@staticmethod
def _get_display_in_listing_from_info_file(info):
data = info.pop('display_in_listing', [])
if isinstance(data, (tuple, list)):
return list(data)

return [data]

def _remove_modules(self, plugin_name):
"""Remove all modules from the plugin."""
# Get the plugins import path
Expand Down