aboutsummaryrefslogtreecommitdiffstats
path: root/markdown
diff options
context:
space:
mode:
authorWaylan Limberg <waylan.limberg@icloud.com>2018-01-12 22:48:41 -0500
committerGitHub <noreply@github.com>2018-01-12 22:48:41 -0500
commitcd7324333a995eeb62a3e6eacdb3b179c6256133 (patch)
treed26faeb19e7ebee577df752e1c622ae15475ca42 /markdown
parentd4de20b77ae2e522fe1a5c730b426a5b60ac86f5 (diff)
downloadmarkdown-cd7324333a995eeb62a3e6eacdb3b179c6256133.tar.gz
markdown-cd7324333a995eeb62a3e6eacdb3b179c6256133.tar.bz2
markdown-cd7324333a995eeb62a3e6eacdb3b179c6256133.zip
Refactor Extension loading (#627)
Deprecated naming support is removed: * Removed special treatment for modules in `markdown.extensions` * Removed support for `mdx_` prefixes. Support for Entry Point names added: Support for "short names" are now implemented with entry points. Therefore all the users who call extension names as `toc` will not get errors as the builtin extensions all have entry points defined which match the old "short names" for modules in `markdown.extensions`. The benefit is that any extension can offer the same support without requiring the user to manually copy a file to that location on the file system (way to many extension authors have included such instructions in their installation documentation). The one odd thing about this is that we have been issuing a DeprecationWarning for short names and now they are fully supported again. But I think it's the right thing to do. Support for using dot notation is not removed. After all, it was never deprecated. And we shouldn't "force" entry points. There are plenty of reasons why users may not want that and not all of them can be resolved by using class instances instead. All of the following ways to load an extension are valid: # Class instance from markdown.extensions.toc import TocExtension markdown.markdown(src, extensions=[TocExtension()] # Entry point name markdown.markdown(src, extensions=['toc']) # Dot notation with class markdown.markdown(src, extensions=['markdown.extensions.toc:TocExtension']) # Dot notation without class markdown.markdown(src, extensions=['markdown.extensions.toc'])
Diffstat (limited to 'markdown')
-rw-r--r--markdown/core.py86
-rw-r--r--markdown/extensions/extra.py14
2 files changed, 33 insertions, 67 deletions
diff --git a/markdown/core.py b/markdown/core.py
index 7d9d839..ce5c333 100644
--- a/markdown/core.py
+++ b/markdown/core.py
@@ -2,9 +2,9 @@ from __future__ import absolute_import
from __future__ import unicode_literals
import codecs
import sys
-import warnings
import logging
import importlib
+import pkg_resources
from . import util
from .preprocessors import build_preprocessors
from .blockprocessors import build_block_parser
@@ -49,10 +49,11 @@ class Markdown(object):
Keyword arguments:
* extensions: A list of extensions.
- If they are of type string, the module mdx_name.py will be loaded.
- If they are a subclass of markdown.Extension, they will be used
- as-is.
- * extension_configs: Configuration settingis for extensions.
+ If an item is an instance of a subclass of `markdown.extension.Extension`, the instance will be used
+ as-is. If an item is of type string, first an entry point will be loaded. If that fails, the string is
+ assumed to use Python dot notation (`path.to.module:ClassName`) to load a markdown.Extension subclass. If
+ no class is specified, then a `makeExtension` function is called within the specified module.
+ * extension_configs: Configuration settings for extensions.
* output_format: Format of output. Supported formats are:
* "xhtml1": Outputs XHTML 1.x. Default.
* "xhtml5": Outputs XHTML style tags of HTML 5
@@ -108,8 +109,8 @@ class Markdown(object):
Keyword arguments:
* extensions: A list of extensions, which can either
- be strings or objects. See the docstring on Markdown.
- * configs: A dictionary mapping module names to config options.
+ be strings or objects.
+ * configs: A dictionary mapping extension names to config options.
"""
for ext in extensions:
@@ -130,72 +131,37 @@ class Markdown(object):
def build_extension(self, ext_name, configs):
"""
- Build extension by name, then return the module.
+ Build extension from a string name, then return an instance.
- """
+ First attempt to load an entry point. The string name must be registered as an entry point in the
+ `markdown.extensions` group which points to a subclass of the `markdown.extensions.Extension` class. If
+ multiple distributions have registered the same name, the first one found by `pkg_resources.iter_entry_points`
+ is returned.
+ If no entry point is found, assume dot notation (`path.to.module:ClassName`). Load the specified class and
+ return an instance. If no class is specified, import the module and call a `makeExtension` function and return
+ the Extension instance returned by that function.
+ """
configs = dict(configs)
+ entry_points = [ep for ep in pkg_resources.iter_entry_points('markdown.extensions', ext_name)]
+ if entry_points:
+ ext = entry_points[0].load()
+ return ext(**configs)
+
# Get class name (if provided): `path.to.module:ClassName`
ext_name, class_name = ext_name.split(':', 1) \
if ':' in ext_name else (ext_name, '')
- # Try loading the extension first from one place, then another
try:
- # Assume string uses dot syntax (`path.to.some.module`)
module = importlib.import_module(ext_name)
logger.debug(
'Successfuly imported extension module "%s".' % ext_name
)
- # For backward compat (until deprecation)
- # check that this is an extension.
- if ('.' not in ext_name and not (hasattr(module, 'makeExtension') or
- (class_name and hasattr(module, class_name)))):
- # We have a name conflict
- # eg: extensions=['tables'] and PyTables is installed
- raise ImportError
- except ImportError:
- # Preppend `markdown.extensions.` to name
- module_name = '.'.join(['markdown.extensions', ext_name])
- try:
- module = importlib.import_module(module_name)
- logger.debug(
- 'Successfuly imported extension module "%s".' %
- module_name
- )
- warnings.warn('Using short names for Markdown\'s builtin '
- 'extensions is deprecated. Use the '
- 'full path to the extension with Python\'s dot '
- 'notation (eg: "%s" instead of "%s"). The '
- 'current behavior will raise an error in version '
- '2.7. See the Release Notes for '
- 'Python-Markdown version 2.6 for more info.' %
- (module_name, ext_name),
- DeprecationWarning)
- except ImportError:
- # Preppend `mdx_` to name
- module_name_old_style = '_'.join(['mdx', ext_name])
- try:
- module = importlib.import_module(module_name_old_style)
- logger.debug(
- 'Successfuly imported extension module "%s".' %
- module_name_old_style)
- warnings.warn('Markdown\'s behavior of prepending "mdx_" '
- 'to an extension name is deprecated. '
- 'Use the full path to the '
- 'extension with Python\'s dot notation '
- '(eg: "%s" instead of "%s"). The current '
- 'behavior will raise an error in version 2.7. '
- 'See the Release Notes for Python-Markdown '
- 'version 2.6 for more info.' %
- (module_name_old_style, ext_name),
- DeprecationWarning)
- except ImportError as e:
- message = "Failed loading extension '%s' from '%s', '%s' " \
- "or '%s'" % (ext_name, ext_name, module_name,
- module_name_old_style)
- e.args = (message,) + e.args[1:]
- raise
+ except ImportError as e:
+ message = 'Failed loading extension "%s".' % ext_name
+ e.args = (message,) + e.args[1:]
+ raise
if class_name:
# Load given class name from module.
diff --git a/markdown/extensions/extra.py b/markdown/extensions/extra.py
index f59e09e..cea18ed 100644
--- a/markdown/extensions/extra.py
+++ b/markdown/extensions/extra.py
@@ -37,13 +37,13 @@ from .. import util
import re
extensions = [
- 'markdown.extensions.smart_strong',
- 'markdown.extensions.fenced_code',
- 'markdown.extensions.footnotes',
- 'markdown.extensions.attr_list',
- 'markdown.extensions.def_list',
- 'markdown.extensions.tables',
- 'markdown.extensions.abbr'
+ 'smart_strong',
+ 'fenced_code',
+ 'footnotes',
+ 'attr_list',
+ 'def_list',
+ 'tables',
+ 'abbr'
]