aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWaylan Limberg <waylan@gmail.com>2014-08-29 20:00:39 -0400
committerWaylan Limberg <waylan@gmail.com>2014-08-29 20:00:39 -0400
commit9a5b11116e2ea8a240fa2d03cad9de334023da9d (patch)
treeb40a5fcd3f65c22a891577cce28247d2355ddfa2
parente11a15531b5bd2d3ca2636e624ac377471b294e0 (diff)
downloadmarkdown-9a5b11116e2ea8a240fa2d03cad9de334023da9d.tar.gz
markdown-9a5b11116e2ea8a240fa2d03cad9de334023da9d.tar.bz2
markdown-9a5b11116e2ea8a240fa2d03cad9de334023da9d.zip
Mark special treatment of extension names as PendingDeprecation
The builtin extensions will no longer get special treatment and have the path ("markdown.extensions.") appended . The same applies for "mdx_" extensions. All names extension must provide the full path. Fixes #336. Also deprecating support for passing in extension config settings as part of the string name. The extension_configs keyword should be used instead. Fixes #335. Also raising PendingDeprecationWarnings for positional args or the "config" keyword on the Extension Class. Pass each setting as a seperate keyword instead. Docs and tests are updated. Still need to update extension API docs.
-rw-r--r--docs/cli.txt13
-rw-r--r--docs/reference.txt42
-rw-r--r--docs/release-2.5.txt27
-rw-r--r--markdown/__init__.py33
-rw-r--r--markdown/extensions/__init__.py15
-rw-r--r--tests/test_apis.py20
-rw-r--r--tests/test_extensions.py16
7 files changed, 111 insertions, 55 deletions
diff --git a/docs/cli.txt b/docs/cli.txt
index acad4ff..7741d34 100644
--- a/docs/cli.txt
+++ b/docs/cli.txt
@@ -147,17 +147,14 @@ The `--extension_configs` option will only support YAML config files if [PyYaml]
installed on your system. JSON should work with no additional dependencies. The format
of your config file is automatically detected.
-As an alternative, you may append the extension configs as a string to the extension name
-that is provided to the `-x-` option in the following format:
-
- $ python -m markdown -x "markdown.extensions.footnotes(PLACE_MARKER=~~~~~~~~,UNIQUE_IDS=1)" input.txt
-
-Note that there are no quotes or whitespace in the above format, which severely limits
-how it can be used. For more complex settings, it is suggested that the
-`--extension_configs` option be used.
+!!!warning
+ The previously documented method of appending the extension configs as a string to the
+ extension name will be deprecated in Python-Markdown version 2.6. The `--extension_configs`
+ option should be used instead. See the [2.5 release notes] for more information.
[ec]: reference.html#extension_configs
[YAML]: http://yaml.org/
[JSON]: http://json.org/
[PyYAML]: http://pyyaml.org/
+[2.5 release notes]: release-2.5.txt
diff --git a/docs/reference.txt b/docs/reference.txt
index 033b658..afc3b05 100644
--- a/docs/reference.txt
+++ b/docs/reference.txt
@@ -66,11 +66,12 @@ The following options are available on the `markdown.markdown` function:
The list of extensions may contain instances of extensions and/or strings
of extension names.
- extensions=[MyExtension(), 'path.to.my.ext', 'extra']
+ extensions=[MyExtension(), 'path.to.my.ext']
!!! warning
- The prefered method is to pass in an instance of an extension. The other
- methods may be removed in a future version of Python-Markdown.
+ The prefered method is to pass in an instance of an extension. Strings
+ should only be used when it is impossable to import the Extension Class
+ directly (from the command line or in a template).
When passing in extension instances, each class instance must be a subclass
of `markdown.extensions.Extension` and any configuration options should be
@@ -86,24 +87,27 @@ The following options are available on the `markdown.markdown` function:
If an extension name is provided as a string, the extension must be
importable as a python module on your PYTHONPATH. Python's dot notation is
supported. Therefore, to import the 'extra' extension, one could do
- `extensions=['markdown.extensions.extra']` However, if no dots are provided
- in the string (`extensions=['extra']`) Markdown will first look for the
- module `markdown.extensions.extra` (the built-in extension), then a module
- named `mdx_extra` ('mdx_' will be appended to the beginning of the string)
- at the root of your PYTHONPATH.
+ `extensions=['markdown.extensions.extra']`
- When loading an extension by name (as a string), you may either pass in
- configuration settings to the extension using the
- [extension_configs](#extension_configs) keyword or by appending the
- settings to the name in the following format:
+ Additionaly, a Class may be specified in the name. The class must be at the end of
+ the name and be seperated by a colon from the module.
- extensions=['name(option1=value,option2=value)']
-
- Note that there are no quotes or whitespace in the above format, which
- severely limits how it can be used. For more complex settings, it is
- suggested that an instance of a class be passed in or, if that's not
- possible, the [extension_configs](#extension_configs) keyword
- be used.
+ Therefore, if you were to import the class like this:
+
+ from path.to.module import SomeExtensionClass
+
+ Then the named extension would comprise this string:
+
+ "path.to.module:SomeExtensionClass"
+
+ !!! note
+ You should only need to specify the class name if more than one extension is defined
+ within the same module. The extensions that come with Python-Markdown do *not*
+ need to have the class name specified.
+
+ When loading an extension by name (as a string), you may pass in
+ configuration settings to the extension using the
+ [extension_configs](#extension_configs) keyword.
!!! seealso "See Also"
See the documentation of the [Extension API](extensions/api.html) for
diff --git a/docs/release-2.5.txt b/docs/release-2.5.txt
index a81acfd..d45fe24 100644
--- a/docs/release-2.5.txt
+++ b/docs/release-2.5.txt
@@ -31,6 +31,33 @@ Backwards-incompatible Changes
[CodeHilite Extension]: extensions/code_hilite.html
[linenumes]: extensions/code_hilite.html#usage
+* In previous versions of Python-Markdown, the builtin extensions received
+ special status and did not require the full path to be provided. Additionaly,
+ third party extensions whose name started with "mdx_" received the same
+ special treatment. This behavior will be deprecated in version 2.6 and will
+ raise a PendingDeprecationWarning in 2.5. Ensure that you always use the full
+ path to your extensions. For example, if you previously did the following:
+
+ markdown.markdown(text, extensions=['extra'])
+
+ You should change your code to the following:
+
+ markdown.markdown(text, extensions=['markdown.extensions.extra'])
+
+ The same applies to the command line:
+
+ $ python -m markdown -x markdown.extensions.extra input.txt
+
+ See the [documentation](reference.html#extensions) for a full explaination
+ of the current behavior.
+
+* The previously documented method of appending the extension configs as
+ a string to the extension name will be deprecated in Python-Markdown
+ version 2.6 and will raise a PendingDeprecationWarning in 2.5. The
+ [extension_configs](reference.html#extension_configs) keyword should
+ be used instead. See the [documentation](reference.html#extension-configs)
+ for a full explaination of the current behavior.
+
What's New in Python-Markdown 2.5
---------------------------------
diff --git a/markdown/__init__.py b/markdown/__init__.py
index 0fdcd97..6bf84d0 100644
--- a/markdown/__init__.py
+++ b/markdown/__init__.py
@@ -36,6 +36,7 @@ from .__version__ import version, version_info
import codecs
import sys
import logging
+import warnings
import importlib
from . import util
from .preprocessors import build_preprocessors
@@ -161,7 +162,7 @@ class Markdown(object):
"""
for ext in extensions:
if isinstance(ext, util.string_type):
- ext = self.build_extension(ext, configs.get(ext, []))
+ ext = self.build_extension(ext, configs.get(ext, {}))
if isinstance(ext, Extension):
ext.extendMarkdown(self, globals())
logger.info('Successfully loaded extension "%s.%s".'
@@ -173,22 +174,29 @@ class Markdown(object):
return self
- def build_extension(self, ext_name, configs = []):
+ def build_extension(self, ext_name, configs):
"""Build extension by name, then return the module.
The extension name may contain arguments as part of the string in the
following format: "extname(key1=value1,key2=value2)"
"""
-
- # Parse extensions config params (ignore the order)
+
configs = dict(configs)
+
+ # Parse extensions config params (ignore the order)
pos = ext_name.find("(") # find the first "("
if pos > 0:
ext_args = ext_name[pos+1:-1]
ext_name = ext_name[:pos]
pairs = [x.split("=") for x in ext_args.split(",")]
configs.update([(x.strip(), y.strip()) for (x, y) in pairs])
+ warnings.warn('Setting configs in the Named Extension string is pending deprecation. '
+ 'It is recommended that you pass an instance of the extension class to '
+ 'Markdown or use the "extension_configs" keyword. The current behavior '
+ 'will be deprecated in version 2.6 and raise an error in version 2.7. '
+ 'See the Release Notes for Python-Markdown version 2.5 for more info.',
+ PendingDeprecationWarning)
# Get class name (if provided): `path.to.module:ClassName`
ext_name, class_name = ext_name.split(':', 1) if ':' in ext_name else (ext_name, '')
@@ -204,12 +212,25 @@ class Markdown(object):
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 pending deprecation. '
+ 'Use the full path to the extension with Python\'s dot notation '
+ '(eg: "%s" instead of "%s"). The current behavior will be deprecated in '
+ 'version 2.6 and raise an error in version 2.7. See the Release Notes for '
+ 'Python-Markdown version 2.5 for more info.' % (module_name, ext_name),
+ PendingDeprecationWarning)
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 behavuor of appending "mdx_" to an extension name '
+ 'is pending deprecation. Use the full path to the extension with '
+ 'Python\'s dot notation (eg: "%s" instead of "%s"). The '
+ 'current behavior will be deprecated in version 2.6 and raise an '
+ 'error in version 2.7. See the Release Notes for Python-Markdown '
+ 'version 2.5 for more info.' % (module_name_old_style, ext_name),
+ PendingDeprecationWarning)
except ImportError as e:
message = "Failed loading extension '%s' from '%s', '%s' or '%s'" \
% (ext_name, ext_name, module_name, module_name_old_style)
@@ -218,11 +239,11 @@ class Markdown(object):
if class_name:
# Load given class name from module.
- return getattr(module, class_name)(configs=configs)
+ return getattr(module, class_name)(**configs)
else:
# Expect makeExtension() function to return a class.
try:
- return module.makeExtension(configs=configs)
+ return module.makeExtension(**configs)
except AttributeError as e:
message = e.args[0]
message = "Failed to initiate extension " \
diff --git a/markdown/extensions/__init__.py b/markdown/extensions/__init__.py
index 2751eef..03b2a4c 100644
--- a/markdown/extensions/__init__.py
+++ b/markdown/extensions/__init__.py
@@ -5,6 +5,7 @@ Extensions
from __future__ import unicode_literals
from ..util import parseBoolValue
+import warnings
class Extension(object):
""" Base class for extensions to subclass. """
@@ -25,8 +26,20 @@ class Extension(object):
# (there only ever used to be one so we use arg[0])
if len(args):
self.setConfigs(args[0])
+ warnings.warn('Extension classes accepting positional args is pending Deprecation. '
+ 'Each setting should be passed into the Class as a keyword. Positional '
+ 'args will be deprecated in version 2.6 and raise an error in version '
+ '2.7. See the Release Notes for Python-Markdown version 2.5 for more info.',
+ PendingDeprecationWarning)
# check for configs kwarg for backward compat.
- self.setConfigs(kwargs.pop('configs', {}))
+ if 'configs' in kwargs.keys():
+ self.setConfigs(kwargs.pop('configs', {}))
+ warnings.warn('Extension classes accepting a dict on the single keyword "config" is '
+ 'pending Deprecation. Each setting should be passed into the Class as '
+ 'a keyword directly. The "config" keyword will be deprecated in version '
+ '2.6 and raise an error in version 2.7. See the Release Notes for '
+ 'Python-Markdown version 2.5 for more info.',
+ PendingDeprecationWarning)
# finally, use kwargs
self.setConfigs(kwargs)
diff --git a/tests/test_apis.py b/tests/test_apis.py
index 010bf70..b19db62 100644
--- a/tests/test_apis.py
+++ b/tests/test_apis.py
@@ -303,14 +303,24 @@ class TestErrors(unittest.TestCase):
self.assertRaises(NotImplementedError,
markdown.Markdown, extensions=['fake_c'])
- def testDotSyntaxExtention(self):
- """ Test that dot syntax imports properly (not using mdx_). """
- _create_fake_extension(name='fake_d', use_old_style=False)
- self.assertRaises(NotImplementedError,
+ def testMdxExtention(self):
+ """ Test that appending mdx_ raises a PendingDeprecationWarning. """
+ _create_fake_extension(name='fake_d', use_old_style=True)
+ self.assertRaises(PendingDeprecationWarning,
markdown.Markdown, extensions=['fake_d'])
+ def testShortNameExtention(self):
+ """ Test that using a short name raises a PendingDeprecationWarning. """
+ self.assertRaises(PendingDeprecationWarning,
+ markdown.Markdown, extensions=['footnotes'])
+
+ def testStringConfigExtention(self):
+ """ Test that passing configs to an Extension in the name raises a PendingDeprecationWarning. """
+ self.assertRaises(PendingDeprecationWarning,
+ markdown.Markdown, extensions=['markdown.extension.footnotes(PLACE_MARKER=FOO)'])
+
-def _create_fake_extension(name, has_factory_func=True, is_wrong_type=False, use_old_style=True):
+def _create_fake_extension(name, has_factory_func=True, is_wrong_type=False, use_old_style=False):
""" Create a fake extension module for testing. """
if use_old_style:
mod_name = '_'.join(['mdx', name])
diff --git a/tests/test_extensions.py b/tests/test_extensions.py
index a7be627..a21fd94 100644
--- a/tests/test_extensions.py
+++ b/tests/test_extensions.py
@@ -47,22 +47,6 @@ class TestExtensionClass(unittest.TestCase):
# self.ext.setConfig('bad', 'baz) ==> KeyError
self.assertRaises(KeyError, self.ext.setConfig, 'bad', 'baz')
- def testConfigAsArgListOnInit(self):
- ext = self.ExtKlass([('foo', 'baz'), ('bar', 'blah')])
- self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'})
-
- def testConfigAsArgDictOnInit(self):
- ext = self.ExtKlass({'foo': 'baz', 'bar': 'blah', 'bar': 'blah'})
- self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'})
-
- def testConfigAsKwargListOnInit(self):
- ext = self.ExtKlass(configs=[('foo', 'baz'), ('bar', 'blah')])
- self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'})
-
- def testConfigAsKwargDictOnInit(self):
- ext = self.ExtKlass(configs={'foo': 'baz', 'bar': 'blah'})
- self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'})
-
def testConfigAsKwargsOnInit(self):
ext = self.ExtKlass(foo='baz', bar='blah')
self.assertEqual(ext.getConfigs(), {'foo': 'baz', 'bar': 'blah'})