mirror of
https://github.com/Mikaela/Limnoria.git
synced 2024-11-25 12:19:24 +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
|
chardet
|
||||||
pytz
|
pytz
|
||||||
python-dateutil
|
python-dateutil
|
||||||
|
@ -34,23 +34,42 @@ import os.path
|
|||||||
import linecache
|
import linecache
|
||||||
import importlib.util
|
import importlib.util
|
||||||
|
|
||||||
|
try:
|
||||||
|
import pkg_resources
|
||||||
|
except ImportError:
|
||||||
|
pkg_resources = None
|
||||||
|
|
||||||
if not hasattr(importlib.util, 'module_from_spec'):
|
if not hasattr(importlib.util, 'module_from_spec'):
|
||||||
# Python < 3.5
|
# Python < 3.5
|
||||||
import imp
|
import imp
|
||||||
|
|
||||||
from . import callbacks, conf, log, registry
|
from . import callbacks, conf, log, registry
|
||||||
|
|
||||||
|
ENTRYPOINT_GROUPS = [
|
||||||
|
'limnoria.plugins',
|
||||||
|
]
|
||||||
|
|
||||||
installDir = os.path.dirname(sys.modules[__name__].__file__)
|
installDir = os.path.dirname(sys.modules[__name__].__file__)
|
||||||
_pluginsDir = os.path.join(installDir, 'plugins')
|
_pluginsDir = os.path.join(installDir, 'plugins')
|
||||||
|
|
||||||
class Deprecated(ImportError):
|
class Deprecated(ImportError):
|
||||||
pass
|
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):
|
def loadPluginModule(name, ignoreDeprecation=False):
|
||||||
"""Loads (and returns) the module for the plugin with the given name."""
|
"""Loads (and returns) the module for the plugin with the given name."""
|
||||||
files = []
|
files = []
|
||||||
pluginDirs = conf.supybot.directories.plugins()[:]
|
pluginDirs = conf.supybot.directories.plugins()[:]
|
||||||
pluginDirs.append(_pluginsDir)
|
pluginDirs.append(_pluginsDir)
|
||||||
|
module = None
|
||||||
for dir in pluginDirs:
|
for dir in pluginDirs:
|
||||||
try:
|
try:
|
||||||
files.extend(os.listdir(dir))
|
files.extend(os.listdir(dir))
|
||||||
@ -63,31 +82,36 @@ def loadPluginModule(name, ignoreDeprecation=False):
|
|||||||
if len(matched_names) >= 1:
|
if len(matched_names) >= 1:
|
||||||
name = matched_names[0]
|
name = matched_names[0]
|
||||||
else:
|
else:
|
||||||
raise ImportError(name)
|
module = loadPluginFromEntrypoint(name)
|
||||||
|
if module is None:
|
||||||
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)
|
raise ImportError(name)
|
||||||
module = importlib.util.module_from_spec(spec)
|
|
||||||
sys.modules[module.__name__] = module
|
if module is None:
|
||||||
spec.loader.exec_module(module)
|
# Found by listing files; must now import it
|
||||||
else:
|
try:
|
||||||
# Python < 3.5
|
if hasattr(importlib.util, 'module_from_spec'):
|
||||||
moduleInfo = imp.find_module(name, pluginDirs)
|
# Python >= 3.5
|
||||||
module = imp.load_module(name, *moduleInfo)
|
spec = importlib.machinery.PathFinder.find_spec(name, pluginDirs)
|
||||||
except:
|
if spec is None or spec.loader is None:
|
||||||
sys.modules.pop(name, None)
|
# spec is None if 'name' can't be found; and
|
||||||
keys = list(sys.modules.keys())
|
# spec.loader might be None in some rare occasions as well
|
||||||
for key in keys:
|
# (eg. for namespace packages)
|
||||||
if key.startswith(name + '.'):
|
raise ImportError(name)
|
||||||
sys.modules.pop(key)
|
module = importlib.util.module_from_spec(spec)
|
||||||
raise
|
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 'deprecated' in module.__dict__ and module.deprecated:
|
||||||
if ignoreDeprecation:
|
if ignoreDeprecation:
|
||||||
log.warning('Deprecated plugin loaded: %s', name)
|
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