Codebase list straight.plugin / run/73c0c33c-c2a0-4efb-9441-205b7736e6b0/upstream
Import upstream version 1.4.1+git20210203.1.363b0af Debian Janitor 1 year, 4 months ago
36 changed file(s) with 203 addition(s) and 1165 deletion(s). Raw diff Collapse all Expand all
+0
-2
.gitignore less more
0 *.py[co]
1 *.swp
0 Metadata-Version: 2.1
1 Name: straight.plugin
2 Version: 1.4.2
3 Summary: A simple namespaced plugin facility
4 Home-page: https://github.com/ironfroggy/straight.plugin
5 Author: Calvin Spealman
6 Author-email: ironfroggy@gmail.com
7 Classifier: Programming Language :: Python :: 2
8 Classifier: Programming Language :: Python :: 3
9 Classifier: Environment :: Plugins
10 License-File: LICENSE
11 License-File: AUTHORS
+0
-153
docs/Makefile less more
0 # Makefile for Sphinx documentation
1 #
2
3 # You can set these variables from the command line.
4 SPHINXOPTS =
5 SPHINXBUILD = sphinx-build
6 PAPER =
7 BUILDDIR = _build
8
9 # Internal variables.
10 PAPEROPT_a4 = -D latex_paper_size=a4
11 PAPEROPT_letter = -D latex_paper_size=letter
12 ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
13 # the i18n builder cannot share the environment and doctrees with the others
14 I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) .
15
16 .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
17
18 help:
19 @echo "Please use \`make <target>' where <target> is one of"
20 @echo " html to make standalone HTML files"
21 @echo " dirhtml to make HTML files named index.html in directories"
22 @echo " singlehtml to make a single large HTML file"
23 @echo " pickle to make pickle files"
24 @echo " json to make JSON files"
25 @echo " htmlhelp to make HTML files and a HTML help project"
26 @echo " qthelp to make HTML files and a qthelp project"
27 @echo " devhelp to make HTML files and a Devhelp project"
28 @echo " epub to make an epub"
29 @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
30 @echo " latexpdf to make LaTeX files and run them through pdflatex"
31 @echo " text to make text files"
32 @echo " man to make manual pages"
33 @echo " texinfo to make Texinfo files"
34 @echo " info to make Texinfo files and run them through makeinfo"
35 @echo " gettext to make PO message catalogs"
36 @echo " changes to make an overview of all changed/added/deprecated items"
37 @echo " linkcheck to check all external links for integrity"
38 @echo " doctest to run all doctests embedded in the documentation (if enabled)"
39
40 clean:
41 -rm -rf $(BUILDDIR)/*
42
43 html:
44 $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
45 @echo
46 @echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
47
48 dirhtml:
49 $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
50 @echo
51 @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
52
53 singlehtml:
54 $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
55 @echo
56 @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
57
58 pickle:
59 $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
60 @echo
61 @echo "Build finished; now you can process the pickle files."
62
63 json:
64 $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
65 @echo
66 @echo "Build finished; now you can process the JSON files."
67
68 htmlhelp:
69 $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
70 @echo
71 @echo "Build finished; now you can run HTML Help Workshop with the" \
72 ".hhp project file in $(BUILDDIR)/htmlhelp."
73
74 qthelp:
75 $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
76 @echo
77 @echo "Build finished; now you can run "qcollectiongenerator" with the" \
78 ".qhcp project file in $(BUILDDIR)/qthelp, like this:"
79 @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/straightplugin.qhcp"
80 @echo "To view the help file:"
81 @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/straightplugin.qhc"
82
83 devhelp:
84 $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
85 @echo
86 @echo "Build finished."
87 @echo "To view the help file:"
88 @echo "# mkdir -p $$HOME/.local/share/devhelp/straightplugin"
89 @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/straightplugin"
90 @echo "# devhelp"
91
92 epub:
93 $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
94 @echo
95 @echo "Build finished. The epub file is in $(BUILDDIR)/epub."
96
97 latex:
98 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
99 @echo
100 @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
101 @echo "Run \`make' in that directory to run these through (pdf)latex" \
102 "(use \`make latexpdf' here to do that automatically)."
103
104 latexpdf:
105 $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
106 @echo "Running LaTeX files through pdflatex..."
107 $(MAKE) -C $(BUILDDIR)/latex all-pdf
108 @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
109
110 text:
111 $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
112 @echo
113 @echo "Build finished. The text files are in $(BUILDDIR)/text."
114
115 man:
116 $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
117 @echo
118 @echo "Build finished. The manual pages are in $(BUILDDIR)/man."
119
120 texinfo:
121 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
122 @echo
123 @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
124 @echo "Run \`make' in that directory to run these through makeinfo" \
125 "(use \`make info' here to do that automatically)."
126
127 info:
128 $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
129 @echo "Running Texinfo files through makeinfo..."
130 make -C $(BUILDDIR)/texinfo info
131 @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
132
133 gettext:
134 $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
135 @echo
136 @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
137
138 changes:
139 $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
140 @echo
141 @echo "The overview file is in $(BUILDDIR)/changes."
142
143 linkcheck:
144 $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
145 @echo
146 @echo "Link check complete; look for any errors in the above output " \
147 "or in $(BUILDDIR)/linkcheck/output.txt."
148
149 doctest:
150 $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
151 @echo "Testing of doctests in the sources finished, look at the " \
152 "results in $(BUILDDIR)/doctest/output.txt."
+0
-19
docs/api.rst less more
0 Straight Plugin API
1 ===================
2
3 Loaders
4 #######
5
6 .. autofunction:: straight.plugin.loaders.unified_load
7 .. autoclass:: straight.plugin.loaders.Loader
8 .. autoclass:: straight.plugin.loaders.ModuleLoader
9 .. autoclass:: straight.plugin.loaders.ObjectLoader
10 .. autoclass:: straight.plugin.loaders.ClassLoader
11
12 .. _api-plugin-manager:
13
14 PluginManager
15 #############
16
17 .. autoclass:: straight.plugin.manager.PluginManager
18 :members: produce, call, first, pipe,
+0
-244
docs/conf.py less more
0 # -*- coding: utf-8 -*-
1 #
2 # straight.plugin documentation build configuration file, created by
3 # sphinx-quickstart on Wed Jan 25 22:49:22 2012.
4 #
5 # This file is execfile()d with the current directory set to its containing dir.
6 #
7 # Note that not all possible configuration values are present in this
8 # autogenerated file.
9 #
10 # All configuration values have a default; values that are commented out
11 # serve to show the default.
12
13 import sys, os
14
15 # If extensions (or modules to document with autodoc) are in another directory,
16 # add these directories to sys.path here. If the directory is relative to the
17 # documentation root, use os.path.abspath to make it absolute, like shown here.
18 #sys.path.insert(0, os.path.abspath('.'))
19
20 # -- General configuration -----------------------------------------------------
21
22 # If your documentation needs a minimal Sphinx version, state it here.
23 #needs_sphinx = '1.0'
24
25 # Add any Sphinx extension module names here, as strings. They can be extensions
26 # coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
27 extensions = [
28 'sphinx.ext.autodoc',
29 ]
30
31 # Add any paths that contain templates here, relative to this directory.
32 templates_path = ['_templates']
33
34 # The suffix of source filenames.
35 source_suffix = '.rst'
36
37 # The encoding of source files.
38 #source_encoding = 'utf-8-sig'
39
40 # The master toctree document.
41 master_doc = 'index'
42
43 # General information about the project.
44 project = u'straight.plugin'
45 copyright = u'2012, Calvin Spealman'
46
47 # The version info for the project you're documenting, acts as replacement for
48 # |version| and |release|, also used in various other places throughout the
49 # built documents.
50 #
51 # The short X.Y version.
52 version = '1.4'
53 # The full version, including alpha/beta/rc tags.
54 release = '1.4.0'
55
56 # The language for content autogenerated by Sphinx. Refer to documentation
57 # for a list of supported languages.
58 #language = None
59
60 # There are two options for replacing |today|: either, you set today to some
61 # non-false value, then it is used:
62 #today = ''
63 # Else, today_fmt is used as the format for a strftime call.
64 #today_fmt = '%B %d, %Y'
65
66 # List of patterns, relative to source directory, that match files and
67 # directories to ignore when looking for source files.
68 exclude_patterns = ['_build']
69
70 # The reST default role (used for this markup: `text`) to use for all documents.
71 #default_role = None
72
73 # If true, '()' will be appended to :func: etc. cross-reference text.
74 #add_function_parentheses = True
75
76 # If true, the current module name will be prepended to all description
77 # unit titles (such as .. function::).
78 #add_module_names = True
79
80 # If true, sectionauthor and moduleauthor directives will be shown in the
81 # output. They are ignored by default.
82 #show_authors = False
83
84 # The name of the Pygments (syntax highlighting) style to use.
85 pygments_style = 'sphinx'
86
87 # A list of ignored prefixes for module index sorting.
88 #modindex_common_prefix = []
89
90
91 # -- Options for HTML output ---------------------------------------------------
92
93 # The theme to use for HTML and HTML Help pages. See the documentation for
94 # a list of builtin themes.
95 html_theme = 'default'
96
97 # Theme options are theme-specific and customize the look and feel of a theme
98 # further. For a list of options available for each theme, see the
99 # documentation.
100 #html_theme_options = {}
101
102 # Add any paths that contain custom themes here, relative to this directory.
103 #html_theme_path = []
104
105 # The name for this set of Sphinx documents. If None, it defaults to
106 # "<project> v<release> documentation".
107 #html_title = None
108
109 # A shorter title for the navigation bar. Default is the same as html_title.
110 #html_short_title = None
111
112 # The name of an image file (relative to this directory) to place at the top
113 # of the sidebar.
114 #html_logo = None
115
116 # The name of an image file (within the static path) to use as favicon of the
117 # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
118 # pixels large.
119 #html_favicon = None
120
121 # Add any paths that contain custom static files (such as style sheets) here,
122 # relative to this directory. They are copied after the builtin static files,
123 # so a file named "default.css" will overwrite the builtin "default.css".
124 html_static_path = ['_static']
125
126 # If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
127 # using the given strftime format.
128 #html_last_updated_fmt = '%b %d, %Y'
129
130 # If true, SmartyPants will be used to convert quotes and dashes to
131 # typographically correct entities.
132 #html_use_smartypants = True
133
134 # Custom sidebar templates, maps document names to template names.
135 #html_sidebars = {}
136
137 # Additional templates that should be rendered to pages, maps page names to
138 # template names.
139 #html_additional_pages = {}
140
141 # If false, no module index is generated.
142 #html_domain_indices = True
143
144 # If false, no index is generated.
145 #html_use_index = True
146
147 # If true, the index is split into individual pages for each letter.
148 #html_split_index = False
149
150 # If true, links to the reST sources are added to the pages.
151 #html_show_sourcelink = True
152
153 # If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
154 #html_show_sphinx = True
155
156 # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
157 #html_show_copyright = True
158
159 # If true, an OpenSearch description file will be output, and all pages will
160 # contain a <link> tag referring to it. The value of this option must be the
161 # base URL from which the finished HTML is served.
162 #html_use_opensearch = ''
163
164 # This is the file name suffix for HTML files (e.g. ".xhtml").
165 #html_file_suffix = None
166
167 # Output file base name for HTML help builder.
168 htmlhelp_basename = 'straightplugindoc'
169
170
171 # -- Options for LaTeX output --------------------------------------------------
172
173 latex_elements = {
174 # The paper size ('letterpaper' or 'a4paper').
175 #'papersize': 'letterpaper',
176
177 # The font size ('10pt', '11pt' or '12pt').
178 #'pointsize': '10pt',
179
180 # Additional stuff for the LaTeX preamble.
181 #'preamble': '',
182 }
183
184 # Grouping the document tree into LaTeX files. List of tuples
185 # (source start file, target name, title, author, documentclass [howto/manual]).
186 latex_documents = [
187 ('index', 'straightplugin.tex', u'straight.plugin Documentation',
188 u'Calvin Spealman', 'manual'),
189 ]
190
191 # The name of an image file (relative to this directory) to place at the top of
192 # the title page.
193 #latex_logo = None
194
195 # For "manual" documents, if this is true, then toplevel headings are parts,
196 # not chapters.
197 #latex_use_parts = False
198
199 # If true, show page references after internal links.
200 #latex_show_pagerefs = False
201
202 # If true, show URL addresses after external links.
203 #latex_show_urls = False
204
205 # Documents to append as an appendix to all manuals.
206 #latex_appendices = []
207
208 # If false, no module index is generated.
209 #latex_domain_indices = True
210
211
212 # -- Options for manual page output --------------------------------------------
213
214 # One entry per manual page. List of tuples
215 # (source start file, name, description, authors, manual section).
216 man_pages = [
217 ('index', 'straightplugin', u'straight.plugin Documentation',
218 [u'Calvin Spealman'], 1)
219 ]
220
221 # If true, show URL addresses after external links.
222 #man_show_urls = False
223
224
225 # -- Options for Texinfo output ------------------------------------------------
226
227 # Grouping the document tree into Texinfo files. List of tuples
228 # (source start file, target name, title, author,
229 # dir menu entry, description, category)
230 texinfo_documents = [
231 ('index', 'straightplugin', u'straight.plugin Documentation',
232 u'Calvin Spealman', 'straightplugin', 'One line description of project.',
233 'Miscellaneous'),
234 ]
235
236 # Documents to append as an appendix to all manuals.
237 #texinfo_appendices = []
238
239 # If false, no module index is generated.
240 #texinfo_domain_indices = True
241
242 # How to display URL addresses: 'footnote', 'no', or 'inline'.
243 #texinfo_show_urls = 'footnote'
+0
-117
docs/getting-started.rst less more
0 Getting Started
1 ===============
2
3 Install
4 ^^^^^^^
5
6 ::
7
8 pip install straight.plugin
9
10 That was super easy.
11
12 Decide on a Namespace
13 ^^^^^^^^^^^^^^^^^^^^^
14
15 You'll want to decide on a :term:`namespace` within your package where you'll
16 keep your own plugins and where other developers can add more plugins for
17 your package to use.
18
19 For example, if you're writing a log filtering library named ``logfilter`` you may
20 choose ``logfilter.plugins`` as a package to hold your plugins, so you'll create
21 the empty package as you would any other python package. However, the only
22 contents of ``logfilter/plugins/__init__.py`` will be a little bit of special
23 code telling python this is a :term:`namespace package`.
24
25 ::
26
27 # This file will not be needed in Python 3.3
28 from pkgutil import extend_path
29 __path__ = extend_path(__path__, __name__)
30
31
32 Now, any modules you place in this package are plugin modules able to be loaded
33 by ``straight.plugin``.
34
35 ::
36
37 from straight.plugin import load
38
39 plugins = load("logfilter.plugins")
40
41 If you'll be using more plugins than writing them, you should
42 :doc:`read more <loaders>` about the loaders available and how they work.
43
44
45 Write a Plugin
46 ^^^^^^^^^^^^^^
47
48 Writing a plugin is even easier than loading them. There are two important
49 plugin types to learn: Module plugins and class Plugins. Every module in
50 your :term:`namespace package` is a module plugin. Every class they define
51 is a class plugin.
52
53 When you load module plugins, you get all of them.
54
55 When you load class plugins, you filter them by a common base and only get
56 those class plugins which inherit it.
57
58 Module plugins are simple and usually define a few functions with names
59 expected by whoever is loading and using the plugins.
60
61 ::
62
63 # This is a module plugin
64
65 def add_extra(data):
66 if 'x' in data and 'y' in data:
67 data['z'] = x * y
68
69 # This was a fairly useless plugin
70
71 Class plugins are only a little longer, but can be a bit more controlled to
72 work with. They depend on a common class the plugins inherit, and this would
73 be defined by the project loading and using the plugins.
74
75 ::
76
77 # This is a class plugin
78
79 class RstContentParser(ContentPlugin):
80 """Parses any .rst files in a bundle."""
81
82 extensions = ('.rst',)
83
84 def parse(self, content_file):
85 src = content_file.read()
86 return self.parse_string(src)
87
88 def parse_string(self, src):
89 parts = publish_parts(source=src, writer_name='html')
90 return parts['html_body']
91
92 You can fit as many class plugins inside a module plugin as you want, and
93 to load them instead of the modules you simply pass a ``subclasses``
94 parameter to ``load()``.
95
96 ::
97
98 from straight.plugin import load
99
100 plugins = load("jules.plugins", subclasses=ContentPlugin)
101
102 The resulting set of plugins are all the classes found which inherit from
103 ContentPlugin. You can do whatever you want with these, but there are some
104 helpful tools to make it easier to work with Class plugins.
105
106 You can easily create instances of all the classes, which gives you a set
107 of Instance plugins.
108
109 ::
110
111 instances = plugins.produce()
112
113 You can even pass initialization parameters to ``produce()`` and they'll
114 be used when creating instances of all the classes. You can see the
115 :ref:`API docs <api-plugin-manager>` for the ``PluginManager`` to see the
116 other ways you can work with groups of plugins.
+0
-45
docs/glossary.rst less more
0 Glossary
1 -------------
2
3 .. glossary::
4 :sorted:
5
6 package
7 A Python package is a module defined by a directory, containing
8 a ``__init__.py`` file, and can contain other modules or other
9 packages within it.
10
11 ::
12
13 package/
14 __init__.py
15 subpackage/
16 __init__.py
17 submodule.py
18
19 see also, :term:`namespace package`
20
21 distribution
22 Separately installable sets of Python modules as stored in the
23 Python package index, and installed by distutils or setuptools.
24
25 *definition taken from* `PEP 382`_ *text*
26
27 vendor package
28 Groups of files installed by an operating system's packaging
29 mechanism (e.g. Debian or Redhat packages install on Linux systems).
30
31 *definition taken from* `PEP 382`_ *text*
32
33 namespace package
34 Mechanism for splitting a single Python package across multiple
35 directories on disk. One or more distributions (see :term:`distribution`)
36 may provide modules which exist inside the same :term:`namespace package`.
37
38 *definition taken from* `PEP 382`_ *text*
39
40 module
41 An importable python namespace defined in a single file.
42
43
44 .. _PEP 382: http://www.python.org/dev/peps/pep-0382/
+0
-82
docs/index.rst less more
0 .. straight.plugin documentation master file, created by
1 sphinx-quickstart on Wed Jan 25 22:49:22 2012.
2 You can adapt this file completely to your liking, but it should at least
3 contain the root `toctree` directive.
4
5 Welcome to straight.plugin's documentation!
6 ===========================================
7
8 .. toctree::
9 :maxdepth: 1
10
11 Getting Started <getting-started>
12 Writing Plugins <write-plugin>
13 Using Plugins <loaders>
14 API <api>
15 Glossary <glossary>
16
17
18 Overview
19 ========
20
21 Straight Plugin is very easy.
22
23 Straight Plugin provides a type of plugin you can create
24 from almost any existing Python modules, and an easy way for outside developers
25 to add functionality and customization to your projects with their own
26 plugins.
27
28 Using any available plugins is a snap.
29
30 ::
31
32 from straight.plugin import load
33
34 plugins = load('theproject.plugins', subclasses=FileHandler)
35
36 handlers = plugins.produce()
37 for line in open(filename):
38 print handlers.pipe(line)
39
40
41 And, writing plugins is just as easy.
42
43 ::
44
45 from theproject import FileHandler
46
47 class LineNumbers(FileHandler):
48 def __init__(self):
49 self.lineno = 0
50 def pipe(line):
51 self.lineno += 1
52 return "%04d %s" % (self.lineno, line)
53
54
55 Plugins are found from a :term:`namespace`, which means the above example
56 would find any ``FileHandler`` classes defined in modules you might import
57 as ``theproject.plugins.default`` or ``theproject.plugins.extra``. Through
58 the magic of :term:`namespace packages <namespace package>`, we can even
59 split these up into separate installations, even managed by different teams.
60 This means you can ship a project with a set of default plugins implementing
61 its behavior, and allow other projects to hook in new functionality simply
62 by shipping their own plugins under the same :term:`namespace`.
63
64 :doc:`Get started and learn more, today <getting-started>`
65
66
67 More Resources
68 ##############
69
70 * Full Documentation: http://readthedocs.org/docs/straightplugin/
71
72 * Mailing List: https://groups.google.com/forum/#!forum/straight.plugin
73
74
75 Indices and tables
76 ==================
77
78 * :ref:`genindex`
79 * :ref:`modindex`
80 * :ref:`search`
81
+0
-85
docs/loaders.rst less more
0 Plugin Loaders
1 ==============
2
3 Currently, three simple loaders are provided.
4
5 * The :ref:`ModuleLoader <moduleloader>` simply loads the modules found
6 * The :ref:`ClassLoader <classloader>` loads the subclasses of a given type
7 * The :ref:`ObjectLoader <objectloader>` loads arbitrary objects from the modules
8
9 .. _classloader:
10
11 ClassLoader
12 -----------
13
14 The recommended loader is the ``ClassLoader``, used to load all the
15 classes from all of the modules in the namespace given. Optionally,
16 you can pass a ``subclasses`` parameter to ``load()``, which will
17 filter the loaded classes to those which are a sub-class of any given
18 type.
19
20 For example,
21
22 ::
23
24 import os
25 from straight.plugin.loaders import ClassLoader
26 from myapp import FileHandler
27
28 plugins = ClassLoader().load('myplugins', subclasses=FileHandler)
29
30 for filename in os.listdir('.'):
31 for handler_cls in plugins:
32 handler = handler_cls(filename)
33 if handler.valid():
34 handler.process()
35
36 However, it is preferred that you use the ``load()`` helper provided.
37
38 ::
39
40 from straight.plugin import load
41
42 plugins = load('myplugins', subclasses=FileHandler)
43
44 This will automatically use the ``ClassLoader`` when given a ``subclasses``
45 argument.
46
47 .. _moduleloader:
48
49 ModuleLoader
50 ------------
51
52 Before anything else, ``straight.plugin`` loads modules. The
53 ``ModuleLoader`` is used to do this.
54
55 ::
56
57 from straight.plugin.loaders import ModuleLoader
58
59 plugins = ModuleLoader().load('myplugins')
60
61 A note about `PEP-420 <http://www.python.org/dev/peps/pep-0420/>`_:
62
63 Python 3.3 will support a new type of package, the Namespace Package. This
64 allows language-level support for the namespaces that make ``straight.plugin``
65 work and when 3.3 lands, you can create addition plugins to be found in a
66 namespace. For now, continue to use the ``pkgutil`` boilerplate, but when
67 3.3 is released, ``straight.plugin`` already supports both forms of
68 namespace package!
69
70 .. _objectloader:
71
72 ObjectLoader
73 ------------
74
75 If you need to combine multiple plugins inside each module, you can
76 load all the objects from the modules, rather than the modules themselves.
77
78 ::
79
80 from straight.plugin.loaders import ObjectLoader
81
82 plugins = ObjectLoader().load('myplugins')
83
84
+0
-190
docs/make.bat less more
0 @ECHO OFF
1
2 REM Command file for Sphinx documentation
3
4 if "%SPHINXBUILD%" == "" (
5 set SPHINXBUILD=sphinx-build
6 )
7 set BUILDDIR=_build
8 set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% .
9 set I18NSPHINXOPTS=%SPHINXOPTS% .
10 if NOT "%PAPER%" == "" (
11 set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS%
12 set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS%
13 )
14
15 if "%1" == "" goto help
16
17 if "%1" == "help" (
18 :help
19 echo.Please use `make ^<target^>` where ^<target^> is one of
20 echo. html to make standalone HTML files
21 echo. dirhtml to make HTML files named index.html in directories
22 echo. singlehtml to make a single large HTML file
23 echo. pickle to make pickle files
24 echo. json to make JSON files
25 echo. htmlhelp to make HTML files and a HTML help project
26 echo. qthelp to make HTML files and a qthelp project
27 echo. devhelp to make HTML files and a Devhelp project
28 echo. epub to make an epub
29 echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter
30 echo. text to make text files
31 echo. man to make manual pages
32 echo. texinfo to make Texinfo files
33 echo. gettext to make PO message catalogs
34 echo. changes to make an overview over all changed/added/deprecated items
35 echo. linkcheck to check all external links for integrity
36 echo. doctest to run all doctests embedded in the documentation if enabled
37 goto end
38 )
39
40 if "%1" == "clean" (
41 for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i
42 del /q /s %BUILDDIR%\*
43 goto end
44 )
45
46 if "%1" == "html" (
47 %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html
48 if errorlevel 1 exit /b 1
49 echo.
50 echo.Build finished. The HTML pages are in %BUILDDIR%/html.
51 goto end
52 )
53
54 if "%1" == "dirhtml" (
55 %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml
56 if errorlevel 1 exit /b 1
57 echo.
58 echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml.
59 goto end
60 )
61
62 if "%1" == "singlehtml" (
63 %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml
64 if errorlevel 1 exit /b 1
65 echo.
66 echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml.
67 goto end
68 )
69
70 if "%1" == "pickle" (
71 %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle
72 if errorlevel 1 exit /b 1
73 echo.
74 echo.Build finished; now you can process the pickle files.
75 goto end
76 )
77
78 if "%1" == "json" (
79 %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json
80 if errorlevel 1 exit /b 1
81 echo.
82 echo.Build finished; now you can process the JSON files.
83 goto end
84 )
85
86 if "%1" == "htmlhelp" (
87 %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp
88 if errorlevel 1 exit /b 1
89 echo.
90 echo.Build finished; now you can run HTML Help Workshop with the ^
91 .hhp project file in %BUILDDIR%/htmlhelp.
92 goto end
93 )
94
95 if "%1" == "qthelp" (
96 %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp
97 if errorlevel 1 exit /b 1
98 echo.
99 echo.Build finished; now you can run "qcollectiongenerator" with the ^
100 .qhcp project file in %BUILDDIR%/qthelp, like this:
101 echo.^> qcollectiongenerator %BUILDDIR%\qthelp\straightplugin.qhcp
102 echo.To view the help file:
103 echo.^> assistant -collectionFile %BUILDDIR%\qthelp\straightplugin.ghc
104 goto end
105 )
106
107 if "%1" == "devhelp" (
108 %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp
109 if errorlevel 1 exit /b 1
110 echo.
111 echo.Build finished.
112 goto end
113 )
114
115 if "%1" == "epub" (
116 %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub
117 if errorlevel 1 exit /b 1
118 echo.
119 echo.Build finished. The epub file is in %BUILDDIR%/epub.
120 goto end
121 )
122
123 if "%1" == "latex" (
124 %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex
125 if errorlevel 1 exit /b 1
126 echo.
127 echo.Build finished; the LaTeX files are in %BUILDDIR%/latex.
128 goto end
129 )
130
131 if "%1" == "text" (
132 %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text
133 if errorlevel 1 exit /b 1
134 echo.
135 echo.Build finished. The text files are in %BUILDDIR%/text.
136 goto end
137 )
138
139 if "%1" == "man" (
140 %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man
141 if errorlevel 1 exit /b 1
142 echo.
143 echo.Build finished. The manual pages are in %BUILDDIR%/man.
144 goto end
145 )
146
147 if "%1" == "texinfo" (
148 %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo
149 if errorlevel 1 exit /b 1
150 echo.
151 echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo.
152 goto end
153 )
154
155 if "%1" == "gettext" (
156 %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale
157 if errorlevel 1 exit /b 1
158 echo.
159 echo.Build finished. The message catalogs are in %BUILDDIR%/locale.
160 goto end
161 )
162
163 if "%1" == "changes" (
164 %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes
165 if errorlevel 1 exit /b 1
166 echo.
167 echo.The overview file is in %BUILDDIR%/changes.
168 goto end
169 )
170
171 if "%1" == "linkcheck" (
172 %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck
173 if errorlevel 1 exit /b 1
174 echo.
175 echo.Link check complete; look for any errors in the above output ^
176 or in %BUILDDIR%/linkcheck/output.txt.
177 goto end
178 )
179
180 if "%1" == "doctest" (
181 %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest
182 if errorlevel 1 exit /b 1
183 echo.
184 echo.Testing of doctests in the sources finished, look at the ^
185 results in %BUILDDIR%/doctest/output.txt.
186 goto end
187 )
188
189 :end
+0
-96
docs/write-plugin.rst less more
0 Writing Plugins
1 ===============
2
3 Plugins can exist inside your
4 existing packages or in special namespace packages, which exist
5 only to house plugins.
6
7 The only requirement is that any package containing plugins be
8 designated a "namespace package", which is currently performed
9 in Python via the ``pkgutil.extend_path`` utility, seen below.
10 This allows the namespace to be provided in multiple places on
11 the python ``sys.path``, where ``import`` looks, and all the
12 contents will be combined.
13
14 Use a :term:`namespace package`
15
16 This allows multiple packages installed on your system to share
17 this name, so they may come from different installed projects
18 and all combine to provide a larger set of plugins.
19
20
21 Example
22 -------
23
24 ::
25
26 # logfilter/__init__.py
27
28 from pkgutil import extend_path
29 __path__ = extend_path(__path__, __name__)
30
31
32 ::
33
34 # logfilter/hide_extra.py
35
36 from logfilter import Skip
37
38 def filter(log_entry):
39 level = log_entry.split(':', 1)[0]
40 if level != 'EXTRA':
41 return log_entry
42 else:
43 raise Skip()
44
45
46 Using the plugin
47 ''''''''''''''''
48
49 In our log tool, we might load all the modules in the ``logfilter``
50 namespace, and then use them all to process each entry in our logs.
51 We don't need to know all the filters ahead of time, and other packages
52 can be installed on a user's system providing additional modules
53 in the namespace, which we never even knew about.
54
55 ::
56
57 from straight.plugin import load
58
59 class Skip(Exception):
60 pass
61
62 plugins = load('logfilter')
63
64 def filter_entry(log_entry):
65 for plugin in plugins:
66 try:
67 log_entry = plugin.filter(log_entry)
68 except Skip:
69 pass
70 return log_entry
71
72
73 Distributing Plugins
74 ''''''''''''''''''''
75
76 If you are writing plugins inside your own project to use, they'll be
77 distributed like any other modules in your package. There is no extra work
78 to do here.
79
80 However, if you want to release and distribute plugins on their own, you'll
81 need to tell your :term:`setup.py` about your :term:`namespace package`.
82
83 ::
84
85 setup(
86 # ...
87 namespace_packages = ['logfilter.plugins']
88 )
89
90 This will make sure when your plugins are installed alongside the original
91 project, both are importable, even though they came from their own
92 distributions.
93
94 You can read more about this at the Distribute
95 `documentation on namespace packages <http://packages.python.org/distribute/setuptools.html#namespace-packages>`_.
+0
-1
requirements-dev.txt less more
0 sphinx
0 [egg_info]
1 tag_build =
2 tag_date = 0
3
00 #!/usr/bin/env python
11
2 import sys
23 from setuptools import setup, find_packages
34
4 INSTALL_REQUIRES = []
55
6 try:
7 import importlib
8 except ImportError:
9 INSTALL_REQUIRES.append('importlib')
10
11 setup(name='straight.plugin',
12 version='1.4.1',
13 description='A simple namespaced plugin facility',
14 author='Calvin Spealman',
15 author_email='ironfroggy@gmail.com',
16 url='https://github.com/ironfroggy/straight.plugin',
6 setup(
7 name="straight.plugin",
8 version="1.4.2",
9 description="A simple namespaced plugin facility",
10 author="Calvin Spealman",
11 author_email="ironfroggy@gmail.com",
12 url="https://github.com/ironfroggy/straight.plugin",
1713 packages=find_packages(),
18 namespace_packages=['straight'],
19 install_requires=INSTALL_REQUIRES,
14 namespace_packages=["straight"],
2015 classifiers=[
21 'Programming Language :: Python :: 2',
22 'Programming Language :: Python :: 3',
23 'Environment :: Plugins',
24 ]
16 "Programming Language :: Python :: 2",
17 "Programming Language :: Python :: 3",
18 "Environment :: Plugins",
19 ],
2520 )
0 __import__('pkg_resources').declare_namespace(__name__)
0 __import__("pkg_resources").declare_namespace(__name__)
00 from straight.plugin import loaders
11
2
32 load = loaders.unified_load
00 """Facility to load plugins."""
11
2 import os
23 import sys
3 import os
44
5 from functools import lru_cache
6 from imp import find_module
57 from importlib import import_module
6 from imp import find_module
78
89 from straight.plugin.manager import PluginManager
10
11
12 def unique_list(seq):
13 seen = set()
14 seen_add = seen.add
15 return [x for x in seq if not (x in seen or seen_add(x))]
916
1017
1118 class Loader(object):
1320
1421 def __init__(self, *args, **kwargs):
1522 self._cache = []
23 self.loaded = False
24
25 def _fill_cache(self, *args, **kwargs):
26 raise NotImplementedError()
1627
1728 def load(self, *args, **kwargs):
18 self._fill_cache(*args, **kwargs)
19 self._post_fill()
20 self._order()
29 if not self.loaded:
30 self._fill_cache(*args, **kwargs)
31 self._post_fill()
32 self._order()
33 self.loaded = True
2134 return PluginManager(self._cache)
2235
2336 def _meta(self, plugin):
24 meta = getattr(plugin, '__plugin__', None)
37 meta = getattr(plugin, "__plugin__", None)
2538 return meta
2639
2740 def _post_fill(self):
2841 for plugin in self._cache:
2942 meta = self._meta(plugin)
30 if not getattr(meta, 'load', True):
43 if not getattr(meta, "load", True):
3144 self._cache.remove(plugin)
32 for implied_namespace in getattr(meta, 'imply_plugins', []):
45 for implied_namespace in getattr(meta, "imply_plugins", []):
3346 plugins = self._cache
3447 self._cache = self.load(implied_namespace)
3548 self._post_fill()
4356
4457 def _plugin_priority(self, plugin):
4558 meta = self._meta(plugin)
46 return getattr(meta, 'priority', 0.0)
59 return getattr(meta, "priority", 0.0)
4760
4861
4962 class ModuleLoader(Loader):
5063 """Performs the work of locating and loading straight plugins.
51
64
5265 This looks for plugins in every location in the import path.
5366 """
5467
5770 self.recurse = recurse
5871
5972 def _isPackage(self, path):
60 pkg_init = os.path.join(path, '__init__.py')
61 if os.path.exists(pkg_init):
62 return True
63
64 return False
73 pkg_init = os.path.join(path, "__init__.py")
74 return os.path.exists(pkg_init)
6575
6676 def _findPluginFilePaths(self, namespace):
6777 already_seen = set()
6878
6979 # Look in each location in the path
70 for path in sys.path:
80 for path in set(sys.path):
7181
7282 # Within this, we want to look for a package for the namespace
7383 namespace_rel_path = namespace.replace(".", os.path.sep)
7484 namespace_path = os.path.join(path, namespace_rel_path)
75 if os.path.exists(namespace_path):
85 try:
7686 for possible in os.listdir(namespace_path):
7787
7888 poss_path = os.path.join(namespace_path, possible)
79 if os.path.isdir(poss_path):
80 if not self._isPackage(poss_path):
81 continue
89
90 if self._isPackage(poss_path):
8291 if self.recurse:
83 subns = '.'.join((namespace, possible.split('.py')[0]))
92 subns = ".".join((namespace, possible.split(".py")[0]))
8493 for path in self._findPluginFilePaths(subns):
8594 yield path
8695 base = possible
8796 else:
8897 base, ext = os.path.splitext(possible)
89 if base == '__init__' or ext != '.py':
98 if base == "__init__" or ext != ".py":
9099 continue
91
100
92101 if base not in already_seen:
93102 already_seen.add(base)
94103 yield os.path.join(namespace, possible)
104 except (FileNotFoundError, NotADirectoryError):
105 pass
95106
96107 def _findPluginModules(self, namespace):
97108 for filepath in self._findPluginFilePaths(namespace):
98109 path_segments = list(filepath.split(os.path.sep))
99110 path_segments = [p for p in path_segments if p]
100111 path_segments[-1] = os.path.splitext(path_segments[-1])[0]
101 import_path = '.'.join(path_segments)
112 import_path = ".".join(path_segments)
102113
103114 try:
104115 module = import_module(import_path)
105116 except ImportError:
106 #raise Exception(import_path)
117 # raise Exception(import_path)
107118
108119 module = None
109120
121132 class ObjectLoader(Loader):
122133 """Loads classes or objects out of modules in a namespace, based on a
123134 provided criteria.
124
135
125136 The load() method returns all objects exported by the module.
126137 """
127138
128139 def __init__(self, recurse=False):
140 super().__init__()
129141 self.module_loader = ModuleLoader(recurse=recurse)
130142
131143 def _fill_cache(self, namespace):
134146
135147 for module in modules:
136148 for attr_name in dir(module):
137 if not attr_name.startswith('_'):
149 if not attr_name.startswith("_"):
138150 objects.append(getattr(module, attr_name))
139
151
140152 self._cache = objects
141153 return objects
142154
160172 return classes
161173
162174
175 @lru_cache(maxsize=None, typed=False)
163176 def unified_load(namespace, subclasses=None, recurse=False):
164177 """Provides a unified interface to both the module and class loaders,
165178 finding modules by default or classes if given a ``subclasses`` parameter.
169182 return ClassLoader(recurse=recurse).load(namespace, subclasses=subclasses)
170183 else:
171184 return ModuleLoader(recurse=recurse).load(namespace)
172
00 class PluginManager(object):
1
21 def __init__(self, plugins):
32 self._plugins = plugins
43
4948
5049 Useful to utilize plugins as sets of filters.
5150 """
52
51 r = first_arg
5352 for plugin in self._plugins:
5453 method = getattr(plugin, methodname, None)
5554 if method is None:
5857 if r is not None:
5958 first_arg = r
6059 return r
61
0 Metadata-Version: 2.1
1 Name: straight.plugin
2 Version: 1.4.2
3 Summary: A simple namespaced plugin facility
4 Home-page: https://github.com/ironfroggy/straight.plugin
5 Author: Calvin Spealman
6 Author-email: ironfroggy@gmail.com
7 Classifier: Programming Language :: Python :: 2
8 Classifier: Programming Language :: Python :: 3
9 Classifier: Environment :: Plugins
10 License-File: LICENSE
11 License-File: AUTHORS
0 AUTHORS
1 LICENSE
2 MANIFEST.in
3 README.rst
4 setup.py
5 tests.py
6 straight/__init__.py
7 straight.plugin.egg-info/PKG-INFO
8 straight.plugin.egg-info/SOURCES.txt
9 straight.plugin.egg-info/dependency_links.txt
10 straight.plugin.egg-info/namespace_packages.txt
11 straight.plugin.egg-info/top_level.txt
12 straight/plugin/__init__.py
13 straight/plugin/loaders.py
14 straight/plugin/manager.py
15 test-packages/class-test-plugins/testplugin/__init__.py
16 test-packages/class-test-plugins/testplugin/testclasses.py
17 test-packages/imply-plugins/testplugin/__init__.py
18 test-packages/imply-plugins/testplugin/foo.py
19 test-packages/imply-plugins/testplugin_2/__init__.py
20 test-packages/imply-plugins/testplugin_2/bar.py
21 test-packages/more-test-plugins/testplugin/__init__.py
22 test-packages/more-test-plugins/testplugin/bar.py
23 test-packages/package-test-plugins/testplugin/__init__.py
24 test-packages/package-test-plugins/testplugin/bar/__init__.py
25 test-packages/package-test-plugins/testplugin/baz/__init__.py
26 test-packages/package-test-plugins/testplugin/baz/quu/__init__.py
27 test-packages/package-test-plugins/testplugin/foo/__init__.py
28 test-packages/pep-420-plugins/testplugin/foo.py
29 test-packages/some-test-plugins/testplugin/__init__.py
30 test-packages/some-test-plugins/testplugin/foo.py
00 from pkgutil import extend_path
1
12 __path__ = extend_path(__path__, __name__)
00 class A(object):
11 class __plugin__:
22 priority = 0.5
3
34
45 class B(object):
56 class __plugin__:
67 priority = 1.0
78
9
810 class A1(A):
911 pass
00 from pkgutil import extend_path
1
12 __path__ = extend_path(__path__, __name__)
00 class __plugin__:
1 imply_plugins = (
2 'testplugin_2',
3 )
1 imply_plugins = ("testplugin_2",)
42 load = False
3
4
55 def do(x):
66 return x + 1
00 from pkgutil import extend_path
1
12 __path__ = extend_path(__path__, __name__)
00 from pkgutil import extend_path
1
12 __path__ = extend_path(__path__, __name__)
00 from pkgutil import extend_path
1
12 __path__ = extend_path(__path__, __name__)
00 from pkgutil import extend_path
1
12 __path__ = extend_path(__path__, __name__)
00 #!/usr/bin/env python
11
2 import os
23 import sys
3 import os
44 import unittest
55 from types import ModuleType
6
7 import mock
6 from unittest import mock
87
98 from straight.plugin import loaders, manager
109
2019 Usually you can use TestResult.skip() or one of the skipping decorators
2120 instead of raising this directly.
2221 """
22
2323 def skipIf(cond, reason):
2424 """
2525 Unconditionally skip a test.
2626 """
27
2728 def decorator(test_item):
2829 if cond:
2930 if not isinstance(test_item, type):
31
3032 @functools.wraps(test_item)
3133 def skip_wrapper(*args, **kwargs):
3234 pass
35
3336 test_item = skip_wrapper
3437
3538 test_item.__unittest_skip__ = True
3740 return test_item
3841 else:
3942 return test_item
43
4044 return decorator
4145
4246
5660 for path in self.paths:
5761 del sys.path[-1]
5862 for modname in list(sys.modules):
59 if modname.startswith('testplugin'):
63 if modname.startswith("testplugin"):
6064 del sys.modules[modname]
6165
6266
6367 class ModuleLoaderTestCase(LoaderTestCaseMixin, unittest.TestCase):
6468
6569 paths = (
66 os.path.join(os.path.dirname(__file__), 'test-packages', 'more-test-plugins'),
67 os.path.join(os.path.dirname(__file__), 'test-packages', 'some-test-plugins'),
68 )
69
70 os.path.join(os.path.dirname(__file__), "test-packages", "more-test-plugins"),
71 os.path.join(os.path.dirname(__file__), "test-packages", "some-test-plugins"),
72 )
73
7074 def setUp(self):
7175 self.loader = loaders.ModuleLoader()
7276 super(ModuleLoaderTestCase, self).setUp()
73
77
7478 def test_load(self):
75 modules = list(self.loader.load('testplugin'))
79 modules = list(self.loader.load("testplugin"))
7680 assert len(modules) == 2, modules
7781
7882 def test_plugin(self):
79 assert self.loader.load('testplugin')[0].do(1) == 2
83 assert self.loader.load("testplugin")[0].do(1) == 2
8084
8185
8286 class ImpliedNamespaceModuleTestCase(LoaderTestCaseMixin, unittest.TestCase):
8387
8488 paths = (
85 os.path.join(os.path.dirname(__file__), 'test-packages', 'pep-420-plugins'),
86 )
87
89 os.path.join(os.path.dirname(__file__), "test-packages", "pep-420-plugins"),
90 )
91
8892 def setUp(self):
8993 self.loader = loaders.ModuleLoader()
9094 super(ImpliedNamespaceModuleTestCase, self).setUp()
91
92 @skipIf(sys.version_info < (3, 3),"Python < 3.3")
95
96 @skipIf(sys.version_info < (3, 3), "Python < 3.3")
9397 def test_load(self):
94 modules = list(self.loader.load('testplugin'))
98 modules = list(self.loader.load("testplugin"))
9599 assert len(modules) == 1, modules
96100
97101 @skipIf(sys.version_info < (3, 3), "Python < 3.3")
98102 def test_plugin(self):
99 r = self.loader.load('testplugin')[0].do("implied namespace packages are")
103 r = self.loader.load("testplugin")[0].do("implied namespace packages are")
100104 self.assertEqual(r, "implied namespace packages are from pep-420")
101105
102106
103107 class ImplyLoaderTestCase(LoaderTestCaseMixin, unittest.TestCase):
104108
105 paths = (
106 os.path.join(os.path.dirname(__file__), 'test-packages', 'imply-plugins'),
107 )
108
109 paths = (os.path.join(os.path.dirname(__file__), "test-packages", "imply-plugins"),)
110
109111 def setUp(self):
110112 self.loader = loaders.ModuleLoader()
111113 super(ImplyLoaderTestCase, self).setUp()
112
114
113115 def test_load(self):
114 modules = list(self.loader.load('testplugin'))
116 modules = list(self.loader.load("testplugin"))
115117 assert len(modules) == 1, modules
116 assert modules[0].__name__ == 'testplugin_2.bar', modules[0].__name__
118 assert modules[0].__name__ == "testplugin_2.bar", modules[0].__name__
117119
118120
119121 class ObjectLoaderTestCase(LoaderTestCaseMixin, unittest.TestCase):
120122
121123 paths = (
122 os.path.join(os.path.dirname(__file__), 'test-packages', 'more-test-plugins'),
123 os.path.join(os.path.dirname(__file__), 'test-packages', 'some-test-plugins'),
124 os.path.join(os.path.dirname(__file__), "test-packages", "more-test-plugins"),
125 os.path.join(os.path.dirname(__file__), "test-packages", "some-test-plugins"),
124126 )
125127
126128 def setUp(self):
128130 super(ObjectLoaderTestCase, self).setUp()
129131
130132 def test_load_all(self):
131 objects = list(self.loader.load('testplugin'))
132 self.assertEqual(len(objects), 2, str(objects)[:100] + ' ...')
133 objects = list(self.loader.load("testplugin"))
134 self.assertEqual(len(objects), 2, str(objects)[:100] + " ...")
133135
134136
135137 class ClassLoaderTestCase(LoaderTestCaseMixin, unittest.TestCase):
136138
137139 paths = (
138 os.path.join(os.path.dirname(__file__), 'test-packages', 'class-test-plugins'),
140 os.path.join(os.path.dirname(__file__), "test-packages", "class-test-plugins"),
139141 )
140142
141143 def setUp(self):
143145 super(ClassLoaderTestCase, self).setUp()
144146
145147 def test_all_classes(self):
146 classes = list(self.loader.load('testplugin'))
148 classes = list(self.loader.load("testplugin"))
147149
148150 self.assertEqual(len(classes), 3)
149151
150152 def test_subclasses(self):
151153 from testplugin import testclasses
152 classes = list(self.loader.load('testplugin', subclasses=testclasses.A))
154
155 classes = list(self.loader.load("testplugin", subclasses=testclasses.A))
153156
154157 self.assertEqual(len(classes), 1)
155158 self.assertTrue(classes[0] is testclasses.A1)
156159
160
157161 class PriorityLoaderTestCase(LoaderTestCaseMixin, unittest.TestCase):
158162
159163 paths = (
160 os.path.join(os.path.dirname(__file__), 'test-packages', 'class-test-plugins'),
164 os.path.join(os.path.dirname(__file__), "test-packages", "class-test-plugins"),
161165 )
162166
163167 def setUp(self):
165169 super(PriorityLoaderTestCase, self).setUp()
166170
167171 def test_all_classes(self):
168 classes = list(self.loader.load('testplugin'))
169
170 self.assertEqual(classes[0].__name__, 'B')
171 self.assertEqual(classes[1].__name__, 'A')
172 self.assertEqual(classes[2].__name__, 'A1')
172 classes = list(self.loader.load("testplugin"))
173
174 self.assertEqual(classes[0].__name__, "B")
175 self.assertEqual(classes[1].__name__, "A")
176 self.assertEqual(classes[2].__name__, "A1")
177
173178
174179 class PackageLoaderTestCase(LoaderTestCaseMixin, unittest.TestCase):
175180 paths = (
176 os.path.join(os.path.dirname(__file__), 'test-packages', 'package-test-plugins'),
181 os.path.join(
182 os.path.dirname(__file__), "test-packages", "package-test-plugins"
183 ),
177184 )
178185
179186 def setUp(self):
180187 self.loader = loaders.ModuleLoader()
181188 super(PackageLoaderTestCase, self).setUp()
182
189
183190 def test_find_packages(self):
184 filepaths = list(self.loader._findPluginFilePaths('testplugin'))
191 filepaths = list(self.loader._findPluginFilePaths("testplugin"))
185192
186193 self.assertEqual(len(filepaths), 3)
187194
188
189195 def test_load_packages(self):
190 packages = list(self.loader.load('testplugin'))
196 packages = list(self.loader.load("testplugin"))
191197
192198 self.assertEqual(len(packages), 3)
193199
195201 self.assertTrue(isinstance(pkg, ModuleType))
196202
197203 def test_plugin(self):
198 plugins = self.loader.load('testplugin')
204 plugins = self.loader.load("testplugin")
199205
200206 results = set(p.do(1) for p in plugins)
201207
204210
205211 class RecursingPackageLoaderTestCase(LoaderTestCaseMixin, unittest.TestCase):
206212 paths = (
207 os.path.join(os.path.dirname(__file__), 'test-packages', 'package-test-plugins'),
213 os.path.join(
214 os.path.dirname(__file__), "test-packages", "package-test-plugins"
215 ),
208216 )
209217
210218 def setUp(self):
212220 super(RecursingPackageLoaderTestCase, self).setUp()
213221
214222 def test_find_packages(self):
215 filepaths = list(self.loader._findPluginFilePaths('testplugin'))
223 filepaths = list(self.loader._findPluginFilePaths("testplugin"))
216224
217225 self.assertEqual(len(filepaths), 4)
218226
219
220227 def test_load_packages(self):
221 packages = list(self.loader.load('testplugin'))
228 packages = list(self.loader.load("testplugin"))
222229
223230 self.assertEqual(len(packages), 4)
224231
226233 self.assertTrue(isinstance(pkg, ModuleType))
227234
228235 def test_plugin(self):
229 plugins = self.loader.load('testplugin')
236 plugins = self.loader.load("testplugin")
230237
231238 results = set(p.do(1) for p in plugins)
232239
233 self.assertEqual(results, set((2, 3, 4, 'quu')))
240 self.assertEqual(results, set((2, 3, 4, "quu")))
234241
235242
236243 class PluginManagerTestCase(unittest.TestCase):
237
238 def setUp(self):
239 self.m = manager.PluginManager([
240 mock.Mock(),
241 mock.Mock(),
242 ])
244 def setUp(self):
245 self.m = manager.PluginManager([mock.Mock(), mock.Mock()])
243246
244247 def test_first(self):
245248 self.m._plugins[0].x.return_value = 1
246249
247 self.assertEqual(1, self.m.first('x', 'a'))
250 self.assertEqual(1, self.m.first("x", "a"))
248251 self.assertFalse(self.m._plugins[1].called)
249 self.assertTrue(self.m._plugins[0].called_with('a'))
252 self.assertTrue(self.m._plugins[0].called_with("a"))
250253
251254 def test_pipe(self):
252255 def plus_one(x):
253256 return x + 1
257
254258 self.m._plugins[0].x.side_effect = plus_one
255259 self.m._plugins[1].x.side_effect = plus_one
256
257 self.assertEqual(3, self.m.pipe('x', 1))
260 self.assertEqual(3, self.m.pipe("x", 1))
261
262 def test_pipe_no_plugins_found(self):
263 no_plugins = manager.PluginManager([])
264 self.assertEqual(1, no_plugins.pipe("x", 1))
258265
259266 def test_call(self):
260 results = self.m.call('x', 1)
261 self.assertTrue(self.m._plugins[0].called_with('a'))
267 results = self.m.call("x", 1)
268 self.assertTrue(self.m._plugins[0].called_with("a"))
262269 self.assertTrue(self.m._plugins[1].x.called_with(1))
263270
264271 def test_produce(self):
268275 assert products[1] is self.m._plugins[1].return_value
269276 self.m._plugins[1].called_with(1, 2)
270277
271 if __name__ == '__main__':
278
279 if __name__ == "__main__":
272280 unittest.main()
+0
-10
tox.ini less more
0 [tox]
1 envlist = py27,py33,py34,py35
2
3 [default]
4 deps =
5
6 [testenv]
7 setenv =
8 PYTHON_PATH = {toxinidir}
9 commands = {envpython} tests.py {posargs}