aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.spell-dict1
-rw-r--r--docs/cli.md34
-rw-r--r--docs/extensions/abbreviations.md4
-rw-r--r--docs/extensions/admonition.md11
-rw-r--r--docs/extensions/api.md77
-rw-r--r--docs/extensions/attr_list.md4
-rw-r--r--docs/extensions/code_hilite.md4
-rw-r--r--docs/extensions/definition_lists.md4
-rw-r--r--docs/extensions/extra.md2
-rw-r--r--docs/extensions/fenced_code_blocks.md4
-rw-r--r--docs/extensions/footnotes.md5
-rw-r--r--docs/extensions/index.md48
-rw-r--r--docs/extensions/meta_data.md6
-rw-r--r--docs/extensions/nl2br.md6
-rw-r--r--docs/extensions/sane_lists.md4
-rw-r--r--docs/extensions/smart_strong.md13
-rw-r--r--docs/extensions/smarty.md6
-rw-r--r--docs/extensions/tables.md4
-rw-r--r--docs/extensions/toc.md6
-rw-r--r--docs/extensions/wikilinks.md4
-rw-r--r--docs/reference.md57
-rw-r--r--markdown/core.py86
-rw-r--r--markdown/extensions/extra.py14
-rwxr-xr-xsetup.py19
-rw-r--r--tests/test_apis.py49
-rw-r--r--tests/test_extensions.py38
-rw-r--r--tests/test_legacy.py69
27 files changed, 288 insertions, 291 deletions
diff --git a/.spell-dict b/.spell-dict
index ab6b7a5..139a195 100644
--- a/.spell-dict
+++ b/.spell-dict
@@ -86,6 +86,7 @@ sanitizer
sanitizers
Sauder
schemeless
+setuptools
Sergej
serializer
serializers
diff --git a/docs/cli.md b/docs/cli.md
index dbac2c4..35c77b4 100644
--- a/docs/cli.md
+++ b/docs/cli.md
@@ -118,16 +118,26 @@ Using Extensions
To load a Python-Markdown extension from the command line use the `-x`
(or `--extension`) option. The extension module must be on your `PYTHONPATH`
(see the [Extension API](extensions/api.md) for details). The extension can
-then be invoked by the name of that module using Python's dot syntax:
+then be invoked by the name assigned to an entry point or using Python's dot
+notation to point to an extension
+
+For example, to load an extension with the assigned entry point name `myext`,
+run the following command:
+
+```bash
+python -m markdown -x myext input.txt
+```
+
+And to load an extension with Python's dot notation:
```bash
-python -m markdown -x path.to.module input.txt
+python -m markdown -x path.to.module:MyExtClass input.txt
```
To load multiple extensions, specify an `-x` option for each extension:
```bash
-python -m markdown -x markdown.extensions.footnotes -x markdown.extensions.codehilite input.txt
+python -m markdown -x myext -x path.to.module:MyExtClass input.txt
```
If the extension supports configuration options (see the documentation for the
@@ -135,7 +145,7 @@ extension you are using to determine what settings it supports, if any), you
can pass them in as well:
```bash
-python -m markdown -x markdown.extensions.footnotes -c config.yml input.txt
+python -m markdown -x myext -c config.yml input.txt
```
The `-c` (or `--extension_configs`) option accepts a file name. The file must be
@@ -145,25 +155,19 @@ map to a Python Dictionary in the format required by the
the file `config.yaml` referenced in the above example might look like this:
```yaml
-markdown.extensions.footnotes:
- PLACE_MARKER: ~~~~~~~~
- UNIQUE_IDS: True
+myext:
+ option1: 'value1'
+ option2: True
```
Note that while the `--extension_configs` option does specify the
-"markdown.extensions.footnotes" extension, you still need to load the extension
-with the `-x` option, or the configuration for that extension will be ignored.
+`myext` extension, you still need to load the extension with the `-x` option,
+or the configuration for that extension will be ignored.
The `--extension_configs` option will only support YAML configuration files if
[PyYAML] is installed on your system. JSON should work with no additional
dependencies. The format of your configuration file is automatically detected.
-!!!warning
- The previously documented method of appending the extension configuration
- options 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/
diff --git a/docs/extensions/abbreviations.md b/docs/extensions/abbreviations.md
index d580d56..eed8788 100644
--- a/docs/extensions/abbreviations.md
+++ b/docs/extensions/abbreviations.md
@@ -39,7 +39,7 @@ is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.</p>
Usage
-----
-See [Extensions](index.md) for general extension usage, specify `markdown.extensions.abbr`
-as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `abbr` as the name
+of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/admonition.md b/docs/extensions/admonition.md
index 26e6299..a6a8398 100644
--- a/docs/extensions/admonition.md
+++ b/docs/extensions/admonition.md
@@ -78,7 +78,14 @@ rST suggests the following `types`, but you're free to use whatever you want:
Styling
-------
-There is no CSS included as part of this extension. Look up the default
-[Sphinx][sphinx] theme if you need inspiration.
+There is no CSS included as part of this extension. Check out the default
+[Sphinx][sphinx] theme for inspiration.
[sphinx]: http://sphinx.pocoo.org/
+
+## Usage
+
+See [Extensions](index.md) for general extension usage. Use `admonition` as the
+name of the extension.
+
+This extension does not accept any special configuration options.
diff --git a/docs/extensions/api.md b/docs/extensions/api.md
index 3d8cfff..cba4ea7 100644
--- a/docs/extensions/api.md
+++ b/docs/extensions/api.md
@@ -639,7 +639,7 @@ following methods available to assist in working with configuration settings:
Sets multiple configuration settings given a dict of key/value pairs.
-### `makeExtension` {: #makeextension }
+### Naming an Extension { #naming_an_extension }
As noted in the [library reference] an instance of an extension can be passed
directly to Markdown. In fact, this is the preferred way to use third-party
@@ -649,36 +649,70 @@ For example:
```python
import markdown
-import myextension
-myext = myextension.MyExtension(option='value')
-md = markdown.Markdown(extensions=[myext])
+from path.to.module import MyExtention
+md = markdown.Markdown(extensions=[MyExtension(option='value')])
```
-Markdown also accepts "named" third party extensions for those occasions when it
-is impractical to import an extension directly (from the command line or from
-within templates).
+However, Markdown also accepts "named" third party extensions for those
+occasions when it is impractical to import an extension directly (from the
+command line or from within templates). A "name" can either be a registered
+[entry point](#entry_point) or a string using Python's [dot
+notation](#dot_notation).
-The "name" of your extension must be a string consisting of the importable path to
-your module using Python's dot notation. Therefore, if you are providing a library
-to your users and would like to include a custom markdown extension within your
-library, that extension would be named `"mylib.mdext.myext"` where `mylib/mdext/myext.py`
-contains the extension and the `mylib` directory is on the PYTHONPATH.
+#### Entry Point { #entry_point }
+
+[Entry points] are defined in a Python package's `setup.py` script. The script
+must use [setuptools] to support entry points. Python-Markdown extensions must
+be assigned to the `markdown.extensions` group. An entry point definition might
+look like this:
+
+```python
+from setuptools import setup
+
+setup(
+ # ...
+ entry_points={
+ 'markdown.extensions': ['myextension = path.to.module:MyExtension']
+ }
+)
+```
+
+After a user installs your extension using the above script, they could then
+call the extension using the `myextension` string name like this:
+
+```python
+markdown.markdown(text, extensions=['myextention'])
+```
+
+Note that if two or more entry points within the same group are assigned the
+same name, Python-Markdown will only ever use the first one found and ignore all
+others. Therefore, be sure to give your extension a unique name.
+
+For more information on writing `setup.py` scripts, see the Python documentation
+on [Packaging and Distributing Projects].
+
+#### Dot Notation { #dot_notation }
+
+If an extension does not have a registered entry point, Python's dot notation
+may be used instead. The extension must be installed as a Python module on your
+PYTHONPATH. Generally, a class should be specified in the name. The class must
+be at the end of the name and be separated by a colon from the module.
-The string can also include the name of the class separated by a colon.
Therefore, if you were to import the class like this:
```python
-from path.to.module import SomeExtensionClass
+from path.to.module import MyExtention
```
-Then the named extension would comprise this string:
+Then the extension can be loaded as follows:
```python
-"path.to.module:SomeExtensionClass"
+markdown.markdown(text, extensions=['path.to.module:MyExtention'])
```
You do not need to do anything special to support this feature. As long as your
-extension class is able to be imported, a user can include it with the above syntax.
+extension class is able to be imported, a user can include it with the above
+syntax.
The above two methods are especially useful if you need to implement a large
number of extensions with more than one residing in a module. However, if you do
@@ -697,9 +731,9 @@ def makeExtension(**kwargs):
return MyExtension(**kwargs)
```
-When Markdown is passed the "name" of your extension as a dot notation string,
-it will import the module and call the `makeExtension` function to initiate your
-extension.
+When Markdown is passed the "name" of your extension as a dot notation string
+that does not include a class (for example `path.to.module`), it will import the
+module and call the `makeExtension` function to initiate your extension.
[Preprocessors]: #preprocessors
[Inline Patterns]: #inlinepatterns
@@ -718,3 +752,6 @@ extension.
[Footnotes]: https://github.com/Python-Markdown/mdx_footnotes
[Definition Lists]: https://github.com/Python-Markdown/mdx_definition_lists
[library reference]: ../reference.md
+[setuptools]: https://packaging.python.org/key_projects/#setuptools
+[Entry points]: https://setuptools.readthedocs.io/en/latest/setuptools.html#dynamic-discovery-of-services-and-plugins
+[Packaging and Distributing Projects]: https://packaging.python.org/tutorials/distributing-packages/
diff --git a/docs/extensions/attr_list.md b/docs/extensions/attr_list.md
index 7b2e19f..9fc8b6e 100644
--- a/docs/extensions/attr_list.md
+++ b/docs/extensions/attr_list.md
@@ -92,7 +92,7 @@ The above results in the following output:
## Usage
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.attr_list` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `attr_list` as the
+name of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/code_hilite.md b/docs/extensions/code_hilite.md
index 6490dcc..552a82d 100644
--- a/docs/extensions/code_hilite.md
+++ b/docs/extensions/code_hilite.md
@@ -168,8 +168,8 @@ Lets see the source for that:
## Usage
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.codehilite` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `codehilite` as the
+name of the extension.
See the [Library Reference](../reference.md#extensions) for information about
configuring extensions.
diff --git a/docs/extensions/definition_lists.md b/docs/extensions/definition_lists.md
index e9f8984..0d42fd0 100644
--- a/docs/extensions/definition_lists.md
+++ b/docs/extensions/definition_lists.md
@@ -46,7 +46,7 @@ the family Rosaceae.</dd>
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.def_list` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `def_list` as the
+name of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/extra.md b/docs/extensions/extra.md
index 0639d3d..3d9f374 100644
--- a/docs/extensions/extra.md
+++ b/docs/extensions/extra.md
@@ -26,7 +26,7 @@ From the Python interpreter:
```pycon
>>> import markdown
->>> html = markdown.markdown(text, ['markdown.extensions.extra'])
+>>> html = markdown.markdown(text, ['extra'])
```
There may be [additional extensions](index.md) that are distributed with
diff --git a/docs/extensions/fenced_code_blocks.md b/docs/extensions/fenced_code_blocks.md
index b7a657e..96fe786 100644
--- a/docs/extensions/fenced_code_blocks.md
+++ b/docs/extensions/fenced_code_blocks.md
@@ -110,7 +110,7 @@ The lines can be specified with PHP Extra's syntax:
## Usage
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.fenced_code` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `fenced_code` as
+the name of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/footnotes.md b/docs/extensions/footnotes.md
index aaff184..4df12a1 100644
--- a/docs/extensions/footnotes.md
+++ b/docs/extensions/footnotes.md
@@ -63,8 +63,8 @@ is indented consistently and any errors are more easily discernible by the autho
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.footnotes` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `footnotes` as the
+name of the extension.
See the [Library Reference](../reference.md#extensions) for information about
configuring extensions.
@@ -90,4 +90,3 @@ The following options are provided to configure the output:
The text string for the `title` HTML attribute of the footnote definition link.
`%d` will be replaced by the footnote number. Defaults to `Jump back to
footnote %d in the text`
-
diff --git a/docs/extensions/index.md b/docs/extensions/index.md
index 34f8084..3d98760 100644
--- a/docs/extensions/index.md
+++ b/docs/extensions/index.md
@@ -1,7 +1,6 @@
title: Extensions
-Available Extensions
-====================
+# Extensions
Python Markdown offers a flexible extension mechanism, which makes it possible
to change and/or extend the behavior of the parser without having to edit the
@@ -10,7 +9,7 @@ actual source files.
To use an extension, pass it to markdown with the `extensions` keyword.
```python
-markdown.markdown(some_text, extensions=[MyExtension(), 'path.to.my.ext', 'markdown.extensions.footnotes'])
+markdown.markdown(some_text, extensions=[MyExtClass(), 'myext', 'path.to.my.ext:MyExtClass'])
```
See the [Library Reference](../reference.md#extensions) for more details.
@@ -18,7 +17,7 @@ See the [Library Reference](../reference.md#extensions) for more details.
From the command line, specify an extension with the `-x` option.
```bash
-python -m markdown -x markdown.extensions.footnotes -x markdown.extensions.tables input.txt > output.html
+python -m markdown -x myext -x path.to.module:MyExtClass input.txt > output.html
```
See the [Command Line docs](../cli.md) or use the `--help` option for more details.
@@ -34,27 +33,26 @@ The extensions listed below are included with (at least) the most recent release
and are officially supported by Python-Markdown. Any documentation is
maintained here and all bug reports should be made to the project. If you
have a typical install of Python-Markdown, these extensions are already
-available to you using the "name" listed in the second column below.
-
-Extension | "Name"
------------------------------------- | ---------------
-[Extra] | `markdown.extensions.extra`
-&nbsp; &nbsp; [Abbreviations][] | `markdown.extensions.abbr`
-&nbsp; &nbsp; [Attribute Lists][] | `markdown.extensions.attr_list`
-&nbsp; &nbsp; [Definition Lists][] | `markdown.extensions.def_list`
-&nbsp; &nbsp; [Fenced Code Blocks][] | `markdown.extensions.fenced_code`
-&nbsp; &nbsp; [Footnotes][] | `markdown.extensions.footnotes`
-&nbsp; &nbsp; [Tables][] | `markdown.extensions.tables`
-&nbsp; &nbsp; [Smart Strong][] | `markdown.extensions.smart_strong`
-[Admonition][] | `markdown.extensions.admonition`
-[CodeHilite][] | `markdown.extensions.codehilite`
-[HeaderId] | `markdown.extensions.headerid`
-[Meta-Data] | `markdown.extensions.meta`
-[New Line to Break] | `markdown.extensions.nl2br`
-[Sane Lists] | `markdown.extensions.sane_lists`
-[SmartyPants] | `markdown.extensions.smarty`
-[Table of Contents] | `markdown.extensions.toc`
-[WikiLinks] | `markdown.extensions.wikilinks`
+available to you using the "Entry Point" name listed in the second column below.
+
+Extension | Entry Point | Dot Notation
+------------------------------------ | -------------- | ------------
+[Extra] | `extra` | `markdown.extensions.extra`
+&nbsp; &nbsp; [Abbreviations][] | `abbr` | `markdown.extensions.abbr`
+&nbsp; &nbsp; [Attribute Lists][] | `attr_list` | `markdown.extensions.attr_list`
+&nbsp; &nbsp; [Definition Lists][] | `def_list` | `markdown.extensions.def_list`
+&nbsp; &nbsp; [Fenced Code Blocks][] | `fenced_code` | `markdown.extensions.fenced_code`
+&nbsp; &nbsp; [Footnotes][] | `footnotes` | `markdown.extensions.footnotes`
+&nbsp; &nbsp; [Tables][] | `tables` | `markdown.extensions.tables`
+&nbsp; &nbsp; [Smart Strong][] | `smart_strong` | `markdown.extensions.smart_strong`
+[Admonition][] | `admonition` | `markdown.extensions.admonition`
+[CodeHilite][] | `codehilite` | `markdown.extensions.codehilite`
+[Meta-Data] | `meta` | `markdown.extensions.meta`
+[New Line to Break] | `nl2br` | `markdown.extensions.nl2br`
+[Sane Lists] | `sane_lists` | `markdown.extensions.sane_lists`
+[SmartyPants] | `smarty` | `markdown.extensions.smarty`
+[Table of Contents] | `toc` | `markdown.extensions.toc`
+[WikiLinks] | `wikilinks` | `markdown.extensions.wikilinks`
[Extra]: extra.md
[Abbreviations]: abbreviations.md
diff --git a/docs/extensions/meta_data.md b/docs/extensions/meta_data.md
index 36d5e7a..29f3e0b 100644
--- a/docs/extensions/meta_data.md
+++ b/docs/extensions/meta_data.md
@@ -57,8 +57,8 @@ by Markdown.
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.meta` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `meta` as the name
+of the extension.
Accessing the Meta-Data
-----------------------
@@ -67,7 +67,7 @@ The meta-data is made available as a python Dict in the `Meta` attribute of an
instance of the Markdown class. For example, using the above document:
```pycon
->>> md = markdown.Markdown(extensions = ['markdown.extensions.meta'])
+>>> md = markdown.Markdown(extensions = ['meta'])
>>> html = md.convert(text)
>>> # Meta-data has been stripped from output
>>> print html
diff --git a/docs/extensions/nl2br.md b/docs/extensions/nl2br.md
index 4f5e611..8c53d33 100644
--- a/docs/extensions/nl2br.md
+++ b/docs/extensions/nl2br.md
@@ -20,7 +20,7 @@ Example
... Line 1
... Line 2
... """
->>> html = markdown.markdown(text, extensions=['markdown.extensions.nl2br'])
+>>> html = markdown.markdown(text, extensions=['nl2br'])
>>> print html
<p>Line 1<br />
Line 2</p>
@@ -29,7 +29,7 @@ Line 2</p>
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.nl2br` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `nl2br` as the name
+of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/sane_lists.md b/docs/extensions/sane_lists.md
index 49a7a85..81f8d84 100644
--- a/docs/extensions/sane_lists.md
+++ b/docs/extensions/sane_lists.md
@@ -71,7 +71,7 @@ In all other ways, Sane Lists should behave as normal Markdown lists.
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.sane_lists` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `sane_lists` as the
+name of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/smart_strong.md b/docs/extensions/smart_strong.md
index 1fb3c68..65c88d6 100644
--- a/docs/extensions/smart_strong.md
+++ b/docs/extensions/smart_strong.md
@@ -19,21 +19,18 @@ Example
```pycon
>>> import markdown
->>> markdown.markdown('Text with double__underscore__words.', \
- extensions=['markdown.extensions.smart_strong'])
+>>> markdown.markdown('Text with double__underscore__words.', extensions=['smart_strong'])
u'<p>Text with double__underscore__words.</p>'
->>> markdown.markdown('__Strong__ still works.', \
- extensions=['markdown.extensions.smart_strong'])
+>>> markdown.markdown('__Strong__ still works.', extensions=['smart_strong'])
u'<p><strong>Strong</strong> still works.</p>'
->>> markdown.markdown('__this__works__too__.', \
- extensions=['markdown.extensions.smart_strong'])
+>>> markdown.markdown('__this__works__too__.', extensions=['smart_strong'])
u'<p><strong>this__works__too</strong>.</p>'
```
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.smart_strong` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `smart_strong` as
+the name of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/smarty.md b/docs/extensions/smarty.md
index 9353d9b..109d78c 100644
--- a/docs/extensions/smarty.md
+++ b/docs/extensions/smarty.md
@@ -27,7 +27,7 @@ the German language:
```python
extension_configs = {
- 'markdown.extensions.smarty': {
+ 'smarty': {
'substitutions': {
'left-single-quote': '&sbquo;', # sb is not a typo!
'right-single-quote': '&lsquo;',
@@ -53,8 +53,8 @@ extension_configs = {
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.smarty` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `smarty` as the
+name of the extension.
See the [Library Reference](../reference.md#extensions) for information about
configuring extensions.
diff --git a/docs/extensions/tables.md b/docs/extensions/tables.md
index 2bea470..59693f0 100644
--- a/docs/extensions/tables.md
+++ b/docs/extensions/tables.md
@@ -52,7 +52,7 @@ will be rendered as:
Usage
-----
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.tables` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `tables` as the
+name of the extension.
This extension does not accept any special configuration options.
diff --git a/docs/extensions/toc.md b/docs/extensions/toc.md
index a1c583f..e358132 100644
--- a/docs/extensions/toc.md
+++ b/docs/extensions/toc.md
@@ -65,7 +65,7 @@ This allows one to insert the Table of Contents elsewhere in their page
template. For example:
```pycon
->>> md = markdown.Markdown(extensions=['markdown.extensions.toc'])
+>>> md = markdown.Markdown(extensions=['toc'])
>>> html = md.convert(text)
>>> page = render_some_template(context={'body': html, 'toc': md.toc})
```
@@ -73,8 +73,8 @@ template. For example:
Usage
-----
-See [Extensions](index.md) for general extension usage, specify `markdown.extensions.toc`
-as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `toc` as the name
+of the extension.
See the [Library Reference](../reference.md#extensions) for information about
configuring extensions.
diff --git a/docs/extensions/wikilinks.md b/docs/extensions/wikilinks.md
index a46265a..dd82a96 100644
--- a/docs/extensions/wikilinks.md
+++ b/docs/extensions/wikilinks.md
@@ -46,8 +46,8 @@ becomes
## Usage
-See [Extensions](index.md) for general extension usage, specify
-`markdown.extensions.wikilinks` as the name of the extension.
+See [Extensions](index.md) for general extension usage. Use `wikilinks` as the
+name of the extension.
See the [Library Reference](../reference.md#extensions) for information about
configuring extensions.
diff --git a/docs/reference.md b/docs/reference.md
index 55fb501..1d73439 100644
--- a/docs/reference.md
+++ b/docs/reference.md
@@ -67,7 +67,7 @@ __extensions__{: #extensions }
of extension names.
:::python
- extensions=[MyExtension(), 'path.to.my.ext']
+ extensions=[MyExtClass(), 'myext', 'path.to.my.ext:MyExtClass']
!!! note
The preferred method is to pass in an instance of an extension. Strings
@@ -81,37 +81,48 @@ __extensions__{: #extensions }
:::python
from markdown.extensions import Extension
- class MyExtension(Extension):
+ class MyExtClass(Extension):
# define your extension here...
- markdown.markdown(text, extensions=[MyExtension(option='value')])
+ markdown.markdown(text, extensions=[MyExtClass(option='value')])
- 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
- required. Therefore, to import the 'extra' extension, one would do
- `extensions=['markdown.extensions.extra']`
+ If an extension name is provided as a string, the string must either be the
+ registered entry point of any installed extension or the importable path
+ using Python's dot notation.
- Additionally, a Class may be specified in the name. The class must be at the
- end of the name and be separated by a colon from the module.
+ See the documentation specific to an extension for the string name assigned
+ to an extension as an entry point. Simply include the defined name as
+ a string in the list of extensions. For example, if an extension has the
+ name `myext` assigned to it and the extension is properly installed, then
+ do the following:
+
+ :::python
+ markdown.markdown(text, extensions=['myext'])
+
+ If an extension does not have a registered entry point, Python's dot
+ notation may be used instead. The extension must be installed as a
+ Python module on your PYTHONPATH. Generally, a class should be specified in
+ the name. The class must be at the end of the name and be separated by a
+ colon from the module.
Therefore, if you were to import the class like this:
:::python
- from path.to.module import SomeExtensionClass
+ from path.to.module import MyExtClass
- Then the named extension would comprise this string:
+ Then load the extension as follows:
:::python
- "path.to.module:SomeExtensionClass"
+ markdown.markdown(text, extensions=['path.to.module:MyExtClass'])
- !!! 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. However,
- doing so will not effect the behavior of the parser.
+ If only one extension is defined within a module and the module includes a
+ `makeExtension` function which returns an instance of the extension, then
+ the class name is not necessary. For example, in that case one could do
+ `extensions=['path.to.module']`. Check the documentation for a specific
+ extension to determine if it supports this feature.
- When loading an extension by name (as a string), you may pass in
- configuration settings to the extension using the
+ When loading an extension by name (as a string), you can only pass in
+ configuration settings to the extension by using the
[`extension_configs`](#extension_configs) keyword.
!!! seealso "See Also"
@@ -144,6 +155,14 @@ __extension_configs__{: #extension_configs }
}
}
+ When specifying the extension name, be sure to use the exact same
+ string as is used in the [extensions](#extensions) keyword to load the
+ extension. Otherwise, the configuration settings will not be applied to
+ the extension. In other words, you cannot use the entry point in on
+ place and Python dot notation in the other. While both may be valid for
+ a given extension, they will not be recognized as being the same
+ extension by Markdown.
+
See the documentation specific to the extension you are using for help in
specifying configuration settings for that extension.
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'
]
diff --git a/setup.py b/setup.py
index 6f0a1e7..73c7ab3 100755
--- a/setup.py
+++ b/setup.py
@@ -59,6 +59,25 @@ setup(
entry_points={
'console_scripts': [
'%s = markdown.__main__:run' % SCRIPT_NAME,
+ ],
+ # Register the built in extensions
+ 'markdown.extensions': [
+ 'abbr = markdown.extensions.abbr:AbbrExtension',
+ 'admonition = markdown.extensions.admonition:AdmonitionExtension',
+ 'attr_list = markdown.extensions.attr_list:AttrListExtension',
+ 'codehilite = markdown.extensions.codehilite:CodeHiliteExtension',
+ 'def_list = markdown.extensions.def_list:DefListExtension',
+ 'extra = markdown.extensions.extra:ExtraExtension',
+ 'fenced_code = markdown.extensions.fenced_code:FencedCodeExtension',
+ 'footnotes = markdown.extensions.footnotes:FootnoteExtension',
+ 'meta = markdown.extensions.meta:MetaExtension',
+ 'nl2br = markdown.extensions.nl2br:Nl2BrExtension',
+ 'sane_lists = markdown.extensions.sane_lists:SaneListExtension',
+ 'smart_strong = markdown.extensions.smart_strong:SmartEmphasisExtension',
+ 'smarty = markdown.extensions.smarty:SmartyExtension',
+ 'tables = markdown.extensions.tables:TableExtension',
+ 'toc = markdown.extensions.toc:TocExtension',
+ 'wikilinks = markdown.extensions.wikilinks:WikiLinkExtension',
]
},
classifiers=[
diff --git a/tests/test_apis.py b/tests/test_apis.py
index 6a1829b..42e7496 100644
--- a/tests/test_apis.py
+++ b/tests/test_apis.py
@@ -11,7 +11,6 @@ from __future__ import unicode_literals
import unittest
import sys
import os
-import types
import markdown
import warnings
from markdown.__main__ import parse_options
@@ -46,11 +45,15 @@ class TestMarkdownBasics(unittest.TestCase):
from markdown.extensions.footnotes import FootnoteExtension
markdown.Markdown(extensions=[FootnoteExtension()])
- def testNamedExtension(self):
+ def testEntryPointExtension(self):
+ """ Test Extension loading with an entry point. """
+ markdown.Markdown(extensions=['footnotes'])
+
+ def testDotNotationExtension(self):
""" Test Extension loading with Name (`path.to.module`). """
markdown.Markdown(extensions=['markdown.extensions.footnotes'])
- def TestNamedExtensionWithClass(self):
+ def TestDotNotationExtensionWithClass(self):
""" Test Extension loading with class name (`path.to.module:Class`). """
markdown.Markdown(extensions=['markdown.extensions.footnotes:FootnoteExtension'])
@@ -343,46 +346,6 @@ class TestErrors(unittest.TestCase):
markdown.Markdown, extensions=[markdown.extensions.Extension()]
)
- def testMdxExtention(self):
- """ Test that prepending mdx_ raises a DeprecationWarning. """
- _create_fake_extension(name='fake', use_old_style=True)
- self.assertRaises(
- DeprecationWarning,
- markdown.Markdown, extensions=['fake']
- )
-
- def testShortNameExtention(self):
- """ Test that using a short name raises a DeprecationWarning. """
- self.assertRaises(
- DeprecationWarning,
- markdown.Markdown, extensions=['footnotes']
- )
-
-
-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])
- else:
- mod_name = name
- if not PY3:
- # mod_name must be bytes in Python 2.x
- mod_name = bytes(mod_name)
- ext_mod = types.ModuleType(mod_name)
-
- def makeExtension(*args, **kwargs):
- if is_wrong_type:
- return object
- else:
- return markdown.extensions.Extension(*args, **kwargs)
-
- if has_factory_func:
- ext_mod.makeExtension = makeExtension
- # Warning: this brute forces the extenson module onto the system. Either
- # this needs to be specificly overriden or a new python session needs to
- # be started to get rid of this. This should be ok in a testing context.
- sys.modules[mod_name] = ext_mod
-
class testETreeComments(unittest.TestCase):
"""
diff --git a/tests/test_extensions.py b/tests/test_extensions.py
index 5dee0fd..5a04e64 100644
--- a/tests/test_extensions.py
+++ b/tests/test_extensions.py
@@ -72,7 +72,7 @@ class TestAbbr(unittest.TestCase):
""" Test abbr extension. """
def setUp(self):
- self.md = markdown.Markdown(extensions=['markdown.extensions.abbr'])
+ self.md = markdown.Markdown(extensions=['abbr'])
def testSimpleAbbr(self):
""" Test Abbreviations. """
@@ -109,7 +109,7 @@ class TestCodeHilite(TestCaseWithAssertStartsWith):
def testBasicCodeHilite(self):
text = '\t# A Code Comment'
- md = markdown.Markdown(extensions=['markdown.extensions.codehilite'])
+ md = markdown.Markdown(extensions=['codehilite'])
if self.has_pygments:
# Pygments can use random lexer here as we did not specify the language
self.assertStartsWith('<div class="codehilite"><pre>', md.convert(text))
@@ -205,7 +205,7 @@ class TestCodeHilite(TestCaseWithAssertStartsWith):
text1 = "\t:::Python hl_lines='1'\n\t#line 1\n\t#line 2\n\t#line 3"
for text in (text0, text1):
- md = markdown.Markdown(extensions=['markdown.extensions.codehilite'])
+ md = markdown.Markdown(extensions=['codehilite'])
if self.has_pygments:
self.assertStartsWith(
'<div class="codehilite"><pre><span class="hll"',
@@ -236,7 +236,7 @@ class TestFencedCode(TestCaseWithAssertStartsWith):
""" Test fenced_code extension. """
def setUp(self):
- self.md = markdown.Markdown(extensions=['markdown.extensions.fenced_code'])
+ self.md = markdown.Markdown(extensions=['fenced_code'])
self.has_pygments = True
try:
import pygments # noqa
@@ -323,7 +323,7 @@ line 3
md = markdown.Markdown(
extensions=[
markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
- 'markdown.extensions.fenced_code'
+ 'fenced_code'
]
)
@@ -359,7 +359,7 @@ line 3
md = markdown.Markdown(
extensions=[
markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
- 'markdown.extensions.fenced_code'
+ 'fenced_code'
]
)
if self.has_pygments:
@@ -382,7 +382,7 @@ line 3
md = markdown.Markdown(
extensions=[
markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False),
- 'markdown.extensions.fenced_code'
+ 'fenced_code'
]
)
self.assertTrue('<code class="language-python">' in md.convert(text))
@@ -392,7 +392,7 @@ class TestMetaData(unittest.TestCase):
""" Test MetaData extension. """
def setUp(self):
- self.md = markdown.Markdown(extensions=['markdown.extensions.meta'])
+ self.md = markdown.Markdown(extensions=['meta'])
def testBasicMetaData(self):
""" Test basic metadata. """
@@ -459,7 +459,7 @@ class TestWikiLinks(unittest.TestCase):
""" Test Wikilinks Extension. """
def setUp(self):
- self.md = markdown.Markdown(extensions=['markdown.extensions.wikilinks'])
+ self.md = markdown.Markdown(extensions=['wikilinks'])
self.text = "Some text with a [[WikiLink]]."
def testBasicWikilinks(self):
@@ -500,9 +500,9 @@ class TestWikiLinks(unittest.TestCase):
""" Test Complex Settings. """
md = markdown.Markdown(
- extensions=['markdown.extensions.wikilinks'],
+ extensions=['wikilinks'],
extension_configs={
- 'markdown.extensions.wikilinks': [
+ 'wikilinks': [
('base_url', 'http://example.com/'),
('end_url', '.html'),
('html_class', '')
@@ -524,7 +524,7 @@ wiki_end_url: .html
wiki_html_class:
Some text with a [[WikiLink]]."""
- md = markdown.Markdown(extensions=['markdown.extensions.meta', 'markdown.extensions.wikilinks'])
+ md = markdown.Markdown(extensions=['meta', 'wikilinks'])
self.assertEqual(
md.convert(text),
'<p>Some text with a '
@@ -557,7 +557,7 @@ class TestAdmonition(unittest.TestCase):
""" Test Admonition Extension. """
def setUp(self):
- self.md = markdown.Markdown(extensions=['markdown.extensions.admonition'])
+ self.md = markdown.Markdown(extensions=['admonition'])
def testRE(self):
RE = self.md.parser.blockprocessors['admonition'].RE
@@ -574,7 +574,7 @@ class TestTOC(TestCaseWithAssertStartsWith):
""" Test TOC Extension. """
def setUp(self):
- self.md = markdown.Markdown(extensions=['markdown.extensions.toc'])
+ self.md = markdown.Markdown(extensions=['toc'])
def testMarker(self):
""" Test TOC with a Marker. """
@@ -818,7 +818,7 @@ class TestTOC(TestCaseWithAssertStartsWith):
def testWithAttrList(self):
""" Test TOC with attr_list Extension. """
- md = markdown.Markdown(extensions=['markdown.extensions.toc', 'markdown.extensions.attr_list'])
+ md = markdown.Markdown(extensions=['toc', 'attr_list'])
text = '# Header 1\n\n## Header 2 { #foo }'
self.assertEqual(
md.convert(text),
@@ -849,7 +849,7 @@ class TestTOC(TestCaseWithAssertStartsWith):
class TestSmarty(unittest.TestCase):
def setUp(self):
config = {
- 'markdown.extensions.smarty': [
+ 'smarty': [
('smart_angled_quotes', True),
('substitutions', {
'ndash': '\u2013',
@@ -865,7 +865,7 @@ class TestSmarty(unittest.TestCase):
]
}
self.md = markdown.Markdown(
- extensions=['markdown.extensions.smarty'],
+ extensions=['smarty'],
extension_configs=config
)
@@ -885,8 +885,8 @@ class TestFootnotes(unittest.TestCase):
def testBacklinkText(self):
md = markdown.Markdown(
- extensions=['markdown.extensions.footnotes'],
- extension_configs={'markdown.extensions.footnotes': {'BACKLINK_TEXT': 'back'}}
+ extensions=['footnotes'],
+ extension_configs={'footnotes': {'BACKLINK_TEXT': 'back'}}
)
text = 'paragraph[^1]\n\n[^1]: A Footnote'
self.assertEqual(
diff --git a/tests/test_legacy.py b/tests/test_legacy.py
index ddc54bb..7fca02a 100644
--- a/tests/test_legacy.py
+++ b/tests/test_legacy.py
@@ -123,80 +123,67 @@ class TestExtensions(LegacyTestCase):
location = os.path.join(parent_test_dir, 'extensions')
exclude = ['codehilite']
- attr_list = Kwargs(
- extensions=[
- 'markdown.extensions.attr_list',
- 'markdown.extensions.def_list',
- 'markdown.extensions.smarty'
- ]
- )
+ attr_list = Kwargs(extensions=['attr_list', 'def_list', 'smarty'])
- codehilite = Kwargs(extensions=['markdown.extensions.codehilite'])
+ codehilite = Kwargs(extensions=['codehilite'])
- toc = Kwargs(extensions=['markdown.extensions.toc'])
+ toc = Kwargs(extensions=['toc'])
- toc_invalid = Kwargs(extensions=['markdown.extensions.toc'])
+ toc_invalid = Kwargs(extensions=['toc'])
- toc_out_of_order = Kwargs(extensions=['markdown.extensions.toc'])
+ toc_out_of_order = Kwargs(extensions=['toc'])
toc_nested = Kwargs(
- extensions=['markdown.extensions.toc'],
- extension_configs={'markdown.extensions.toc': {'permalink': True}}
+ extensions=['toc'],
+ extension_configs={'toc': {'permalink': True}}
)
toc_nested2 = Kwargs(
- extensions=['markdown.extensions.toc'],
- extension_configs={'markdown.extensions.toc': {'permalink': "[link]"}}
+ extensions=['toc'],
+ extension_configs={'toc': {'permalink': "[link]"}}
)
- toc_nested_list = Kwargs(extensions=['markdown.extensions.toc'])
+ toc_nested_list = Kwargs(extensions=['toc'])
- wikilinks = Kwargs(extensions=['markdown.extensions.wikilinks'])
+ wikilinks = Kwargs(extensions=['wikilinks'])
- fenced_code = Kwargs(extensions=['markdown.extensions.fenced_code'])
+ fenced_code = Kwargs(extensions=['fenced_code'])
- github_flavored = Kwargs(extensions=['markdown.extensions.fenced_code'])
+ github_flavored = Kwargs(extensions=['fenced_code'])
- sane_lists = Kwargs(extensions=['markdown.extensions.sane_lists'])
+ sane_lists = Kwargs(extensions=['sane_lists'])
- nl2br_w_attr_list = Kwargs(
- extensions=[
- 'markdown.extensions.nl2br',
- 'markdown.extensions.attr_list'
- ]
- )
+ nl2br_w_attr_list = Kwargs(extensions=['nl2br', 'attr_list'])
- admonition = Kwargs(extensions=['markdown.extensions.admonition'])
+ admonition = Kwargs(extensions=['admonition'])
smarty = Kwargs(
- extensions=['markdown.extensions.smarty'],
- extension_configs={'markdown.extensions.smarty': {'smart_angled_quotes': True}}
+ extensions=['smarty'],
+ extension_configs={'smarty': {'smart_angled_quotes': True}}
)
class TestExtensionsExtra(LegacyTestCase):
location = os.path.join(parent_test_dir, 'extensions/extra')
- default_kwargs = Kwargs(extensions=['markdown.extensions.extra'])
+ default_kwargs = Kwargs(extensions=['extra'])
- loose_def_list = Kwargs(extensions=['markdown.extensions.def_list'])
+ loose_def_list = Kwargs(extensions=['def_list'])
- simple_def_lists = Kwargs(extensions=['markdown.extensions.def_list'])
+ simple_def_lists = Kwargs(extensions=['def_list'])
- abbr = Kwargs(extensions=['markdown.extensions.abbr'])
+ abbr = Kwargs(extensions=['abbr'])
- footnotes = Kwargs(extensions=['markdown.extensions.footnotes'])
+ footnotes = Kwargs(extensions=['footnotes'])
- tables = Kwargs(extensions=['markdown.extensions.tables'])
+ tables = Kwargs(extensions=['tables'])
- tables_and_attr_list = Kwargs(
- extensions=['markdown.extensions.tables', 'markdown.extensions.attr_list']
- )
+ tables_and_attr_list = Kwargs(extensions=['tables', 'attr_list'])
extra_config = Kwargs(
- extensions=['markdown.extensions.extra'],
+ extensions=['extra'],
extension_configs={
- 'markdown.extensions.extra': {
- 'markdown.extensions.footnotes': {
+ 'extra': {
+ 'footnotes': {
'PLACE_MARKER': '~~~placemarker~~~'
}
}