mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-22 10:29:25 +01:00
Add a 'limnoria.plugins entrypoint, to discover plugins installed via pip/setuptools.
This commit is contained in:
parent
428f5ca5dc
commit
f54588f9dd
@ -1,3 +1,4 @@
|
||||
setuptools
|
||||
chardet
|
||||
pytz
|
||||
python-dateutil
|
||||
|
@ -34,23 +34,42 @@ import os.path
|
||||
import linecache
|
||||
import importlib.util
|
||||
|
||||
try:
|
||||
import pkg_resources
|
||||
except ImportError:
|
||||
pkg_resources = None
|
||||
|
||||
if not hasattr(importlib.util, 'module_from_spec'):
|
||||
# Python < 3.5
|
||||
import imp
|
||||
|
||||
from . import callbacks, conf, log, registry
|
||||
|
||||
ENTRYPOINT_GROUPS = [
|
||||
'limnoria.plugins',
|
||||
]
|
||||
|
||||
installDir = os.path.dirname(sys.modules[__name__].__file__)
|
||||
_pluginsDir = os.path.join(installDir, 'plugins')
|
||||
|
||||
class Deprecated(ImportError):
|
||||
pass
|
||||
|
||||
def loadPluginFromEntrypoint(name):
|
||||
if pkg_resources:
|
||||
for entrypoint_group in ENTRYPOINT_GROUPS:
|
||||
for entrypoint in pkg_resources.iter_entry_points(entrypoint_group):
|
||||
if entrypoint.name.lower() == name.lower():
|
||||
return entrypoint.load()
|
||||
|
||||
return None
|
||||
|
||||
def loadPluginModule(name, ignoreDeprecation=False):
|
||||
"""Loads (and returns) the module for the plugin with the given name."""
|
||||
files = []
|
||||
pluginDirs = conf.supybot.directories.plugins()[:]
|
||||
pluginDirs.append(_pluginsDir)
|
||||
module = None
|
||||
for dir in pluginDirs:
|
||||
try:
|
||||
files.extend(os.listdir(dir))
|
||||
@ -63,31 +82,36 @@ def loadPluginModule(name, ignoreDeprecation=False):
|
||||
if len(matched_names) >= 1:
|
||||
name = matched_names[0]
|
||||
else:
|
||||
raise ImportError(name)
|
||||
|
||||
try:
|
||||
if hasattr(importlib.util, 'module_from_spec'):
|
||||
# Python >= 3.5
|
||||
spec = importlib.machinery.PathFinder.find_spec(name, pluginDirs)
|
||||
if spec is None or spec.loader is None:
|
||||
# spec is None if 'name' can't be found; and
|
||||
# spec.loader might be None in some rare occasions as well
|
||||
# (eg. for namespace packages)
|
||||
module = loadPluginFromEntrypoint(name)
|
||||
if module is None:
|
||||
raise ImportError(name)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module.__name__] = module
|
||||
spec.loader.exec_module(module)
|
||||
else:
|
||||
# Python < 3.5
|
||||
moduleInfo = imp.find_module(name, pluginDirs)
|
||||
module = imp.load_module(name, *moduleInfo)
|
||||
except:
|
||||
sys.modules.pop(name, None)
|
||||
keys = list(sys.modules.keys())
|
||||
for key in keys:
|
||||
if key.startswith(name + '.'):
|
||||
sys.modules.pop(key)
|
||||
raise
|
||||
|
||||
if module is None:
|
||||
# Found by listing files; must now import it
|
||||
try:
|
||||
if hasattr(importlib.util, 'module_from_spec'):
|
||||
# Python >= 3.5
|
||||
spec = importlib.machinery.PathFinder.find_spec(name, pluginDirs)
|
||||
if spec is None or spec.loader is None:
|
||||
# spec is None if 'name' can't be found; and
|
||||
# spec.loader might be None in some rare occasions as well
|
||||
# (eg. for namespace packages)
|
||||
raise ImportError(name)
|
||||
module = importlib.util.module_from_spec(spec)
|
||||
sys.modules[module.__name__] = module
|
||||
spec.loader.exec_module(module)
|
||||
else:
|
||||
# Python < 3.5
|
||||
moduleInfo = imp.find_module(name, pluginDirs)
|
||||
module = imp.load_module(name, *moduleInfo)
|
||||
except:
|
||||
sys.modules.pop(name, None)
|
||||
keys = list(sys.modules.keys())
|
||||
for key in keys:
|
||||
if key.startswith(name + '.'):
|
||||
sys.modules.pop(key)
|
||||
raise
|
||||
|
||||
if 'deprecated' in module.__dict__ and module.deprecated:
|
||||
if ignoreDeprecation:
|
||||
log.warning('Deprecated plugin loaded: %s', name)
|
||||
|
99
src/setup.py
Normal file
99
src/setup.py
Normal file
@ -0,0 +1,99 @@
|
||||
###
|
||||
# Copyright (c) 2020, Valentin Lorentz
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# * Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer.
|
||||
# * Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions, and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# * Neither the name of the author of this software nor the name of
|
||||
# contributors to this software may be used to endorse or promote products
|
||||
# derived from this software without specific prior written consent.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
# POSSIBILITY OF SUCH DAMAGE.
|
||||
###
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
import setuptools
|
||||
except ImportError:
|
||||
setuptools = None
|
||||
|
||||
from . import authors
|
||||
|
||||
if setuptools:
|
||||
def plugin_setup(plugin, **kwargs):
|
||||
"""Wrapper of setuptools.setup that auto-fills some fields for
|
||||
Limnoria plugins."""
|
||||
if isinstance(plugin, str):
|
||||
if plugin in sys.modules:
|
||||
plugin = sys.modules[plugin]
|
||||
else:
|
||||
setup_path = sys.modules['__main__'].__file__
|
||||
sys.path.insert(0, os.path.join(os.path.dirname(setup_path), '..'))
|
||||
plugin = __import__(plugin)
|
||||
|
||||
author = plugin.__author__
|
||||
version = plugin.__version__
|
||||
url = plugin.__url__
|
||||
maintainer = getattr(plugin, '__maintainer__', authors.unknown)
|
||||
|
||||
kwargs.setdefault('package_data', {}).setdefault('', []).append('*.po')
|
||||
|
||||
capitalized_name = plugin.Class.__name__
|
||||
kwargs.setdefault(
|
||||
'name', 'limnoria-%s' % capitalized_name.lower())
|
||||
if version:
|
||||
kwargs.setdefault('version', version)
|
||||
if url:
|
||||
kwargs.setdefault('url', url)
|
||||
|
||||
module_name = kwargs['name'].replace('-', '_')
|
||||
kwargs.setdefault('packages', [module_name])
|
||||
kwargs.setdefault('package_dir', {module_name: '.'})
|
||||
kwargs.setdefault('entry_points', {
|
||||
'limnoria.plugins': '%s = %s' % (capitalized_name, module_name)})
|
||||
|
||||
kwargs.setdefault('install_requires', []).append('limnoria')
|
||||
|
||||
kwargs.setdefault('classifiers', []).extend([
|
||||
'Environment :: Plugins',
|
||||
'Programming Language :: Python :: 3',
|
||||
'Topic :: Communications :: Chat',
|
||||
])
|
||||
|
||||
if author is not authors.unknown:
|
||||
if author.name or author.nick:
|
||||
kwargs.setdefault('author', author.name or author.nick)
|
||||
if author.email:
|
||||
kwargs.setdefault('author_email', author.email)
|
||||
|
||||
if maintainer is not authors.unknown:
|
||||
if maintainer.name or maintainer.nick:
|
||||
kwargs.setdefault(
|
||||
'maintainer', maintainer.name or maintainer.nick)
|
||||
if maintainer.email:
|
||||
kwargs.setdefault('maintainer_email', maintainer.email)
|
||||
|
||||
setuptools.setup(
|
||||
**kwargs)
|
||||
|
||||
else:
|
||||
def plugin_setup(plugin, **kwargs):
|
||||
raise ImportError('setuptools')
|
Loading…
Reference in New Issue
Block a user