Merge pull request #5 from chfw/dev

version 0.0.4 : to find plugin names with different naming patterns
This commit is contained in:
jaska 2018-08-07 22:33:53 +01:00 committed by GitHub
commit 89ebe41b24
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 456 additions and 214 deletions

View File

@ -9,7 +9,7 @@ targets:
- MANIFEST.in: MANIFEST.in.jj2
- requirements.txt: requirements.txt.jj2
- "docs/source/conf.py": "docs/source/myconf.py.jj2"
- "docs/source/index.rst": "docs/source/index.rst.jj2"
- "docs/source/index.rst": "docs/source/custom_index.rst.jj2"
- test.sh: test.sh.jj2
- "lml/_version.py": _version.py.jj2
- output: CHANGELOG.rst

View File

@ -1,6 +1,15 @@
Change log
================================================================================
0.0.4 - 07.08.2018
--------------------------------------------------------------------------------
Added
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
#. `#4 <https://github.com/chfw/lml/issues/4>`_: to find plugin names with
different naming patterns
0.0.3 - 12/06/2018
--------------------------------------------------------------------------------

View File

@ -1,6 +1,12 @@
name: lml
organisation: chfw
releases:
- changes:
- action: Added
details:
- "`#4`: to find plugin names with different naming patterns"
date: 07.08.2018
version: 0.0.4
- changes:
- action: Added
details:

View File

@ -3,39 +3,174 @@ DESCRIPTION = (
'Load me later. A lazy plugin management system.' +
''
)
# -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
# import os
# import sys
# sys.path.insert(0, os.path.abspath('.'))
# -- Project information -----------------------------------------------------
project = u'lml'
copyright = u'2017-2018 Onni Software Ltd.'
author = u'C.W.'
# The short X.Y version
version = u'0.0.4'
# The full version, including alpha/beta/rc tags
release = u'0.0.4'
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc',
'sphinx.ext.doctest',
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
'sphinx.ext.napoleon',
'sphinxcontrib.spelling'
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
project = u'lml'
copyright = u'2017-2018 Onni Software Ltd.'
version = '0.0.3'
release = '0.0.3'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = 'en'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = []
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
html_theme = 'default'
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
html_theme = 'alabaster'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
# html_theme_options = {}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'lmldoc'
latex_elements = {}
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
('index', 'lml.tex',
'lml Documentation',
'Onni Software Ltd.', 'manual'),
(master_doc, 'lml.tex', u'lml Documentation',
u'Onni Software Ltd.', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'lml',
'lml Documentation',
[u'Onni Software Ltd.'], 1)
(master_doc, 'lml', u'lml Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'lml', u'lml Documentation',
author, 'lml', 'One line description of project.',
'Miscellaneous'),
]
# -- Extension configuration -------------------------------------------------
# -- Options for intersphinx extension ---------------------------------------
# Example configuration for intersphinx: refer to the Python standard library.
intersphinx_mapping = {'https://docs.python.org/': None}
# TODO: html_theme not configurable upstream
html_theme = 'default'
# TODO: DESCRIPTION not configurable upstream
texinfo_documents = [
('index', 'lml',
'lml Documentation',
@ -43,3 +178,5 @@ texinfo_documents = [
DESCRIPTION,
'Miscellaneous'),
]
intersphinx_mapping.update({
})

View File

@ -4,10 +4,11 @@ organisation: "chfw"
author: "C.W."
contact: "wangc_2011@hotmail.com"
company: "Onni Software Ltd."
version: "0.0.3"
current_version: "0.0.3"
release: "0.0.3"
version: "0.0.4"
current_version: "0.0.4"
release: "0.0.4"
copyright_year: 2017-2018
license: New BSD
dependencies: []
description: "Load me later. A lazy plugin management system."

View File

@ -14,11 +14,13 @@ from lml._version import __author__ # flake8: noqa
try:
from logging import NullHandler
except ImportError:
class NullHandler(logging.Handler):
"""
Null handler for logging
"""
Null handler for logging
"""
def emit(self, record):
pass
logging.getLogger(__name__).addHandler(NullHandler())
logging.getLogger(__name__).addHandler(NullHandler())

View File

@ -1,2 +1,2 @@
__version__ = '0.0.3'
__author__ = 'C.W.'
__version__ = "0.0.4"
__author__ = "C.W."

View File

@ -9,6 +9,7 @@
:copyright: (c) 2017-2018 by Onni Software Ltd.
:license: New BSD License, see LICENSE for more details
"""
import re
import pkgutil
import logging
from itertools import chain
@ -18,7 +19,13 @@ from lml.utils import do_import
log = logging.getLogger(__name__)
def scan_plugins(prefix, path, black_list=None, white_list=None):
def scan_plugins(
prefix,
pyinstaller_path,
black_list=None,
white_list=None,
plugin_name_patterns=None,
):
"""
Implicitly discover plugins via pkgutil and pyinstaller path
@ -36,7 +43,49 @@ def scan_plugins(prefix, path, black_list=None, white_list=None):
will be auto-loaded: robotchef_britishcuisine, robotchef_chinesecuisine,
etc.
path:string
pyinstaller_path:string
used in pyinstaller only. When your end developer would package
your main library and its plugins using pyinstaller, this path
helps pyinstaller to find the plugins.
black_list:list
a list of module names that should be skipped.
white_list:list
a list of modules that comes with your main module. If you have a
built-in module, the module name should be inserted into the list.
For example, robot_cuisine is a built-in module inside robotchef. It
is listed in white_list.
"""
__plugin_name_patterns = "^%s.+$" % prefix
scan_plugins_regex(
plugin_name_patterns=__plugin_name_patterns,
pyinstaller_path=pyinstaller_path,
black_list=black_list,
white_list=white_list,
)
def scan_plugins_regex(
plugin_name_patterns=None,
pyinstaller_path=None,
black_list=None,
white_list=None,
):
"""
Implicitly discover plugins via pkgutil and pyinstaller path using
regular expression
Parameters
-----------------
plugin_name_patterns: python regular expression
it is used to match all your plugins, either it is a prefix,
a suffix, some text in the middle or all.
pyinstaller_path:string
used in pyinstaller only. When your end developer would package
your main library and its plugins using pyinstaller, this path
helps pyinstaller to find the plugins.
@ -59,15 +108,20 @@ def scan_plugins(prefix, path, black_list=None, white_list=None):
white_list = []
# scan pkgutil.iter_modules
module_names = (module_info[1] for module_info in pkgutil.iter_modules()
if module_info[2] and module_info[1].startswith(prefix))
module_names = (
module_info[1]
for module_info in pkgutil.iter_modules()
if module_info[2] and re.match(plugin_name_patterns, module_info[1])
)
# scan pyinstaller
module_names_from_pyinstaller = scan_from_pyinstaller(prefix, path)
module_names_from_pyinstaller = scan_from_pyinstaller(
plugin_name_patterns, pyinstaller_path
)
all_modules = chain(module_names,
module_names_from_pyinstaller,
white_list)
all_modules = chain(
module_names, module_names_from_pyinstaller, white_list
)
# loop through modules and find our plug ins
for module_name in all_modules:
@ -89,15 +143,20 @@ def scan_plugins(prefix, path, black_list=None, white_list=None):
# see: https://github.com/pyinstaller/pyinstaller/issues/1905
# load modules using iter_modules()
# (should find all plug ins in normal build, but not pyinstaller)
def scan_from_pyinstaller(prefix, path):
def scan_from_pyinstaller(plugin_name_patterns, path):
"""
Discover plugins from pyinstaller
"""
table_of_content = set()
for a_toc in (importer.toc for importer in map(pkgutil.get_importer, path)
if hasattr(importer, 'toc')):
for a_toc in (
importer.toc
for importer in map(pkgutil.get_importer, path)
if hasattr(importer, "toc")
):
table_of_content |= a_toc
for module_name in table_of_content:
if module_name.startswith(prefix) and '.' not in module_name:
if "." in module_name:
continue
if re.match(plugin_name_patterns, module_name):
yield module_name

View File

@ -93,9 +93,10 @@ class PluginInfo(object):
echoing hey..
"""
def __init__(self, plugin_type,
abs_class_path=None,
tags=None, **keywords):
def __init__(
self, plugin_type, abs_class_path=None, tags=None, **keywords
):
self.plugin_type = plugin_type
self.absolute_import_path = abs_class_path
self.cls = None
@ -103,9 +104,9 @@ class PluginInfo(object):
self.__tags = tags
def __getattr__(self, name):
if name == 'module_name':
if name == "module_name":
if self.absolute_import_path:
module_name = self.absolute_import_path.split('.')[0]
module_name = self.absolute_import_path.split(".")[0]
else:
module_name = self.cls.__module__
return module_name
@ -124,8 +125,10 @@ class PluginInfo(object):
yield tag
def __repr__(self):
rep = {"plugin_type": self.plugin_type,
"path": self.absolute_import_path}
rep = {
"plugin_type": self.plugin_type,
"path": self.absolute_import_path,
}
rep.update(self.properties)
return json_dumps(rep)
@ -141,13 +144,14 @@ class PluginInfoChain(object):
It is used in the plugin packages to list all plugin classes
"""
def __init__(self, path):
self._logger = logging.getLogger(
self.__class__.__module__ + '.' + self.__class__.__name__)
self.__class__.__module__ + "." + self.__class__.__name__
)
self.module_name = path
def add_a_plugin(self, plugin_type, submodule=None,
**keywords):
def add_a_plugin(self, plugin_type, submodule=None, **keywords):
"""
Add a plain plugin
@ -161,9 +165,8 @@ class PluginInfoChain(object):
the relative import path to your plugin class
"""
a_plugin_info = PluginInfo(
plugin_type,
self._get_abs_path(submodule),
**keywords)
plugin_type, self._get_abs_path(submodule), **keywords
)
self.add_a_plugin_instance(a_plugin_info)
return self
@ -180,9 +183,11 @@ class PluginInfoChain(object):
The developer has to specify the absolute import path
"""
self._logger.debug("add %s as '%s' plugin",
plugin_info_instance.absolute_import_path,
plugin_info_instance.plugin_type)
self._logger.debug(
"add %s as '%s' plugin",
plugin_info_instance.absolute_import_path,
plugin_info_instance.plugin_type,
)
_load_me_later(plugin_info_instance)
return self
@ -201,12 +206,14 @@ class PluginManager(object):
the plugin type. All plugins of this plugin type will be
registered to it.
"""
def __init__(self, plugin_type):
self.plugin_name = plugin_type
self.registry = defaultdict(list)
self.tag_groups = dict()
self._logger = logging.getLogger(
self.__class__.__module__ + '.' + self.__class__.__name__)
self.__class__.__module__ + "." + self.__class__.__name__
)
_register_class(self)
def get_a_plugin(self, key, **keywords):
@ -237,8 +244,7 @@ class PluginManager(object):
the key to find the plugin
"""
self._logger.debug(self.registry.keys())
raise Exception(
"No %s is found for %s" % (self.plugin_name, key))
raise Exception("No %s is found for %s" % (self.plugin_name, key))
def load_me_later(self, plugin_info):
"""
@ -250,8 +256,7 @@ class PluginManager(object):
plugin_info:
a instance of plugin info
"""
self._logger.debug('load %s later',
plugin_info.absolute_import_path)
self._logger.debug("load %s later", plugin_info.absolute_import_path)
for key in plugin_info.tags():
self.registry[key.lower()].append(plugin_info)
@ -282,9 +287,7 @@ class PluginManager(object):
else:
# only library condition coud raise an exception
raise Exception("%s is not installed" % library)
self._logger.debug("load %s now for '%s'",
cls,
key)
self._logger.debug("load %s now for '%s'", cls, key)
return cls
else:
self.raise_exception(key)
@ -317,8 +320,7 @@ class PluginManager(object):
plugin_info:
a instance of plugin info
"""
self._logger.debug("register %s",
_show_me_your_name(plugin_cls))
self._logger.debug("register %s", _show_me_your_name(plugin_cls))
primary_tag = None
for index, key in enumerate(plugin_info.tags()):
plugin_info.cls = plugin_cls
@ -334,18 +336,21 @@ class PluginManager(object):
def _register_class(cls):
"""Reigister a newly created plugin manager"""
log.debug("declare '%s' plugin manager",
cls.plugin_name)
log.debug("declare '%s' plugin manager", cls.plugin_name)
PLUG_IN_MANAGERS[cls.plugin_name] = cls
if cls.plugin_name in CACHED_PLUGIN_INFO:
# check if there is early registrations or not
for plugin_info in CACHED_PLUGIN_INFO[cls.plugin_name]:
if plugin_info.absolute_import_path:
log.debug("load cached plugin info: %s",
plugin_info.absolute_import_path)
log.debug(
"load cached plugin info: %s",
plugin_info.absolute_import_path,
)
else:
log.debug("load cached plugin info: %s",
_show_me_your_name(plugin_info.cls))
log.debug(
"load cached plugin info: %s",
_show_me_your_name(plugin_info.cls),
)
cls.load_me_later(plugin_info)
del CACHED_PLUGIN_INFO[cls.plugin_name]
@ -358,8 +363,7 @@ def _register_a_plugin(plugin_info, plugin_cls):
manager.register_a_plugin(plugin_cls, plugin_info)
else:
# let's cache it and wait the manager to be registered
log.debug("caching %s",
_show_me_your_name(plugin_cls.__name__))
log.debug("caching %s", _show_me_your_name(plugin_cls.__name__))
CACHED_PLUGIN_INFO[plugin_info.plugin_type].append(plugin_info)
@ -370,17 +374,19 @@ def _load_me_later(plugin_info):
manager.load_me_later(plugin_info)
else:
# let's cache it and wait the manager to be registered
log.debug("caching %s for %s",
plugin_info.absolute_import_path,
plugin_info.plugin_type)
log.debug(
"caching %s for %s",
plugin_info.absolute_import_path,
plugin_info.plugin_type,
)
CACHED_PLUGIN_INFO[plugin_info.plugin_type].append(plugin_info)
def _get_me_pypi_package_name(module):
try:
module_name = module.__module__
root_module_name = module_name.split('.')[0]
return root_module_name.replace('_', '-')
root_module_name = module_name.split(".")[0]
return root_module_name.replace("_", "-")
except AttributeError:
return None

View File

@ -20,14 +20,14 @@ class PythonObjectEncoder(JSONEncoder):
"""
Custom object encoder for json dump
"""
def default(self, obj):
a_list_of_types = (list, dict, str,
int, float, bool, type(None))
a_list_of_types = (list, dict, str, int, float, bool, type(None))
if PY2:
a_list_of_types += (unicode,)
if isinstance(obj, a_list_of_types):
return JSONEncoder.default(self, obj)
return {'_python_object': str(obj)}
return {"_python_object": str(obj)}
def json_dumps(keywords):
@ -41,8 +41,8 @@ def do_import(plugin_module_name):
"""dynamically import a module"""
try:
plugin_module = __import__(plugin_module_name)
if '.' in plugin_module_name:
modules = plugin_module_name.split('.')
if "." in plugin_module_name:
modules = plugin_module_name.split(".")
for module in modules[1:]:
plugin_module = getattr(plugin_module, module)
log.debug("found " + plugin_module_name)
@ -55,9 +55,9 @@ def do_import(plugin_module_name):
def do_import_class(plugin_class):
"""dynamically import a class"""
try:
plugin_module_name = plugin_class.rsplit('.', 1)[0]
plugin_module_name = plugin_class.rsplit(".", 1)[0]
plugin_module = __import__(plugin_module_name)
modules = plugin_class.split('.')
modules = plugin_class.split(".")
for module in modules[1:]:
plugin_module = getattr(plugin_module, module)
return plugin_module

View File

@ -11,14 +11,14 @@ PY26 = PY2 and sys.version_info[1] < 7
NAME = 'lml'
AUTHOR = 'C.W.'
VERSION = '0.0.3'
VERSION = '0.0.4'
EMAIL = 'wangc_2011@hotmail.com'
LICENSE = 'New BSD'
DESCRIPTION = (
'Load me later. A lazy plugin management system.'
)
URL = 'https://github.com/chfw/lml'
DOWNLOAD_URL = '%s/archive/0.0.3.tar.gz' % URL
DOWNLOAD_URL = '%s/archive/0.0.4.tar.gz' % URL
FILES = ['README.rst', 'CHANGELOG.rst']
KEYWORDS = [
'python'
@ -46,8 +46,8 @@ EXTRAS_REQUIRE = {}
# You do not need to read beyond this line
PUBLISH_COMMAND = '{0} setup.py sdist bdist_wheel upload -r pypi'.format(
sys.executable)
GS_COMMAND = ('gs lml v0.0.3 ' +
"Find 0.0.3 in changelog for more details")
GS_COMMAND = ('gs lml v0.0.4 ' +
"Find 0.0.4 in changelog for more details")
NO_GS_MESSAGE = ('Automatic github release is disabled. ' +
'Please install gease to enable it.')
UPLOAD_FAILED_MSG = (

View File

@ -1,5 +1,4 @@
from lml.registry import PluginInfoChain
__test_plugins__ = PluginInfoChain(__name__).add_a_plugin(
'test_io2', 'reader')
__test_plugins__ = PluginInfoChain(__name__).add_a_plugin("test_io2", "reader")

View File

@ -3,7 +3,7 @@ from lml.plugin import PluginManager
class TestPluginManager(PluginManager):
def __init__(self):
PluginManager.__init__(self, 'test_io2')
PluginManager.__init__(self, "test_io2")
def load_me_later(self, plugin_info):
PluginManager.load_me_later(self, plugin_info)

View File

@ -9,27 +9,28 @@ try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='pyexcel-test_plugin2',
name="pyexcel-test_plugin2",
author="C. W.",
version='0.0.1',
version="0.0.1",
author_email="wangc_2011@hotmail.com",
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
packages=find_packages(exclude=["ez_setup", "examples", "tests"]),
include_package_data=True,
long_description=__doc__,
zip_safe=False,
classifiers=[
'Development Status :: 3 - Alpha',
'Topic :: Office/Business',
'Topic :: Utilities',
'Topic :: Software Development :: Libraries',
'Programming Language :: Python',
'License :: OSI Approved :: GNU General Public License v3',
'Intended Audience :: Developers',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7'
]
"Development Status :: 3 - Alpha",
"Topic :: Office/Business",
"Topic :: Utilities",
"Topic :: Software Development :: Libraries",
"Programming Language :: Python",
"License :: OSI Approved :: GNU General Public License v3",
"Intended Audience :: Developers",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
],
)

View File

@ -1,5 +1,4 @@
from lml.plugin import PluginInfoChain
__test_plugins__ = PluginInfoChain(__name__).add_a_plugin(
'test_io', 'x')
__test_plugins__ = PluginInfoChain(__name__).add_a_plugin("test_io", "x")

View File

@ -9,27 +9,28 @@ try:
from setuptools import setup, find_packages
except ImportError:
from ez_setup import use_setuptools
use_setuptools()
from setuptools import setup, find_packages
setup(
name='pyexcel-test_plugin',
name="pyexcel-test_plugin",
author="C. W.",
version='0.0.1',
version="0.0.1",
author_email="wangc_2011@hotmail.com",
packages=find_packages(exclude=['ez_setup', 'examples', 'tests']),
packages=find_packages(exclude=["ez_setup", "examples", "tests"]),
include_package_data=True,
long_description=__doc__,
zip_safe=False,
classifiers=[
'Development Status :: 3 - Alpha',
'Topic :: Office/Business',
'Topic :: Utilities',
'Topic :: Software Development :: Libraries',
'Programming Language :: Python',
'License :: OSI Approved :: GNU General Public License v3',
'Intended Audience :: Developers',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7'
]
"Development Status :: 3 - Alpha",
"Topic :: Office/Business",
"Topic :: Utilities",
"Topic :: Software Development :: Libraries",
"Programming Language :: Python",
"License :: OSI Approved :: GNU General Public License v3",
"Intended Audience :: Developers",
"Programming Language :: Python :: 2.6",
"Programming Language :: Python :: 2.7",
],
)

View File

@ -4,26 +4,26 @@ from nose.tools import eq_
def test_plugin_info():
info = PluginInfo('renderer',
abs_class_path='good.plugin.path',
custom='property')
assert info.custom == 'property'
info = PluginInfo(
"renderer", abs_class_path="good.plugin.path", custom="property"
)
assert info.custom == "property"
keys = list(info.tags())
assert len(keys) == 1
assert keys[0] == 'renderer'
assert info.module_name == 'good'
expected = {"path": "good.plugin.path",
"plugin_type": "renderer",
"custom": "property"}
assert keys[0] == "renderer"
assert info.module_name == "good"
expected = {
"path": "good.plugin.path",
"plugin_type": "renderer",
"custom": "property",
}
eq_(json.loads(info.__repr__()), expected)
def test_module_name_scenario_2():
class TestClass2:
pass
info = PluginInfo('renderer',
custom='property')
info = PluginInfo("renderer", custom="property")
info.cls = TestClass2
eq_(info.module_name, 'test_plugin_info')
eq_(info.module_name, "test_plugin_info")

View File

@ -2,86 +2,105 @@ from mock import patch
from nose.tools import eq_
@patch('pkgutil.get_importer')
@patch("pkgutil.get_importer")
def test_load_from_pyinstaller(pkgutil_get_importer):
sample_toc = set(['pyexcel_io', 'pyexcel_xls', 'blah'])
sample_toc = set(["pyexcel_io", "pyexcel_xls", "blah", "test.dot.module"])
pkgutil_get_importer.return_value.toc = sample_toc
from lml.loader import scan_from_pyinstaller
module_names = scan_from_pyinstaller('pyexcel_', 'path')
expected = ['pyexcel_io', 'pyexcel_xls']
module_names = scan_from_pyinstaller("pyexcel_", "path")
expected = ["pyexcel_io", "pyexcel_xls"]
eq_(sorted(list(module_names)), sorted(expected))
@patch('pkgutil.get_importer')
@patch('pkgutil.iter_modules')
def test_load_plugins(pkgutil_iter_modules,
pkgutil_get_importer):
test_module_name = 'pyexcel_test'
sample_toc = set(['pyexcel_io'])
@patch("pkgutil.get_importer")
def test_load_from_pyinstaller_with_regex(pkgutil_get_importer):
sample_toc = set(["pyexcel_io", "pyexcel_xls", "blah"])
pkgutil_get_importer.return_value.toc = sample_toc
pkgutil_iter_modules.return_value = [('not used', test_module_name, True)]
from lml.loader import scan_from_pyinstaller
module_names = scan_from_pyinstaller("^.+cel_.+$", "path")
expected = ["pyexcel_io", "pyexcel_xls"]
eq_(sorted(list(module_names)), sorted(expected))
@patch("pkgutil.get_importer")
@patch("pkgutil.iter_modules")
def test_load_plugins(pkgutil_iter_modules, pkgutil_get_importer):
test_module_name = "pyexcel_test"
sample_toc = set(["pyexcel_io"])
pkgutil_get_importer.return_value.toc = sample_toc
pkgutil_iter_modules.return_value = [("not used", test_module_name, True)]
from lml.loader import scan_plugins
scan_plugins('pyexcel_', '.', ['pyexcel_io'])
scan_plugins("pyexcel_", ".", ["pyexcel_io"])
from lml.plugin import CACHED_PLUGIN_INFO
info = CACHED_PLUGIN_INFO['test_io'][0]
eq_(info.plugin_type, 'test_io')
eq_(info.absolute_import_path, 'pyexcel_test.x')
info = CACHED_PLUGIN_INFO["test_io"][0]
eq_(info.plugin_type, "test_io")
eq_(info.absolute_import_path, "pyexcel_test.x")
@patch('pkgutil.get_importer')
@patch('pkgutil.iter_modules')
def test_load_plugins_without_pyinstaller(pkgutil_iter_modules,
pkgutil_get_importer):
test_module_name = 'pyexcel_test'
@patch("pkgutil.get_importer")
@patch("pkgutil.iter_modules")
def test_load_plugins_without_pyinstaller(
pkgutil_iter_modules, pkgutil_get_importer
):
test_module_name = "pyexcel_test"
sample_toc = set()
pkgutil_get_importer.return_value.toc = sample_toc
# mock iter modules
pkgutil_iter_modules.return_value = [('not used', test_module_name, True)]
pkgutil_iter_modules.return_value = [("not used", test_module_name, True)]
from lml.loader import scan_plugins
scan_plugins('pyexcel_', '.', ['pyexcel_io'])
scan_plugins("pyexcel_", ".", ["pyexcel_io"])
from lml.plugin import CACHED_PLUGIN_INFO
info = CACHED_PLUGIN_INFO['test_io'][0]
eq_(info.plugin_type, 'test_io')
eq_(info.absolute_import_path, 'pyexcel_test.x')
info = CACHED_PLUGIN_INFO["test_io"][0]
eq_(info.plugin_type, "test_io")
eq_(info.absolute_import_path, "pyexcel_test.x")
@patch('pkgutil.get_importer')
@patch('pkgutil.iter_modules')
@patch('lml.plugin._load_me_later')
def test_load_plugins_without_any_plugins(mocked_load_me_later,
pkgutil_iter_modules,
pkgutil_get_importer):
@patch("pkgutil.get_importer")
@patch("pkgutil.iter_modules")
@patch("lml.plugin._load_me_later")
def test_load_plugins_without_any_plugins(
mocked_load_me_later, pkgutil_iter_modules, pkgutil_get_importer
):
sample_toc = set()
pkgutil_get_importer.return_value.toc = sample_toc
pkgutil_iter_modules.return_value = []
from lml.loader import scan_plugins
scan_plugins('pyexcel_', '.', ['pyexcel_io'])
scan_plugins("pyexcel_", ".", ["pyexcel_io"])
assert mocked_load_me_later.called is False
@patch('pkgutil.get_importer')
@patch('pkgutil.iter_modules')
@patch('lml.plugin._load_me_later')
def test_load_plugins_without_black_list(mocked_load_me_later,
pkgutil_iter_modules,
pkgutil_get_importer):
@patch("pkgutil.get_importer")
@patch("pkgutil.iter_modules")
@patch("lml.plugin._load_me_later")
def test_load_plugins_without_black_list(
mocked_load_me_later, pkgutil_iter_modules, pkgutil_get_importer
):
sample_toc = set()
pkgutil_get_importer.return_value.toc = sample_toc
pkgutil_iter_modules.return_value = []
from lml.loader import scan_plugins
scan_plugins('pyexcel_', '.')
scan_plugins("pyexcel_", ".")
assert mocked_load_me_later.called is False
@patch('pkgutil.get_importer')
@patch('pkgutil.iter_modules')
@patch('lml.plugin._load_me_later')
def test_load_plugins_import_error(mocked_load_me_later,
pkgutil_iter_modules,
pkgutil_get_importer):
sample_toc = set(['test_non_existent_module'])
@patch("pkgutil.get_importer")
@patch("pkgutil.iter_modules")
@patch("lml.plugin._load_me_later")
def test_load_plugins_import_error(
mocked_load_me_later, pkgutil_iter_modules, pkgutil_get_importer
):
sample_toc = set(["test_non_existent_module"])
pkgutil_get_importer.return_value.toc = sample_toc
pkgutil_iter_modules.return_value = [('not used', 'pyexcel_xls', False)]
pkgutil_iter_modules.return_value = [("not used", "pyexcel_xls", False)]
from lml.loader import scan_plugins
scan_plugins('test_', '.', ['pyexcel_io'])
scan_plugins("test_", ".", ["pyexcel_io"])
assert mocked_load_me_later.called is False

View File

@ -6,24 +6,24 @@ from nose.tools import eq_, raises
def test_plugin_manager():
test_plugin = 'my plugin'
test_plugin = "my plugin"
manager = PluginManager(test_plugin)
assert PLUG_IN_MANAGERS[test_plugin] == manager
def test_load_me_later():
test_plugin = 'my plugin'
test_plugin = "my plugin"
manager = PluginManager(test_plugin)
plugin_info = make_me_a_plugin_info(test_plugin)
manager.load_me_later(plugin_info)
assert list(manager.registry.keys()) == [test_plugin]
@patch('lml.plugin.do_import_class')
@patch("lml.plugin.do_import_class")
def test_load_me_now(mock_import):
custom_class = PluginInfo
mock_import.return_value = custom_class
test_plugin = 'my plugin'
test_plugin = "my plugin"
manager = PluginManager(test_plugin)
plugin_info = make_me_a_plugin_info(test_plugin)
manager.load_me_later(plugin_info)
@ -32,27 +32,27 @@ def test_load_me_now(mock_import):
@raises(Exception)
@patch('lml.plugin.do_import_class')
@patch("lml.plugin.do_import_class")
def test_load_me_now_exception(mock_import):
custom_class = PluginInfo
mock_import.return_value = custom_class
test_plugin = 'my plugin'
test_plugin = "my plugin"
manager = PluginManager(test_plugin)
plugin_info = make_me_a_plugin_info('my')
plugin_info = make_me_a_plugin_info("my")
manager.load_me_later(plugin_info)
manager.load_me_now('my', 'my special library')
manager.load_me_now("my", "my special library")
@raises(Exception)
def test_load_me_now_no_key_found():
test_plugin = 'my plugin'
test_plugin = "my plugin"
manager = PluginManager(test_plugin)
manager.load_me_now('my', custom_property='here')
manager.load_me_now("my", custom_property="here")
@patch('lml.plugin.do_import_class')
@patch("lml.plugin.do_import_class")
def test_dynamic_load_library(mock_import):
test_plugin = 'test plugin'
test_plugin = "test plugin"
custom_obj = object()
mock_import.return_value = custom_obj
manager = PluginManager(test_plugin)
@ -61,9 +61,9 @@ def test_dynamic_load_library(mock_import):
eq_(custom_obj, plugin_info.cls)
@patch('lml.plugin.do_import_class')
@patch("lml.plugin.do_import_class")
def test_dynamic_load_library_no_action(mock_import):
test_plugin = 'test plugin'
test_plugin = "test plugin"
manager = PluginManager(test_plugin)
plugin_info = make_me_a_plugin_info(test_plugin)
plugin_info.cls = object()
@ -76,37 +76,38 @@ class TestClass:
def test_register_a_plugin():
test_plugin = 'test plugin'
test_plugin = "test plugin"
manager = PluginManager(test_plugin)
plugin_info = make_me_a_plugin_info('my')
plugin_info = make_me_a_plugin_info("my")
manager.register_a_plugin(TestClass, plugin_info)
eq_(plugin_info.cls, TestClass)
eq_(manager.registry['my'][0], plugin_info)
eq_(manager.registry["my"][0], plugin_info)
def test_get_a_plugin():
test_plugin = 'test plugin'
test_plugin = "test plugin"
manager = PluginManager(test_plugin)
plugin_info = make_me_a_plugin_info('my')
plugin_info = make_me_a_plugin_info("my")
plugin_info.cls = TestClass
manager.register_a_plugin(TestClass, plugin_info)
the_plugin = manager.get_a_plugin('my')
the_plugin = manager.get_a_plugin("my")
assert isinstance(the_plugin, TestClass)
def test_register_class():
test_plugin = 'test_plugin'
plugin_info = make_me_a_plugin_info('my')
test_plugin = "test_plugin"
plugin_info = make_me_a_plugin_info("my")
CACHED_PLUGIN_INFO[test_plugin].append(plugin_info)
manager = PluginManager(test_plugin)
assert list(manager.registry.keys()) == ['my']
assert list(manager.registry.keys()) == ["my"]
def test_load_me_later_function():
from lml.plugin import _load_me_later
test_plugin = 'my plugin'
test_plugin = "my plugin"
manager = PluginManager(test_plugin)
plugin_info = make_me_a_plugin_info(test_plugin)
_load_me_later(plugin_info)
@ -116,13 +117,14 @@ def test_load_me_later_function():
@raises(ImportError)
def test_do_import_cls_error():
from lml.plugin import do_import_class
do_import_class("non.exist.class")
def test_register_a_plugin_function_1():
PluginManager("test plugin")
@PluginInfo('test plugin', tags=['akey'])
@PluginInfo("test plugin", tags=["akey"])
class MyPlugin(object):
pass
@ -130,9 +132,9 @@ def test_register_a_plugin_function_1():
def test_register_a_plugin_function_2():
non_existent_plugin = 'I have no plugin manager'
non_existent_plugin = "I have no plugin manager"
@PluginInfo(non_existent_plugin, tags=['akey'])
@PluginInfo(non_existent_plugin, tags=["akey"])
class MyPlugin(object):
pass
@ -143,35 +145,34 @@ def test_register_a_plugin_function_2():
def test_primary_key():
manager = PluginManager("test plugin2")
@PluginInfo('test plugin2', tags=['primary key', 'key 1', 'key 2'])
@PluginInfo("test plugin2", tags=["primary key", "key 1", "key 2"])
class MyPlugin(object):
pass
pk = manager.get_primary_key('key 1')
eq_(pk, 'primary key')
pk = manager.get_primary_key("key 1")
eq_(pk, "primary key")
def test_dict_as_plugin_payload():
manager = PluginManager("test plugin3")
plugin = PluginInfo('test plugin3', tags=['primary key', 'key 1', 'key 2'])
plugin = PluginInfo("test plugin3", tags=["primary key", "key 1", "key 2"])
plugin(dict(B=1))
instance = manager.load_me_now('key 1')
instance = manager.load_me_now("key 1")
eq_(instance, dict(B=1))
def test_show_me_your_name():
class Test(object):
pass
name = _show_me_your_name(Test)
eq_(name, 'Test')
eq_(name, "Test")
name2 = _show_me_your_name(dict(A=1))
assert 'dict' in name2
assert "dict" in name2
def make_me_a_plugin_info(plugin_name):
return PluginInfo(plugin_name, 'abs_path', custom='property')
return PluginInfo(plugin_name, "abs_path", custom="property")

View File

@ -5,7 +5,6 @@ from lml.utils import do_import
def test_json_dumps():
class TestClass:
pass
@ -16,12 +15,14 @@ def test_json_dumps():
def test_do_import():
import pyexcel_test
pyexcel_test_package = do_import("pyexcel_test")
eq_(pyexcel_test_package, pyexcel_test)
def test_do_import_2():
import lml.plugin as plugin
themodule = do_import("lml.plugin")
eq_(plugin, themodule)
@ -33,5 +34,6 @@ def test_do_import_error():
def test_do_import_cls():
from lml.utils import do_import_class
manager = do_import_class("lml.plugin.PluginManager")
eq_(manager, PluginManager)