pip install markdown
-Or for more specific instructions, view the documentation in `docs/install.txt`
-or on the website at <https://pythonhosted.org/Markdown/install.html>.
+Or for more specific instructions, view the documentation in `docs/install.md`
+or on the website at <https://Python-Markdown.github.io/install/>.
-Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
-Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
-Copyright 2004 Manfred Stienstra (the original version)
+Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
-
-* Redistributions of source code must retain the above copyright
- notice, this list of conditions and the following disclaimer.
-* Redistributions in binary form must reproduce the above copyright
- notice, this list of conditions and the following disclaimer in the
- documentation and/or other materials provided with the distribution.
-* Neither the name of the <organization> nor the
- names of its contributors may be used to endorse or promote products
- derived from this software without specific prior written permission.
+
+* Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+* Neither the name of the Python Markdown Project nor the
+ names of its contributors may be used to endorse or promote products
+ derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE PYTHON MARKDOWN PROJECT ''AS IS'' AND ANY
EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-
setup.py
tox.ini
bin/markdown_py
-docs/_template.html
-docs/authors.txt
-docs/basic.css
-docs/change_log.txt
-docs/cli.txt
-docs/default.css
-docs/index.txt
-docs/install.txt
+docs/authors.md
+docs/cli.md
+docs/favicon.ico
+docs/index.md
+docs/install.md
docs/py.png
-docs/reference.txt
-docs/release-2.0.1.txt
-docs/release-2.0.2.txt
-docs/release-2.0.txt
-docs/release-2.1.0.txt
-docs/release-2.1.1.txt
-docs/release-2.2.0.txt
-docs/release-2.2.1.txt
-docs/release-2.3.txt
-docs/release-2.4.txt
-docs/release-2.5.txt
-docs/release-2.6.txt
-docs/siteindex.txt
-docs/test_suite.txt
-docs/extensions/abbreviations.txt
-docs/extensions/admonition.txt
-docs/extensions/api.txt
-docs/extensions/attr_list.txt
-docs/extensions/code_hilite.txt
-docs/extensions/definition_lists.txt
-docs/extensions/extra.txt
-docs/extensions/fenced_code_blocks.txt
-docs/extensions/footnotes.txt
-docs/extensions/header_id.txt
-docs/extensions/index.txt
-docs/extensions/meta_data.txt
-docs/extensions/nl2br.txt
-docs/extensions/sane_lists.txt
-docs/extensions/smart_strong.txt
-docs/extensions/smarty.txt
-docs/extensions/tables.txt
-docs/extensions/toc.txt
-docs/extensions/wikilinks.txt
+docs/reference.md
+docs/test_suite.md
+docs/change_log/index.md
+docs/change_log/release-2.0.md
+docs/change_log/release-2.1.md
+docs/change_log/release-2.2.md
+docs/change_log/release-2.3.md
+docs/change_log/release-2.4.md
+docs/change_log/release-2.5.md
+docs/change_log/release-2.6.md
+docs/extensions/abbreviations.md
+docs/extensions/admonition.md
+docs/extensions/api.md
+docs/extensions/attr_list.md
+docs/extensions/code_hilite.md
+docs/extensions/definition_lists.md
+docs/extensions/extra.md
+docs/extensions/fenced_code_blocks.md
+docs/extensions/footnotes.md
+docs/extensions/header_id.md
+docs/extensions/index.md
+docs/extensions/meta_data.md
+docs/extensions/nl2br.md
+docs/extensions/sane_lists.md
+docs/extensions/smart_strong.md
+docs/extensions/smarty.md
+docs/extensions/tables.md
+docs/extensions/toc.md
+docs/extensions/wikilinks.md
markdown/__init__.py
markdown/__main__.py
markdown/__version__.py
tests/extensions/extra/footnote_many_footnotes.txt
tests/extensions/extra/footnote_placeholder.html
tests/extensions/extra/footnote_placeholder.txt
+tests/extensions/extra/footnote_placeholder_depth.html
+tests/extensions/extra/footnote_placeholder_depth.txt
tests/extensions/extra/loose_def_list.html
tests/extensions/extra/loose_def_list.txt
tests/extensions/extra/markdown-syntax.html
--- /dev/null
+recursive-include markdown *.py
+recursive-include docs *
+recursive-include tests *.txt *.html *.py
+include setup.py
+include setup.cfg
+include tox.ini
+include makefile
+include LICENSE.md
+include README.md
+include INSTALL.md
+include MANIFEST
+include *-requirements.txt
+include mkdocs.yml
--- /dev/null
+Metadata-Version: 1.2
+Name: Markdown
+Version: 3.0
+Summary: Python implementation of Markdown.
+Home-page: https://Python-Markdown.github.io/
+Author: Waylan Limberg
+Author-email: waylan.limberg@icloud.com
+License: BSD License
+Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-3.0-py2.py3-none-any.whl
+Description:
+ This is a Python implementation of John Gruber's Markdown_.
+ It is almost completely compliant with the reference implementation,
+ though there are a few known issues. See Features_ for information
+ on what exactly is supported and what is not. Additional features are
+ supported by the `Available Extensions`_.
+
+ .. _Markdown: http://daringfireball.net/projects/markdown/
+ .. _Features: https://Python-Markdown.github.io#features
+ .. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
+
+ Support
+ =======
+
+ You may ask for help and discuss various other issues on the
+ `mailing list`_ and report bugs on the `bug tracker`_.
+
+ .. _`mailing list`: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
+ .. _`bug tracker`: http://github.com/Python-Markdown/markdown/issues
+
+Platform: UNKNOWN
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: License :: OSI Approved :: BSD License
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 2
+Classifier: Programming Language :: Python :: 2.7
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
+Classifier: Topic :: Communications :: Email :: Filters
+Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries
+Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
+Classifier: Topic :: Software Development :: Documentation
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Classifier: Topic :: Text Processing :: Filters
+Classifier: Topic :: Text Processing :: Markup :: HTML
+Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
--- /dev/null
+INSTALL.md
+LICENSE.md
+MANIFEST
+MANIFEST.in
+README.md
+doc-requirements.txt
+makefile
+mkdocs.yml
+setup.cfg
+setup.py
+test-requirements.txt
+tox.ini
+Markdown.egg-info/PKG-INFO
+Markdown.egg-info/SOURCES.txt
+Markdown.egg-info/dependency_links.txt
+Markdown.egg-info/entry_points.txt
+Markdown.egg-info/top_level.txt
+docs/authors.md
+docs/cli.md
+docs/favicon.ico
+docs/index.md
+docs/install.md
+docs/py.png
+docs/reference.md
+docs/test_tools.md
+docs/change_log/index.md
+docs/change_log/release-2.0.md
+docs/change_log/release-2.1.md
+docs/change_log/release-2.2.md
+docs/change_log/release-2.3.md
+docs/change_log/release-2.4.md
+docs/change_log/release-2.5.md
+docs/change_log/release-2.6.md
+docs/change_log/release-3.0.md
+docs/extensions/abbreviations.md
+docs/extensions/admonition.md
+docs/extensions/api.md
+docs/extensions/attr_list.md
+docs/extensions/code_hilite.md
+docs/extensions/definition_lists.md
+docs/extensions/extra.md
+docs/extensions/fenced_code_blocks.md
+docs/extensions/footnotes.md
+docs/extensions/index.md
+docs/extensions/legacy_attr.md
+docs/extensions/legacy_em.md
+docs/extensions/meta_data.md
+docs/extensions/nl2br.md
+docs/extensions/sane_lists.md
+docs/extensions/smarty.md
+docs/extensions/tables.md
+docs/extensions/toc.md
+docs/extensions/wikilinks.md
+markdown/__init__.py
+markdown/__main__.py
+markdown/blockparser.py
+markdown/blockprocessors.py
+markdown/core.py
+markdown/inlinepatterns.py
+markdown/postprocessors.py
+markdown/preprocessors.py
+markdown/serializers.py
+markdown/test_tools.py
+markdown/treeprocessors.py
+markdown/util.py
+markdown/extensions/__init__.py
+markdown/extensions/abbr.py
+markdown/extensions/admonition.py
+markdown/extensions/attr_list.py
+markdown/extensions/codehilite.py
+markdown/extensions/def_list.py
+markdown/extensions/extra.py
+markdown/extensions/fenced_code.py
+markdown/extensions/footnotes.py
+markdown/extensions/legacy_attrs.py
+markdown/extensions/legacy_em.py
+markdown/extensions/meta.py
+markdown/extensions/nl2br.py
+markdown/extensions/sane_lists.py
+markdown/extensions/smarty.py
+markdown/extensions/tables.py
+markdown/extensions/toc.py
+markdown/extensions/wikilinks.py
+tests/__init__.py
+tests/test_apis.py
+tests/test_extensions.py
+tests/test_legacy.py
+tests/basic/amps-and-angle-encoding.html
+tests/basic/amps-and-angle-encoding.txt
+tests/basic/angle-links-and-img.html
+tests/basic/angle-links-and-img.txt
+tests/basic/auto-links.html
+tests/basic/auto-links.txt
+tests/basic/backlash-escapes.html
+tests/basic/backlash-escapes.txt
+tests/basic/blockquotes-with-code-blocks.html
+tests/basic/blockquotes-with-code-blocks.txt
+tests/basic/codeblock-in-list.html
+tests/basic/codeblock-in-list.txt
+tests/basic/hard-wrapped.html
+tests/basic/hard-wrapped.txt
+tests/basic/horizontal-rules.html
+tests/basic/horizontal-rules.txt
+tests/basic/inline-html-advanced.html
+tests/basic/inline-html-advanced.txt
+tests/basic/inline-html-comments.html
+tests/basic/inline-html-comments.txt
+tests/basic/inline-html-simple.html
+tests/basic/inline-html-simple.txt
+tests/basic/links-inline.html
+tests/basic/links-inline.txt
+tests/basic/links-reference.html
+tests/basic/links-reference.txt
+tests/basic/literal-quotes.html
+tests/basic/literal-quotes.txt
+tests/basic/markdown-documentation-basics.html
+tests/basic/markdown-documentation-basics.txt
+tests/basic/markdown-syntax.html
+tests/basic/markdown-syntax.txt
+tests/basic/nested-blockquotes.html
+tests/basic/nested-blockquotes.txt
+tests/basic/ordered-and-unordered-list.html
+tests/basic/ordered-and-unordered-list.txt
+tests/basic/strong-and-em-together.html
+tests/basic/strong-and-em-together.txt
+tests/basic/tabs.html
+tests/basic/tabs.txt
+tests/basic/tidyness.html
+tests/basic/tidyness.txt
+tests/extensions/admonition.html
+tests/extensions/admonition.txt
+tests/extensions/attr_list.html
+tests/extensions/attr_list.txt
+tests/extensions/codehilite.html
+tests/extensions/codehilite.txt
+tests/extensions/fenced_code.html
+tests/extensions/fenced_code.txt
+tests/extensions/github_flavored.html
+tests/extensions/github_flavored.txt
+tests/extensions/nl2br_w_attr_list.html
+tests/extensions/nl2br_w_attr_list.txt
+tests/extensions/sane_lists.html
+tests/extensions/sane_lists.txt
+tests/extensions/smarty.html
+tests/extensions/smarty.txt
+tests/extensions/toc.html
+tests/extensions/toc.txt
+tests/extensions/toc_invalid.html
+tests/extensions/toc_invalid.txt
+tests/extensions/toc_nested.html
+tests/extensions/toc_nested.txt
+tests/extensions/toc_nested2.html
+tests/extensions/toc_nested2.txt
+tests/extensions/toc_nested_list.html
+tests/extensions/toc_nested_list.txt
+tests/extensions/toc_out_of_order.html
+tests/extensions/toc_out_of_order.txt
+tests/extensions/wikilinks.html
+tests/extensions/wikilinks.txt
+tests/extensions/extra/abbr.html
+tests/extensions/extra/abbr.txt
+tests/extensions/extra/def-in-list.html
+tests/extensions/extra/def-in-list.txt
+tests/extensions/extra/extra_config.html
+tests/extensions/extra/extra_config.txt
+tests/extensions/extra/footnote.html
+tests/extensions/extra/footnote.txt
+tests/extensions/extra/footnote_many_footnotes.html
+tests/extensions/extra/footnote_many_footnotes.txt
+tests/extensions/extra/footnote_placeholder.html
+tests/extensions/extra/footnote_placeholder.txt
+tests/extensions/extra/footnote_placeholder_depth.html
+tests/extensions/extra/footnote_placeholder_depth.txt
+tests/extensions/extra/loose_def_list.html
+tests/extensions/extra/loose_def_list.txt
+tests/extensions/extra/markdown-syntax.html
+tests/extensions/extra/markdown-syntax.txt
+tests/extensions/extra/named_markers.html
+tests/extensions/extra/named_markers.txt
+tests/extensions/extra/raw-html.html
+tests/extensions/extra/raw-html.txt
+tests/extensions/extra/simple_def-lists.html
+tests/extensions/extra/simple_def-lists.txt
+tests/extensions/extra/tables.html
+tests/extensions/extra/tables.txt
+tests/extensions/extra/tables_and_attr_list.html
+tests/extensions/extra/tables_and_attr_list.txt
+tests/misc/CRLF_line_ends.html
+tests/misc/CRLF_line_ends.txt
+tests/misc/adjacent-headers.html
+tests/misc/adjacent-headers.txt
+tests/misc/ampersand.html
+tests/misc/ampersand.txt
+tests/misc/arabic.html
+tests/misc/arabic.txt
+tests/misc/autolinks_with_asterisks.html
+tests/misc/autolinks_with_asterisks.txt
+tests/misc/autolinks_with_asterisks_russian.html
+tests/misc/autolinks_with_asterisks_russian.txt
+tests/misc/backtick-escape.html
+tests/misc/backtick-escape.txt
+tests/misc/bidi.html
+tests/misc/bidi.txt
+tests/misc/blank-block-quote.html
+tests/misc/blank-block-quote.txt
+tests/misc/blank_lines_in_codeblocks.html
+tests/misc/blank_lines_in_codeblocks.txt
+tests/misc/block_html5.html
+tests/misc/block_html5.txt
+tests/misc/block_html_attr.html
+tests/misc/block_html_attr.txt
+tests/misc/block_html_simple.html
+tests/misc/block_html_simple.txt
+tests/misc/blockquote-below-paragraph.html
+tests/misc/blockquote-below-paragraph.txt
+tests/misc/blockquote-hr.html
+tests/misc/blockquote-hr.txt
+tests/misc/blockquote.html
+tests/misc/blockquote.txt
+tests/misc/bold_links.html
+tests/misc/bold_links.txt
+tests/misc/br.html
+tests/misc/br.txt
+tests/misc/bracket_re.html
+tests/misc/bracket_re.txt
+tests/misc/brackets-in-img-title.html
+tests/misc/brackets-in-img-title.txt
+tests/misc/code-first-line.html
+tests/misc/code-first-line.txt
+tests/misc/comments.html
+tests/misc/comments.txt
+tests/misc/div.html
+tests/misc/div.txt
+tests/misc/em-around-links.html
+tests/misc/em-around-links.txt
+tests/misc/em_strong.html
+tests/misc/em_strong.txt
+tests/misc/em_strong_complex.html
+tests/misc/em_strong_complex.txt
+tests/misc/email.html
+tests/misc/email.txt
+tests/misc/escaped_chars_in_js.html
+tests/misc/escaped_chars_in_js.txt
+tests/misc/escaped_links.html
+tests/misc/escaped_links.txt
+tests/misc/funky-list.html
+tests/misc/funky-list.txt
+tests/misc/h1.html
+tests/misc/h1.txt
+tests/misc/hash.html
+tests/misc/hash.txt
+tests/misc/header-in-lists.html
+tests/misc/header-in-lists.txt
+tests/misc/headers.html
+tests/misc/headers.txt
+tests/misc/hline.html
+tests/misc/hline.txt
+tests/misc/html-comments.html
+tests/misc/html-comments.txt
+tests/misc/html.html
+tests/misc/html.txt
+tests/misc/image-2.html
+tests/misc/image-2.txt
+tests/misc/image_in_links.html
+tests/misc/image_in_links.txt
+tests/misc/ins-at-start-of-paragraph.html
+tests/misc/ins-at-start-of-paragraph.txt
+tests/misc/inside_html.html
+tests/misc/inside_html.txt
+tests/misc/japanese.html
+tests/misc/japanese.txt
+tests/misc/lazy-block-quote.html
+tests/misc/lazy-block-quote.txt
+tests/misc/link-with-parenthesis.html
+tests/misc/link-with-parenthesis.txt
+tests/misc/lists.html
+tests/misc/lists.txt
+tests/misc/lists2.html
+tests/misc/lists2.txt
+tests/misc/lists3.html
+tests/misc/lists3.txt
+tests/misc/lists4.html
+tests/misc/lists4.txt
+tests/misc/lists5.html
+tests/misc/lists5.txt
+tests/misc/lists6.html
+tests/misc/lists6.txt
+tests/misc/lists7.html
+tests/misc/lists7.txt
+tests/misc/lists8.html
+tests/misc/lists8.txt
+tests/misc/markup-inside-p.html
+tests/misc/markup-inside-p.txt
+tests/misc/mismatched-tags.html
+tests/misc/mismatched-tags.txt
+tests/misc/missing-link-def.html
+tests/misc/missing-link-def.txt
+tests/misc/more_comments.html
+tests/misc/more_comments.txt
+tests/misc/multi-line-tags.html
+tests/misc/multi-line-tags.txt
+tests/misc/multi-paragraph-block-quote.html
+tests/misc/multi-paragraph-block-quote.txt
+tests/misc/multi-test.html
+tests/misc/multi-test.txt
+tests/misc/multiline-comments.html
+tests/misc/multiline-comments.txt
+tests/misc/nested-lists.html
+tests/misc/nested-lists.txt
+tests/misc/nested-patterns.html
+tests/misc/nested-patterns.txt
+tests/misc/normalize.html
+tests/misc/normalize.txt
+tests/misc/numeric-entity.html
+tests/misc/numeric-entity.txt
+tests/misc/para-with-hr.html
+tests/misc/para-with-hr.txt
+tests/misc/php.html
+tests/misc/php.txt
+tests/misc/pre.html
+tests/misc/pre.txt
+tests/misc/raw_whitespace.html
+tests/misc/raw_whitespace.txt
+tests/misc/russian.html
+tests/misc/russian.txt
+tests/misc/smart_em.html
+tests/misc/smart_em.txt
+tests/misc/some-test.html
+tests/misc/some-test.txt
+tests/misc/span.html
+tests/misc/span.txt
+tests/misc/strong-with-underscores.html
+tests/misc/strong-with-underscores.txt
+tests/misc/stronintags.html
+tests/misc/stronintags.txt
+tests/misc/tabs-in-lists.html
+tests/misc/tabs-in-lists.txt
+tests/misc/two-spaces.html
+tests/misc/two-spaces.txt
+tests/misc/uche.html
+tests/misc/uche.txt
+tests/misc/underscores.html
+tests/misc/underscores.txt
+tests/misc/url_spaces.html
+tests/misc/url_spaces.txt
+tests/pl/Tests_2004/Amps and angle encoding.html
+tests/pl/Tests_2004/Auto links.html
+tests/pl/Tests_2004/Backslash escapes.html
+tests/pl/Tests_2004/Blockquotes with code blocks.html
+tests/pl/Tests_2004/Hard-wrapped paragraphs with list-like lines.html
+tests/pl/Tests_2004/Horizontal rules.html
+tests/pl/Tests_2004/Inline HTML (Advanced).html
+tests/pl/Tests_2004/Inline HTML (Simple).html
+tests/pl/Tests_2004/Inline HTML comments.html
+tests/pl/Tests_2004/Links, inline style.html
+tests/pl/Tests_2004/Links, reference style.html
+tests/pl/Tests_2004/Literal quotes in titles.html
+tests/pl/Tests_2004/Markdown Documentation - Basics.html
+tests/pl/Tests_2004/Markdown Documentation - Syntax.html
+tests/pl/Tests_2004/Nested blockquotes.html
+tests/pl/Tests_2004/Ordered and unordered lists.html
+tests/pl/Tests_2004/Strong and em together.html
+tests/pl/Tests_2004/Tabs.html
+tests/pl/Tests_2004/Tidyness.html
+tests/pl/Tests_2004/Yuri-Attributes.html
+tests/pl/Tests_2004/Yuri-Email.html
+tests/pl/Tests_2004/Yuri-Footnotes.html
+tests/pl/Tests_2004/Yuri-Links-in-Headers.html
+tests/pl/Tests_2007/Amps and angle encoding.html
+tests/pl/Tests_2007/Auto links.html
+tests/pl/Tests_2007/Backslash escapes.html
+tests/pl/Tests_2007/Blockquotes with code blocks.html
+tests/pl/Tests_2007/Code Blocks.html
+tests/pl/Tests_2007/Code Spans.html
+tests/pl/Tests_2007/Hard-wrapped paragraphs with list-like lines.html
+tests/pl/Tests_2007/Horizontal rules.html
+tests/pl/Tests_2007/Images.html
+tests/pl/Tests_2007/Inline HTML (Advanced).html
+tests/pl/Tests_2007/Inline HTML (Simple).html
+tests/pl/Tests_2007/Inline HTML comments.html
+tests/pl/Tests_2007/Links, inline style.html
+tests/pl/Tests_2007/Links, reference style.html
+tests/pl/Tests_2007/Links, shortcut references.html
+tests/pl/Tests_2007/Literal quotes in titles.html
+tests/pl/Tests_2007/Markdown Documentation - Basics.html
+tests/pl/Tests_2007/Markdown Documentation - Syntax.html
+tests/pl/Tests_2007/Nested blockquotes.html
+tests/pl/Tests_2007/Ordered and unordered lists.html
+tests/pl/Tests_2007/Strong and em together.html
+tests/pl/Tests_2007/Tabs.html
+tests/pl/Tests_2007/Tidyness.html
+tests/test_syntax/__init__.py
+tests/test_syntax/blocks/__init__.py
+tests/test_syntax/blocks/test_code_blocks.py
+tests/test_syntax/blocks/test_headers.py
+tests/test_syntax/blocks/test_hr.py
+tests/test_syntax/blocks/test_paragraphs.py
+tests/test_syntax/extensions/__init__.py
+tests/test_syntax/extensions/test_legacy_attrs.py
+tests/test_syntax/extensions/test_legacy_em.py
+tests/test_syntax/extensions/test_tables.py
+tests/test_syntax/inline/__init__.py
+tests/test_syntax/inline/test_images.py
+tests/test_syntax/inline/test_links.py
\ No newline at end of file
--- /dev/null
+[console_scripts]
+markdown_py = markdown.__main__:run
+
+[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
+legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension
+legacy_em = markdown.extensions.legacy_em:LegacyEmExtension
+meta = markdown.extensions.meta:MetaExtension
+nl2br = markdown.extensions.nl2br:Nl2BrExtension
+sane_lists = markdown.extensions.sane_lists:SaneListExtension
+smarty = markdown.extensions.smarty:SmartyExtension
+tables = markdown.extensions.tables:TableExtension
+toc = markdown.extensions.toc:TocExtension
+wikilinks = markdown.extensions.wikilinks:WikiLinkExtension
+
-Metadata-Version: 1.1
+Metadata-Version: 1.2
Name: Markdown
-Version: 2.6
+Version: 3.0
Summary: Python implementation of Markdown.
-Home-page: https://pythonhosted.org/Markdown/
+Home-page: https://Python-Markdown.github.io/
Author: Waylan Limberg
-Author-email: waylan.limberg [at] icloud.com
+Author-email: waylan.limberg@icloud.com
License: BSD License
-Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-2.6.tar.gz
+Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-3.0-py2.py3-none-any.whl
Description:
This is a Python implementation of John Gruber's Markdown_.
It is almost completely compliant with the reference implementation,
supported by the `Available Extensions`_.
.. _Markdown: http://daringfireball.net/projects/markdown/
- .. _Features: https://pythonhosted.org/Markdown/index.html#Features
- .. _`Available Extensions`: https://pythonhosted.org/Markdown/extensions/index.html
+ .. _Features: https://Python-Markdown.github.io#features
+ .. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
Support
=======
`mailing list`_ and report bugs on the `bug tracker`_.
.. _`mailing list`: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
- .. _`bug tracker`: http://github.com/waylan/Python-Markdown/issues
+ .. _`bug tracker`: http://github.com/Python-Markdown/markdown/issues
Platform: UNKNOWN
Classifier: Development Status :: 5 - Production/Stable
Classifier: Programming Language :: Python :: 2
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
-Classifier: Programming Language :: Python :: 3.2
-Classifier: Programming Language :: Python :: 3.3
Classifier: Programming Language :: Python :: 3.4
+Classifier: Programming Language :: Python :: 3.5
+Classifier: Programming Language :: Python :: 3.6
+Classifier: Programming Language :: Python :: 3.7
Classifier: Topic :: Communications :: Email :: Filters
Classifier: Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries
Classifier: Topic :: Internet :: WWW/HTTP :: Site Management
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: Text Processing :: Filters
Classifier: Topic :: Text Processing :: Markup :: HTML
+Requires-Python: >=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*
[Python-Markdown][]
===================
-[](https://travis-ci.org/waylan/Python-Markdown)
-[](https://coveralls.io/r/waylan/Python-Markdown?branch=master)
-[](https://pypi.python.org/pypi/Markdown#downloads)
-[](http://pypi.python.org/pypi/Markdown)
-[](http://opensource.org/licenses/BSD-3-Clause)
-
-This is a Python implementation of John Gruber's [Markdown][].
+[![Build Status][travis-button]][travis]
+[![Coverage Status][codecov-button]][codecov]
+[![Latest Version][mdversion-button]][md-pypi]
+[![Python Versions][pyversion-button]][md-pypi]
+[![BSD License][bsdlicense-button]][bsdlicense]
+[![Code of Conduct][codeofconduct-button]][Code of Conduct]
+
+[travis-button]: http://img.shields.io/travis/Python-Markdown/markdown.svg
+[travis]: https://travis-ci.org/Python-Markdown/markdown
+[codecov-button]: https://codecov.io/gh/Python-Markdown/markdown/branch/master/graph/badge.svg
+[codecov]: https://codecov.io/gh/Python-Markdown/markdown
+[mdversion-button]: http://img.shields.io/pypi/v/Markdown.svg
+[md-pypi]: http://pypi.python.org/pypi/Markdown
+[pyversion-button]: http://img.shields.io/pypi/pyversions/Markdown.svg
+[bsdlicense-button]: http://img.shields.io/badge/license-BSD-yellow.svg
+[bsdlicense]: http://opensource.org/licenses/BSD-3-Clause
+[codeofconduct-button]: https://img.shields.io/badge/code%20of%20conduct-contributor%20covenant-green.svg?style=flat-square
+[Code of Conduct]: https://github.com/Python-Markdown/markdown/blob/master/CODE_OF_CONDUCT.md
+
+This is a Python implementation of John Gruber's [Markdown][].
It is almost completely compliant with the reference implementation,
-though there are a few known issues. See [Features][] for information
-on what exactly is supported and what is not. Additional features are
+though there are a few known issues. See [Features][] for information
+on what exactly is supported and what is not. Additional features are
supported by the [Available Extensions][].
-[Python-Markdown]: https://pythonhosted.org/Markdown/
+[Python-Markdown]: https://Python-Markdown.github.io/
[Markdown]: http://daringfireball.net/projects/markdown/
-[Features]: https://pythonhosted.org/Markdown/index.html#Features
-[Available Extensions]: https://pythonhosted.org/Markdown/extensions/index.html
-
+[Features]: https://Python-Markdown.github.io#Features
+[Available Extensions]: https://Python-Markdown.github.io/extensions
Documentation
-------------
Installation and usage documentation is available in the `docs/` directory
-of the distribution and on the project website at
-<https://pythonhosted.org/Markdown/>.
+of the distribution and on the project website at
+<https://Python-Markdown.github.io/>.
+
+See the change log at <https://Python-Markdown.github.io/change_log>.
Support
-------
-You may ask for help and discuss various other issues on the [mailing list][] and report bugs on the [bug tracker][].
+You may ask for help and discuss various other issues on the [mailing list][]
+and report bugs on the [bug tracker][].
[mailing list]: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
-[bug tracker]: http://github.com/waylan/Python-Markdown/issues
+[bug tracker]: http://github.com/Python-Markdown/markdown/issues
+
+Code of Conduct
+---------------
+
+Everyone interacting in the Python-Markdown project's codebases, issue trackers,
+and mailing lists is expected to follow the [Code of Conduct].
+++ /dev/null
-#!/usr/bin/env python
-"""
-Python Markdown, the Command Line Script
-========================================
-
-This is the command line script for Python Markdown.
-
-Basic use from the command line:
-
- markdown source.txt > destination.html
-
-Run "markdown --help" to see more options.
-
-See markdown/__init__.py for information on using Python Markdown as a module.
-
-## Authors and License
-
-Started by [Manfred Stienstra](http://www.dwerg.net/). Continued and
-maintained by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan
-Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com).
-
-Contact: markdown@freewisdom.org
-
-Copyright 2007, 2008 The Python Markdown Project (v. 1.7 and later)
-Copyright 200? Django Software Foundation (OrderedDict implementation)
-Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
-Copyright 2004 Manfred Stienstra (the original version)
-
-License: BSD (see docs/LICENSE for details).
-"""
-
-if __name__ == '__main__':
- from markdown.__main__ import run
- run()
--- /dev/null
+mkdocs>=1.0
+mkdocs-nature
+++ /dev/null
-<!DOCTYPE html>
-<html>
-<head>
-<meta charset=utf-8>
-<title>%(page_title)s</title>
-<link rel="stylesheet" href="%(base)sdefault.css" type="text/css">
-</head>
-<body>
-
-<div class="related">
- <h3>Navigation</h3>
- <ul>
- <li class="right" style="margin-right: 10px">
- <a href="%(base)ssiteindex.html" title="General Index">index</a></li>
- <li class="right">
- <a href="%(next_url)s" title="%(next_title)s"
- accesskey="N">next</a> |</li>
- <li class="right">
- <a href="%(prev_url)s" title="%(prev_title)s"
- accesskey="P">previous</a> |</li>
- <li><img src="%(base)spy.png" alt=""
- style="vertical-align: middle; margin-top: -1px"/></li>
- <li><a href="%(base)sindex.html">Python Markdown v%(version)s documentation</a> »</li>
- %(crumb)s
- </ul>
-</div> <!-- .related -->
-
-<div class="document">
- <div class="documentwrapper">
- <div class="bodywrapper">
- <div class="body">
-%(body)s
- </div> <!-- .body -->
- </div> <!-- .bodywrapper -->
- </div> <!-- .documentwrapper -->
-
- <div class="sphinxsidebar">
- <div class="sphinxsidebarwrapper">
- <h3>Table Of Contents</h3>
- %(toc)s
-
- <h4>Previous topic</h4>
- <p class="topless"><a href="%(prev_url)s"
- title="previous chapter">%(prev_title)s</a></p>
- <h4>Next topic</h4>
- <p class="topless"><a href="%(next_url)s"
- title="next chapter">%(next_title)s</a></p>
- <h3>This Page</h3>
- <ul class="this-page-menu">
- <li><a href="https://github.com/waylan/Python-Markdown/issues"
- >Report a Bug</a></li>
- <li><a href="%(source)s"
- rel="nofollow">Show Source</a></li>
- </ul>
- </div> <!-- .sphinxsidebarwrapper -->
- </div> <!-- .sphinxsidebar -->
-
- <div class="clearer"></div>
-</div> <!-- .document -->
-
-<div class="related">
- <h3>Navigation</h3>
- <ul>
- <li class="right" style="margin-right: 10px">
- <a href="%(base)ssiteindex.html" title="General Index">index</a></li>
- <li class="right">
- <a href="%(next_url)s" title="%(next_title)s"
- accesskey="N">next</a> |</li>
- <li class="right">
- <a href="%(prev_url)s" title="%(prev_title)s"
- accesskey="P">previous</a> |</li>
- <li><img src="%(base)spy.png" alt=""
- style="vertical-align: middle; margin-top: -1px"/></li>
- <li><a href="%(base)sindex.html">Python Markdown v%(version)s documentation</a> »</li>
- %(crumb)s
- </ul>
-</div> <!-- .related -->
-
-<div class="footer">© 2010-2012 Python Markdown Project</div>
-</body>
-</html>
--- /dev/null
+title: Authors
+
+Primary Authors
+===============
+
+* __[Waylan Limberg](https://github.com/waylan)__
+
+ @waylan is the current maintainer of the code and has written much of the
+ current code base, included a complete refactor of the core for version 2.0.
+ He started out by authoring many of the available extensions and later was
+ asked to join Yuri, where he began fixing numerous bugs, adding
+ documentation and making general improvements to the existing code base.
+
+* __[Dmitry Shachnev](https://github.com/mitya57)__
+
+ @mitya57 joined the team after providing a number of helpful patches and has
+ been assisting with maintenance, reviewing pull requests and ticket triage.
+
+* __[Isaac Muse](https://github.com/facelessuser)__
+
+ @facelessuser joined the team after providing a number of helpful patches
+ and has been assisting with maintenance, reviewing pull requests and ticket
+ triage.
+
+* __[Yuri Takteyev](http://freewisdom.org/)__
+
+ Yuri wrote most of the code found in version 1.x while procrastinating his
+ Ph.D. Various pieces of his code still exist, most notably the basic
+ structure.
+
+* __[Manfed Stienstra](http://www.dwerg.net/)__
+
+ Manfed wrote the original version of the script and is responsible for
+ various parts of the existing code base.
+
+* __Artem Yunusov__
+
+ Artem, who as part of a 2008 GSoC project, refactored inline patterns,
+ replaced the NanoDOM with ElementTree support and made various other
+ improvements.
+
+* __David Wolever__
+
+ David refactored the extension API and made other improvements
+ as he helped to integrate Markdown into Dr.Project.
+
+Other Contributors
+==================
+
+The incomplete list of individuals below have provided patches or otherwise
+contributed to the project prior to the project being hosted on GitHub. See the
+GitHub commit log for a list of recent contributors. We would like to thank
+everyone who has contributed to the project in any way.
+
+* Eric Abrahamsen
+* Jeff Balogh
+* Sergej Chodarev
+* Chris Clark
+* Tiago Cogumbreiro
+* Kjell Magne Fauske
+* G. Clark Haynes
+* Daniel Krech
+* Steward Midwinter
+* Jack Miller
+* Neale Pickett
+* Paul Stansifer
+* John Szakmeister
+* Malcolm Tredinnick
+* Ben Wilson
+* and many others who helped by reporting bugs
+++ /dev/null
-title: Authors
-prev_title: Release Notes for v2.0
-prev_url: release-2.0.html
-next_title: Table of Contents
-next_url: siteindex.html
-
-Primary Authors
-===============
-
-[Yuri Takteyev](http://freewisdom.org/)
-
-: Yuri has written much of the current code while procrastinating his Ph.D.
-
-[Waylan Limberg](http://achinghead.com/)
-
-: Waylan is the current maintainer of the code and has written much the current
- code base, included a complete refactor of the core. He started out by
- authoring many of the available extensions and later was asked to join Yuri,
- where he began fixing numerous bugs, adding documentation and making general
- improvements to the existing code base.
-
-Artem Yunusov
-
-: Artem, who as part of a 2008 GSoC project, refactored inline patterns,
- replaced the NanoDOM with ElementTree support and made various other
- improvements.
-
-[Manfed Stienstra](http://www.dwerg.net/)
-
-: Manfed wrote the original version of the script and is responsible for
- various parts of the existing code base.
-
-David Wolever
-
-: David refactored the extension API and made other improvements
- as he helped to integrate Markdown into Dr.Project.
-
-Other Contributors
-==================
-
-The incomplete list of individuals below have provided patches or otherwise
-contributed to the project in various ways. We would like to thank everyone
-who has contributed to the project in any way.
-
-* Eric Abrahamsen
-* Jeff Balogh
-* Sergej Chodarev
-* Chris Clark
-* Tiago Cogumbreiro
-* Kjell Magne Fauske
-* G. Clark Haynes
-* Daniel Krech
-* Steward Midwinter
-* Jack Miller
-* Neale Pickett
-* Paul Stansifer
-* John Szakmeister
-* Malcolm Tredinnick
-* Ben Wilson
-* and many others who helped by reporting bugs
+++ /dev/null
-/**
- * Sphinx stylesheet -- basic theme
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-
-/* -- main layout ----------------------------------------------------------- */
-
-div.clearer {
- clear: both;
-}
-
-/* -- relbar ---------------------------------------------------------------- */
-
-div.related {
- width: 100%;
- font-size: 90%;
-}
-
-div.related h3 {
- display: none;
-}
-
-div.related ul {
- margin: 0;
- padding: 0 0 0 10px;
- list-style: none;
-}
-
-div.related li {
- display: inline;
-}
-
-div.related li.right {
- float: right;
- margin-right: 5px;
-}
-
-/* -- sidebar --------------------------------------------------------------- */
-
-div.sphinxsidebarwrapper {
- padding: 10px 5px 0 10px;
-}
-
-div.sphinxsidebar {
- float: left;
- width: 230px;
- margin-left: -100%;
- font-size: 90%;
-}
-
-div.sphinxsidebar ul {
- list-style: none;
-}
-
-div.sphinxsidebar ul ul,
-div.sphinxsidebar ul.want-points {
- margin-left: 20px;
- list-style: square;
-}
-
-div.sphinxsidebar ul ul {
- margin-top: 0;
- margin-bottom: 0;
-}
-
-div.sphinxsidebar form {
- margin-top: 10px;
-}
-
-div.sphinxsidebar input {
- border: 1px solid #98dbcc;
- font-family: sans-serif;
- font-size: 1em;
-}
-
-img {
- border: 0;
-}
-
-/* -- search page ----------------------------------------------------------- */
-
-ul.search {
- margin: 10px 0 0 20px;
- padding: 0;
-}
-
-ul.search li {
- padding: 5px 0 5px 20px;
- background-image: url(file.png);
- background-repeat: no-repeat;
- background-position: 0 7px;
-}
-
-ul.search li a {
- font-weight: bold;
-}
-
-ul.search li div.context {
- color: #888;
- margin: 2px 0 0 30px;
- text-align: left;
-}
-
-ul.keywordmatches li.goodmatch a {
- font-weight: bold;
-}
-
-/* -- index page ------------------------------------------------------------ */
-
-table.contentstable {
- width: 90%;
-}
-
-table.contentstable p.biglink {
- line-height: 150%;
-}
-
-a.biglink {
- font-size: 1.3em;
-}
-
-span.linkdescr {
- font-style: italic;
- padding-top: 5px;
- font-size: 90%;
-}
-
-/* -- general index --------------------------------------------------------- */
-
-table.indextable td {
- text-align: left;
- vertical-align: top;
-}
-
-table.indextable dl, table.indextable dd {
- margin-top: 0;
- margin-bottom: 0;
-}
-
-table.indextable tr.pcap {
- height: 10px;
-}
-
-table.indextable tr.cap {
- margin-top: 10px;
- background-color: #f2f2f2;
-}
-
-img.toggler {
- margin-right: 3px;
- margin-top: 3px;
- cursor: pointer;
-}
-
-/* -- general body styles --------------------------------------------------- */
-
-a.headerlink {
- visibility: hidden;
-}
-
-h1:hover > a.headerlink,
-h2:hover > a.headerlink,
-h3:hover > a.headerlink,
-h4:hover > a.headerlink,
-h5:hover > a.headerlink,
-h6:hover > a.headerlink,
-dt:hover > a.headerlink {
- visibility: visible;
-}
-
-div.body p.caption {
- text-align: inherit;
-}
-
-div.body td {
- text-align: left;
-}
-
-.field-list ul {
- padding-left: 1em;
-}
-
-.first {
- margin-top: 0 !important;
-}
-
-p.rubric {
- margin-top: 30px;
- font-weight: bold;
-}
-
-/* -- sidebars -------------------------------------------------------------- */
-
-div.sidebar {
- margin: 0 0 0.5em 1em;
- border: 1px solid #ddb;
- padding: 7px 7px 0 7px;
- background-color: #ffe;
- width: 40%;
- float: right;
-}
-
-p.sidebar-title {
- font-weight: bold;
-}
-
-/* -- topics ---------------------------------------------------------------- */
-
-div.topic {
- border: 1px solid #ccc;
- padding: 7px 7px 0 7px;
- margin: 10px 0 10px 0;
-}
-
-p.topic-title {
- font-size: 1.1em;
- font-weight: bold;
- margin-top: 10px;
-}
-
-/* -- admonitions ----------------------------------------------------------- */
-
-div.admonition {
- margin-top: 10px;
- margin-bottom: 10px;
- padding: 7px;
-}
-
-div.admonition dt {
- font-weight: bold;
-}
-
-div.admonition dl {
- margin-bottom: 0;
-}
-
-p.admonition-title {
- margin: 0px 10px 5px 0px;
- font-weight: bold;
-}
-
-div.body p.centered {
- text-align: center;
- margin-top: 25px;
-}
-
-/* -- tables ---------------------------------------------------------------- */
-
-table {
- border: 0 solid #dce;
- border-collapse: collapse;
-}
-
-table td, table th {
- padding: 2px 5px 2px 5px;
-}
-
-table td {
- border: 1px solid #ddd;
- background-color: #eef;
-}
-
-table td p.last, table th p.last {
- margin-bottom: 0;
-}
-
-table.field-list td, table.field-list th {
- border: 0 !important;
-}
-
-table.footnote td, table.footnote th {
- border: 0 !important;
-}
-
-table th {
- border: 1px solid #cac;
- background-color: #ede;
-}
-
-th {
- text-align: left;
- padding-right: 5px;
-}
-
-th.head {
- text-align: center;
-}
-
-/* -- other body styles ----------------------------------------------------- */
-
-dl {
- margin-bottom: 15px;
-}
-
-dd p {
- margin-top: 0px;
-}
-
-dd ul, dd table {
- margin-bottom: 10px;
-}
-
-dd {
- margin-top: 3px;
- margin-bottom: 10px;
- margin-left: 30px;
-}
-
-dt:target, .highlight {
- background-color: #fbe54e;
-}
-
-dl.glossary dt {
- font-weight: bold;
- font-size: 1.1em;
-}
-
-.field-list ul {
- margin: 0;
- padding-left: 1em;
-}
-
-.field-list p {
- margin: 0;
-}
-
-.refcount {
- color: #060;
-}
-
-.optional {
- font-size: 1.3em;
-}
-
-.versionmodified {
- font-style: italic;
-}
-
-p.deprecated {
- background-color: #ffe4e4;
- border: 1px solid #f66;
- padding: 7px
-}
-
-.system-message {
- background-color: #fda;
- padding: 5px;
- border: 3px solid red;
-}
-
-.footnote:target {
- background-color: #ffa;
-}
-
-.impl-detail {
- margin-top: 10px;
- margin-bottom: 10px;
- padding: 7px;
- border: 1px solid #ccc;
-}
-
-.impl-detail .compound-first {
- margin-top: 0;
-}
-
-.impl-detail .compound-last {
- margin-bottom: 0;
-}
-
-/* -- code displays --------------------------------------------------------- */
-
-pre {
- overflow: auto;
- overflow-y: hidden;
-}
-
-code {
- font-size: 1.1em;
-}
-
-td.linenos pre {
- padding: 5px 0px;
- border: 0;
- background-color: transparent;
- color: #aaa;
-}
-
-table.highlighttable {
- margin-left: 0.5em;
-}
-
-table.highlighttable td {
- padding: 0 0.5em 0 0.5em;
-}
-
-tt.descname {
- background-color: transparent;
- font-weight: bold;
- font-size: 1.2em;
-}
-
-tt.descclassname {
- background-color: transparent;
-}
-
-tt.xref, a tt {
- background-color: transparent;
- font-weight: bold;
-}
-
-h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
- background-color: transparent;
-}
-
-/* -- math display ---------------------------------------------------------- */
-
-img.math {
- vertical-align: middle;
-}
-
-div.body div.math p {
- text-align: center;
-}
-
-span.eqno {
- float: right;
-}
-
-/* -- printout stylesheet --------------------------------------------------- */
-
-@media print {
- div.document,
- div.documentwrapper,
- div.bodywrapper {
- margin: 0 !important;
- width: 100%;
- }
-
- div.sphinxsidebar,
- div.related,
- div.footer,
- #top-link {
- display: none;
- }
-}
+++ /dev/null
-title: Change Log
-prev_title: Test Suite
-prev_url: test_suite.html
-next_title: Release Notes for v2.6
-next_url: release-2.6.html
-
-Python-Markdown Change Log
-=========================
-
-Feb 19, 2015: Released version 2.6 ([Notes](release-2.6.html)).
-
-Nov 19, 2014: Released version 2.5.2 (a bug-fix release).
-
-Sept 26, 2014: Released version 2.5.1 (a bug-fix release).
-
-Sept 12, 2014: Released version 2.5.0 ([Notes](release-2.5.html)).
-
-Feb 16, 2014: Released version 2.4.0 ([Notes](release-2.4.html)).
-
-Mar 22, 2013: Released version 2.3.1 (a bug-fix release).
-
-Mar 14, 2013: Released version 2.3.0 ([Notes](release-2.3.html))
-
-Nov 4, 2012: Released version 2.2.1 ([Notes](release-2.2.1.html)).
-
-Jul 5, 2012: Released version 2.2.0 ([Notes](release-2.2.0.html)).
-
-Jan 22, 2012: Released version 2.1.1 ([Notes](release-2.1.1.html)).
-
-Nov 24, 2011: Released version 2.1.0 ([Notes](release-2.1.0.html)).
-
-Oct 7, 2009: Released version 2.0.3.
-
-Sept 28, 2009: Released version 2.0.2 ([Notes](release-2.0.2.html)).
-
-May 20, 2009: Released version 2.0.1 ([Notes](release-2.0.1.html)).
-
-Mar 30, 2009: Released version 2.0 ([Notes](release-2.0.html)).
-
-Mar 8, 2009: Release Candidate 2.0-rc-1.
-
-Feb 2009: Added support for multi-level lists to new Blockprocessors.
-
-Jan 2009: Added HTML 4 output as an option (thanks Eric Abrahamsen)
-
-Nov 2008: Added Definition List ext. Replaced old core with Blockprocessors.
-Broken up into multiple files.
-
-Oct 2008: Changed logging behavior to work better with other systems.
-Refactored tree traversing. Added `treap` implementation, then replaced with
-OrderedDict. Renamed various processors to better reflect what they actually
-do. Refactored footnote ext to match PHP Extra's output.
-
-Sept 2008: Moved `prettifyTree` to a Postprocessor, replaced WikiLink ext
-with WikiLinks (note the s) ext (uses bracketed links instead of CamelCase)
-and various bug fixes.
-
-August 18 2008: Reorganized directory structure. Added a 'docs' directory
-and moved all extensions into a 'markdown-extensions' package.
-Added additional documentation and a few bug fixes. (v2.0-beta)
-
-August 4 2008: Updated included extensions to ElementTree. Added a
-separate command line script. (v2.0-alpha)
-
-July 2008: Switched from home-grown NanoDOM to ElementTree and
-various related bugs (thanks Artem Yunusov).
-
-June 2008: Fixed issues with nested inline patterns and cleaned
-up testing framework (thanks Artem Yunusov).
-
-May 2008: Added a number of additional extensions to the
-distribution and other minor changes. Moved repository to git from svn.
-
-Mar 2008: Refactored extension API to accept either an
-extension name (as a string) or an instance of an extension
-(Thanks David Wolever). Fixed various bugs and added doc strings.
-
-Feb 2008: Various bug-fixes mostly regarding extensions.
-
-Feb 18, 2008: Version 1.7.
-
-Feb 13, 2008: A little code cleanup and better documentation
-and inheritance for Preprocessors/Postprocessors.
-
-Feb 9, 2008: Double-quotes no longer HTML escaped and raw HTML
-honors `<?foo>`, `<@foo>`, and `<%foo>` for those who run markdown on
-template syntax.
-
-Dec 12, 2007: Updated docs. Removed encoding argument from Markdown
-and markdown as per list discussion. Clean up in prep for 1.7.
-
-Nov 29, 2007: Added support for images inside links. Also fixed
-a few bugs in the footnote extension.
-
-Nov 19, 2007: `message` now uses python's logging module. Also removed
-limit imposed by recursion in `_process_section()`. You can now parse as
-long of a document as your memory can handle.
-
-Nov 5, 2007: Moved `safe_mode` code to a `textPostprocessor` and added
-escaping option.
-
-Nov 3, 2007: Fixed convert method to accept empty strings.
-
-Oct 30, 2007: Fixed `BOM` removal (thanks Malcolm Tredinnick). Fixed
-infinite loop in bracket regular expression for inline links.
-
-Oct 11, 2007: `LineBreaks` is now an `inlinePattern`. Fixed `HR` in
-blockquotes. Refactored `_processSection` method (see tracker #1793419).
-
-Oct 9, 2007: Added `textPreprocessor` (from 1.6b).
-
-Oct 8, 2008: Fixed Lazy Blockquote. Fixed code block on first line.
-Fixed empty inline image link.
-
-Oct 7, 2007: Limit recursion on inline patterns. Added a 'safe' tag
-to `htmlStash`.
-
-March 18, 2007: Fixed or merged a bunch of minor bugs, including
-multi-line comments and markup inside links. (Tracker #s: 1683066,
-1671153, 1661751, 1627935, 1544371, 1458139.) -> v. 1.6b
-
-Oct 10, 2006: Fixed a bug that caused some text to be lost after
-comments. Added "safe mode" (user's HTML tags are removed).
-
-Sept 6, 2006: Added exception for PHP tags when handling HTML blocks.
-
-August 7, 2006: Incorporated Sergej Chodarev's patch to fix a problem
-with ampersand normalization and HTML blocks.
-
-July 10, 2006: Switched to using `optparse`. Added proper support for
-Unicode.
-
-July 9, 2006: Fixed the `<!--@address.com>` problem (Tracker #1501354).
-
-May 18, 2006: Stopped catching unquoted titles in reference links.
-Stopped creating blank headers.
-
-May 15, 2006: A bug with lists, recursion on block-level elements,
-run-in headers, spaces before headers, Unicode input (thanks to Aaron
-Swartz). Sourceforge tracker #s: 1489313, 1489312, 1489311, 1488370,
-1485178, 1485176. (v. 1.5)
-
-Mar. 24, 2006: Switched to a not-so-recursive algorithm with
-`_handleInline`. (Version 1.4)
-
-Mar. 15, 2006: Replaced some instance variables with class variables
-(a patch from Stelios Xanthakis). Chris Clark's new regexps that do
-not trigger mid-word underlining.
-
-Feb. 28, 2006: Clean-up and command-line handling by Stewart
-Midwinter. (Version 1.3)
-
-Feb. 24, 2006: Fixed a bug with the last line of the list appearing
-again as a separate paragraph. Incorporated Chris Clark's "mail-to"
-patch. Added support for `<br />` at the end of lines ending in two or
-more spaces. Fixed a crashing bug when using `ImageReferencePattern`.
-Added several utility methods to `Nanodom`. (Version 1.2)
-
-Jan. 31, 2006: Added `hr` and `hr/` to BLOCK_LEVEL_ELEMENTS and
-changed `<hr/>` to `<hr />`. (Thanks to Sergej Chodarev.)
-
-Nov. 26, 2005: Fixed a bug with certain tabbed lines inside lists
-getting wrapped in `<pre><code>`. (v. 1.1)
-
-Nov. 19, 2005: Made `<!...`, `<?...`, etc. behave like block-level
-HTML tags.
-
-Nov. 14, 2005: Added entity code and email auto-link fix by Tiago
-Cogumbreiro. Fixed some small issues with backticks to get 100%
-compliance with John's test suite. (v. 1.0)
-
-Nov. 7, 2005: Added an `unlink` method for documents to aid with memory
-collection (per Doug Sauder's suggestion).
-
-Oct. 29, 2005: Restricted a set of HTML tags that get treated as
-block-level elements.
-
-Sept. 18, 2005: Refactored the whole script to make it easier to
-customize it and made footnote functionality into an extension.
-(v. 0.9)
-
-Sept. 5, 2005: Fixed a bug with multi-paragraph footnotes. Added
-attribute support.
-
-Sept. 1, 2005: Changed the way headers are handled to allow inline
-syntax in headers (e.g. links) and got the lists to use p-tags
-correctly (v. 0.8)
-
-Aug. 29, 2005: Added flexible tabs, fixed a few small issues, added
-basic support for footnotes. Got rid of xml.dom.minidom and added
-pretty-printing. (v. 0.7)
-
-Aug. 13, 2005: Fixed a number of small bugs in order to conform to the
-test suite. (v. 0.6)
-
-Aug. 11, 2005: Added support for inline HTML and entities, inline
-images, auto-links, underscore emphasis. Cleaned up and refactored the
-code, added some more comments.
-
-Feb. 19, 2005: Rewrote the handling of high-level elements to allow
-multi-line list items and all sorts of nesting.
-
-Feb. 3, 2005: Reference-style links, single-line lists, backticks,
-escape, emphasis in the beginning of the paragraph.
-
-Nov. 2004: Added links, blockquotes, HTML blocks to Manfred
-Stienstra's code
-
-Apr. 2004: Manfred's version at <http://www.dwerg.net/projects/markdown/>
-
--- /dev/null
+title: Change Log
+
+Python-Markdown Change Log
+=========================
+
+Sept 21, 2018: Released version 3.0 ([Notes](release-3.0.md)).
+
+Jan 4, 2018: Released version 2.6.11 (a bug-fix release). Added a new
+`BACKLINK-TITLE` option to the footnote extension so that non-English
+users can provide a custom title to back links (see #610).
+
+Dec 7, 2017: Released version 2.6.10 (a documentation update).
+
+Aug 17, 2017: Released version 2.6.9 (a bug-fix release).
+
+Jan 25, 2017: Released version 2.6.8 (a bug-fix release).
+
+Sept 23, 2016: Released version 2.6.7 (a bug-fix release).
+
+Mar 20, 2016: Released version 2.6.6 (a bug-fix release).
+
+Nov 24, 2015: Released version 2.6.5 (a bug-fix release).
+
+Nov 6, 2015: Released version 2.6.4 (a bug-fix release).
+
+Oct 26, 2015: Released version 2.6.3 (a bug-fix release).
+
+Apr 20, 2015: Released version 2.6.2 (a bug-fix release).
+
+Mar 8, 2015: Released version 2.6.1 (a bug-fix release). The (new)
+`yaml` option has been removed from the Meta-Data Extension as it was buggy
+(see [#390](https://github.com/Python-Markdown/markdown/issues/390)).
+
+Feb 19, 2015: Released version 2.6 ([Notes](release-2.6.md)).
+
+Nov 19, 2014: Released version 2.5.2 (a bug-fix release).
+
+Sept 26, 2014: Released version 2.5.1 (a bug-fix release).
+
+Sept 12, 2014: Released version 2.5.0 ([Notes](release-2.5.md)).
+
+Feb 16, 2014: Released version 2.4.0 ([Notes](release-2.4.md)).
+
+Mar 22, 2013: Released version 2.3.1 (a bug-fix release).
+
+Mar 14, 2013: Released version 2.3.0 ([Notes](release-2.3.md))
+
+Nov 4, 2012: Released version 2.2.1 (a bug-fix release).
+
+Jul 5, 2012: Released version 2.2.0 ([Notes](release-2.2.md)).
+
+Jan 22, 2012: Released version 2.1.1 (a bug-fix release).
+
+Nov 24, 2011: Released version 2.1.0 ([Notes](release-2.1.md)).
+
+Oct 7, 2009: Released version 2.0.3. (a bug-fix release).
+
+Sept 28, 2009: Released version 2.0.2 (a bug-fix release).
+
+May 20, 2009: Released version 2.0.1 (a bug-fix release).
+
+Mar 30, 2009: Released version 2.0 ([Notes](release-2.0.md)).
+
+Mar 8, 2009: Release Candidate 2.0-rc-1.
+
+Feb 2009: Added support for multi-level lists to new Blockprocessors.
+
+Jan 2009: Added HTML 4 output as an option (thanks Eric Abrahamsen)
+
+Nov 2008: Added Definition List ext. Replaced old core with Blockprocessors.
+Broken up into multiple files.
+
+Oct 2008: Changed logging behavior to work better with other systems.
+Refactored tree traversing. Added `treap` implementation, then replaced with
+OrderedDict. Renamed various processors to better reflect what they actually
+do. Refactored footnote ext to match PHP Extra's output.
+
+Sept 2008: Moved `prettifyTree` to a Postprocessor, replaced WikiLink ext
+with WikiLinks (note the s) ext (uses bracketed links instead of CamelCase)
+and various bug fixes.
+
+August 18 2008: Reorganized directory structure. Added a 'docs' directory
+and moved all extensions into a 'markdown-extensions' package.
+Added additional documentation and a few bug fixes. (v2.0-beta)
+
+August 4 2008: Updated included extensions to ElementTree. Added a
+separate command line script. (v2.0-alpha)
+
+July 2008: Switched from home-grown NanoDOM to ElementTree and
+various related bugs (thanks Artem Yunusov).
+
+June 2008: Fixed issues with nested inline patterns and cleaned
+up testing framework (thanks Artem Yunusov).
+
+May 2008: Added a number of additional extensions to the
+distribution and other minor changes. Moved repository to git from svn.
+
+Mar 2008: Refactored extension API to accept either an
+extension name (as a string) or an instance of an extension
+(Thanks David Wolever). Fixed various bugs and added doc strings.
+
+Feb 2008: Various bug-fixes mostly regarding extensions.
+
+Feb 18, 2008: Version 1.7.
+
+Feb 13, 2008: A little code cleanup and better documentation
+and inheritance for Preprocessors/Postprocessors.
+
+Feb 9, 2008: Double-quotes no longer HTML escaped and raw HTML
+honors `<?foo>`, `<@foo>`, and `<%foo>` for those who run markdown on
+template syntax.
+
+Dec 12, 2007: Updated docs. Removed encoding argument from Markdown
+and markdown as per list discussion. Clean up in prep for 1.7.
+
+Nov 29, 2007: Added support for images inside links. Also fixed
+a few bugs in the footnote extension.
+
+Nov 19, 2007: `message` now uses python's logging module. Also removed
+limit imposed by recursion in `_process_section()`. You can now parse as
+long of a document as your memory can handle.
+
+Nov 5, 2007: Moved `safe_mode` code to a `textPostprocessor` and added
+escaping option.
+
+Nov 3, 2007: Fixed convert method to accept empty strings.
+
+Oct 30, 2007: Fixed `BOM` removal (thanks Malcolm Tredinnick). Fixed
+infinite loop in bracket regular expression for inline links.
+
+Oct 11, 2007: `LineBreaks` is now an `inlinePattern`. Fixed `HR` in
+blockquotes. Refactored `_processSection` method (see tracker #1793419).
+
+Oct 9, 2007: Added `textPreprocessor` (from 1.6b).
+
+Oct 8, 2008: Fixed Lazy Blockquote. Fixed code block on first line.
+Fixed empty inline image link.
+
+Oct 7, 2007: Limit recursion on inline patterns. Added a 'safe' tag
+to `htmlStash`.
+
+March 18, 2007: Fixed or merged a bunch of minor bugs, including
+multi-line comments and markup inside links. (Tracker #s: 1683066,
+1671153, 1661751, 1627935, 1544371, 1458139.) -> v. 1.6b
+
+Oct 10, 2006: Fixed a bug that caused some text to be lost after
+comments. Added "safe mode" (user's HTML tags are removed).
+
+Sept 6, 2006: Added exception for PHP tags when handling HTML blocks.
+
+August 7, 2006: Incorporated Sergej Chodarev's patch to fix a problem
+with ampersand normalization and HTML blocks.
+
+July 10, 2006: Switched to using `optparse`. Added proper support for
+Unicode.
+
+July 9, 2006: Fixed the `<!--@address.com>` problem (Tracker #1501354).
+
+May 18, 2006: Stopped catching unquoted titles in reference links.
+Stopped creating blank headers.
+
+May 15, 2006: A bug with lists, recursion on block-level elements,
+run-in headers, spaces before headers, Unicode input (thanks to Aaron
+Swartz). Sourceforge tracker #s: 1489313, 1489312, 1489311, 1488370,
+1485178, 1485176. (v. 1.5)
+
+Mar. 24, 2006: Switched to a not-so-recursive algorithm with
+`_handleInline`. (Version 1.4)
+
+Mar. 15, 2006: Replaced some instance variables with class variables
+(a patch from Stelios Xanthakis). Chris Clark's new regexps that do
+not trigger mid-word underlining.
+
+Feb. 28, 2006: Clean-up and command-line handling by Stewart
+Midwinter. (Version 1.3)
+
+Feb. 24, 2006: Fixed a bug with the last line of the list appearing
+again as a separate paragraph. Incorporated Chris Clark's "mail-to"
+patch. Added support for `<br />` at the end of lines ending in two or
+more spaces. Fixed a crashing bug when using `ImageReferencePattern`.
+Added several utility methods to `Nanodom`. (Version 1.2)
+
+Jan. 31, 2006: Added `hr` and `hr/` to BLOCK_LEVEL_ELEMENTS and
+changed `<hr/>` to `<hr />`. (Thanks to Sergej Chodarev.)
+
+Nov. 26, 2005: Fixed a bug with certain tabbed lines inside lists
+getting wrapped in `<pre><code>`. (v. 1.1)
+
+Nov. 19, 2005: Made `<!...`, `<?...`, etc. behave like block-level
+HTML tags.
+
+Nov. 14, 2005: Added entity code and email auto-link fix by Tiago
+Cogumbreiro. Fixed some small issues with backticks to get 100%
+compliance with John's test suite. (v. 1.0)
+
+Nov. 7, 2005: Added an `unlink` method for documents to aid with memory
+collection (per Doug Sauder's suggestion).
+
+Oct. 29, 2005: Restricted a set of HTML tags that get treated as
+block-level elements.
+
+Sept. 18, 2005: Refactored the whole script to make it easier to
+customize it and made footnote functionality into an extension.
+(v. 0.9)
+
+Sept. 5, 2005: Fixed a bug with multi-paragraph footnotes. Added
+attribute support.
+
+Sept. 1, 2005: Changed the way headers are handled to allow inline
+syntax in headers (e.g. links) and got the lists to use p-tags
+correctly (v. 0.8)
+
+Aug. 29, 2005: Added flexible tabs, fixed a few small issues, added
+basic support for footnotes. Got rid of xml.dom.minidom and added
+pretty-printing. (v. 0.7)
+
+Aug. 13, 2005: Fixed a number of small bugs in order to conform to the
+test suite. (v. 0.6)
+
+Aug. 11, 2005: Added support for inline HTML and entities, inline
+images, auto-links, underscore emphasis. Cleaned up and refactored the
+code, added some more comments.
+
+Feb. 19, 2005: Rewrote the handling of high-level elements to allow
+multi-line list items and all sorts of nesting.
+
+Feb. 3, 2005: Reference-style links, single-line lists, backticks,
+escape, emphasis in the beginning of the paragraph.
+
+Nov. 2004: Added links, blockquotes, HTML blocks to Manfred
+Stienstra's code
+
+Apr. 2004: Manfred's version at <http://www.dwerg.net/projects/markdown/>
--- /dev/null
+title: Release Notes for v2.0
+
+Python-Markdown 2.0 Release Notes
+=================================
+
+We are happy to release Python-Markdown 2.0, which has been over a year in the
+making. We have rewritten significant portions of the code, dramatically
+extending the extension API, increased performance, and added numerous
+extensions to the distribution (including an extension that mimics PHP Markdown
+Extra), all while maintaining backward compatibility with the end user API in
+version 1.7.
+
+Python-Markdown supports Python versions 2.3, 2.4, 2.5, and 2.6. We have even
+released a version converted to Python 3.0!
+
+Backwards-incompatible Changes
+------------------------------
+
+While Python-Markdown has experienced numerous internal changes, those changes
+should only affect extension authors. If you have not written your own
+extensions, then you should not need to make any changes to your code.
+However, you may want to ensure that any third party extensions you are using
+are compatible with the new API.
+
+The new extension API is fully [documented](../extensions/api.md) in the docs.
+Below is a summary of the significant changes:
+
+* The old home-grown NanoDOM has been replaced with ElementTree. Therefore all
+ extensions must use ElementTree rather than the old NanoDOM.
+* The various processors and patterns are now stored with OrderedDicts rather
+ than lists. Any code adding processors and/or patterns into Python-Markdown
+ will need to be adjusted to use the new API using OrderedDicts.
+* The various types of processors available have been either combined, added,
+ or removed. Ensure that your processors match the currently supported types.
+
+What's New in Python-Markdown 2.0
+---------------------------------
+
+Thanks to the work of Artem Yunusov as part of GSoC 2008, Python-Markdown uses
+ElementTree internally to build the (X)HTML document from markdown source text.
+This has resolved various issues with the older home-grown NanoDOM and made
+notable increases in performance.
+
+Artem also refactored the Inline Patterns to better support nested patterns
+which has resolved many inconsistencies in Python-Markdown's parsing of the
+markdown syntax.
+
+The core parser had been completely rewritten, increasing performance and, for
+the first time, making it possible to override/add/change the way block level
+content is parsed.
+
+Python-Markdown now parses markdown source text more closely to the other
+popular implementations (Perl, PHP, etc.) than it ever has before. With the
+exception of a few minor insignificant differences, any difference should be
+considered a bug, rather than a limitation of the parser.
+
+The option to return HTML4 output as apposed to XHTML has been added. In
+addition, extensions should be able to easily add additional output formats.
+
+As part of implementing markdown in the Dr. Project project (a Trac fork), among
+other things, David Wolever refactored the "extension" keyword so that it
+accepts either the extension names as strings or instances of extensions. This
+makes it possible to include multiple extensions in a single module.
+
+Numerous extensions are included in the distribution by default. See
+[available_extensions](../extensions/index.md) for a complete list.
+
+See the [Change Log](index.md) for a full list of changes.
+
--- /dev/null
+title: Release Notes for v2.1
+
+Python-Markdown 2.1 Release Notes
+=================================
+
+We are pleased to release Python-Markdown 2.1 which makes many
+improvements on 2.0. In fact, we consider 2.1 to be what 2.0 should have been.
+While 2.1 consists mostly of bug fixes, bringing Python-Markdown more inline
+with other implementations, some internal improvements were made to the parser,
+a few new built-in extensions were added, and HTML5 support was added.
+
+Python-Markdown supports Python versions 2.4, 2.5, 2.6, 2.7, 3.1, and 3.2 out
+of the box. In fact, the same code base installs on Python 3.1 and 3.2 with no
+extra work by the end user.
+
+Backwards-incompatible Changes
+------------------------------
+
+While Python-Markdown has received only minor internal changes since the last
+release, there are a few backward-incompatible changes to note:
+
+* Support had been dropped for Python 2.3. No guarantees are made that the
+ library will work in any version of Python lower than 2.4. Additionally, while
+ the library had been tested with Python 2.4, consider Python 2.4 support to be
+ depreciated. It is not likely that any future versions will continue to
+ support any version of Python less than 2.5. Note that Python 3.0 is not
+ supported due to a bug in its 2to3 tool. If you must use Python-Markdown with
+ Python 3.0, it is suggested you manually use Python 3.1's 2to3 tool to do a
+ conversion.
+
+* Python-Markdown previously accepted positional arguments on its class and
+ wrapper methods. It now expects keyword arguments. Currently, the positional
+ arguments should continue to work, but the solution feels hacky and may be
+ removed in a future version. All users are encouraged to use keyword arguments
+ as documented in the [Library Reference](../reference.md).
+
+* Past versions of Python-Markdown provided module level Global variables which
+ controlled the behavior of a few different aspects of the parser. Those global
+ variables have been replaced with attributes on the Markdown class.
+ Additionally, those attributes are settable as keyword arguments when
+ initializing a class instance. Therefore, if you were editing the global
+ variables (either by editing the source or by overriding them in your code),
+ you should now set them on the class. See the [Library
+ Reference](../reference.md) for the options available.
+
+* If you have been using the HeaderId extension to
+ define custom ids on headers, you will want to switch to using the new
+ [Attribute List](../extensions/attr_list.md) extension. The HeaderId extension
+ now only auto-generates ids on headers which have not already had ids defined.
+ Note that the [Extra](../extensions/extra.md) extension has been switched to
+ use Attribute Lists instead of HeaderId as it did previously.
+
+* Some code was moved into the `markdown.util` namespace which was previously in
+ the `markdown` namespace. Extension authors may need to adjust a few import
+ statements in their extensions to work with the changes.
+
+* The command line script name was changed to `markdown_py`. The previous name
+ (`markdown`) was conflicting with people (and Linux package systems) who also
+ had markdown.pl installed on there system as markdown.pl's command line script
+ was also named `markdown`. Be aware that installing Python-Markdown 2.1 will
+ not remove the old versions of the script with different names. You may want
+ to remove them yourself as they are unlikely to work properly.
+
+What's New in Python-Markdown 2.1
+---------------------------------
+
+Three new extensions were added. [Attribute Lists](../extensions/attr_list.md),
+which was inspired by Maruku's feature of the same name, [Newline to
+Break](../extensions/nl2br.md), which was inspired by GitHub Flavored Markdown,
+and Smart Strong, which fills a hole in the Extra extension.
+
+HTML5 is now supported. All this really means is that new block level elements
+introduced in the HTML5 spec are now properly recognized as raw HTML. As
+valid HTML5 can consist of either HTML4 or XHTML1, there is no need to add a
+new HTML5 serializers. That said, `html5` and `xhtml5` have been added as
+aliases of the `html4` and `xhtml1` serializers respectively.
+
+An XHTML serializer has been added. Previously, ElementTree's XML serializer
+was being used for XHTML output. With the new serializer we are able to avoid
+more invalid output like empty elements (i.e., `<p />`) which can choke
+browsers.
+
+Improved support for Python 3.x. Now when running `setupy.py install` in
+Python 3.1 or greater the 2to3 tool is run automatically. Note that Python 3.0
+is not supported due to a bug in its 2to3 tool. If you must use Python-Markdown
+with Python 3.0, it is suggested you manually use Python 3.1's 2to3 tool to
+do a conversion.
+
+Methods on instances of the Markdown class that do not return results can now
+be changed allowing one to do `md.reset().convert(moretext)`.
+
+The Markdown class was refactored so that a subclass could define its own
+`build_parser` method which would build a completely different parser. In
+other words, one could use the basic machinery in the markdown library to
+build a parser of a different markup language without the overhead of building
+the markdown parser and throwing it away.
+
+Import statements within markdown have been improved so that third party
+libraries can embed the markdown library if they desire (licensing permitting).
+
+Added support for Python's `-m` command line option. You can run the markdown
+package as a command line script. Do `python -m markdown [options] [args]`.
+Note that this is only fully supported in Python 2.7+. Python 2.5 & 2.6
+require you to call the module directly (`markdown.__main__`) rather than
+the package (`markdown`). This does not work in Python 2.4.
+
+The command line script has been renamed to `markdown_py` which avoids all the
+various problems we had with previous names. Also improved the command line
+script to accept input on `stdin`.
+
+The testing framework has been completely rebuilt using the Nose testing
+framework. This provides a number of benefits including the ability to better
+test the built-in extensions and other options available to change the parsing
+behavior. See the Test Suite documentation for details.
+
+Various bug fixes have been made, which are too numerous to list here. See the
+[commit log](https://github.com/Python-Markdown/markdown/commits/master) for a
+complete history of the changes.
--- /dev/null
+title: Release Notes for v2.2
+
+Python-Markdown 2.2 Release Notes
+=================================
+
+We are pleased to release Python-Markdown 2.2 which makes improvements on 2.1.
+While 2.2 is primarily a bug fix release, some internal improvements were made
+to the parser, and a few security issues were resolved.
+
+Python-Markdown supports Python versions 2.5, 2.6, 2.7, 3.1, and 3.2 out
+of the box.
+
+Backwards-incompatible Changes
+------------------------------
+
+While Python-Markdown has received only minor internal changes since the last
+release, there are a few backward-incompatible changes to note:
+
+* Support had been dropped for Python 2.4. No guarantees are made that the
+ library will work in any version of Python lower than 2.5. Additionally, while
+ the library had been tested with Python 2.5, consider Python 2.5 support to be
+ depreciated. It is not likely that any future versions will continue to
+ support any version of Python less than 2.6.
+
+* For many years Python-Markdown has identified `<ins>` and `<del>` tags in raw
+ HTML input as block level tags. As they are actually inline level tags, this
+ behavior has been changed. This may result in slightly different output. While
+ in most cases, the new output is more correct, there may be a few edge cases
+ where a document author has relied on the previous incorrect behavior. It is
+ likely that a few adjustments may need to be made to those documents.
+
+* The behavior of the `enable_attributes` keyword has been slightly altered. If
+ authors have been using attributes in documents with `safe_mode` on, those
+ attributes will no longer be parsed unless `enable_attributes` is explicitly
+ set to `True`. This change was made to prevent untrusted authors from
+ injecting potentially harmful JavaScript in documents. This change had no
+ effect when not in `safe_mode`.
+
+What's New in Python-Markdown 2.2
+---------------------------------
+
+The docs were refactored and can now be found at
+<http://packages.python.org/Markdown/>. The docs are now maintained in the
+Repository and are generated by the `setup.py build_docs` command.
+
+The [Sane_Lists](../extensions/sane_lists.md)
+extension was added. The Sane Lists Extension alters the behavior of the
+Markdown List syntax to be less surprising by not allowing the mixing of list
+types. In other words, an ordered list will not continue when an unordered list
+item is encountered and vice versa.
+
+Markdown now excepts a full path to an extension module. In other words, your
+extensions no longer need to be in the primary namespace (and start with `mdx_`)
+for Markdown to find them. Just do `Markdown(extension=['path.to.some.module'])`.
+As long as the provided module contains a compatible extension, the extension
+will be loaded.
+
+The BlockParser API was slightly altered to allow `blockprocessor.run` to return
+`True` or `False` which provides more control to the block processor loop from
+within any Blockprocessor instance.
+
+Various bug fixes have been made. See the
+[commit log](https://github.com/Python-Markdown/markdown/commits/master)
+for a complete history of the changes.
--- /dev/null
+title: Release Notes for v2.3
+
+Python-Markdown 2.3 Release Notes
+=================================
+
+We are pleased to release Python-Markdown 2.3 which adds one new extension,
+removes a few old (obsolete) extensions, and now runs on both Python 2 and
+Python 3 without running the 2to3 conversion tool. See the list of changes
+below for details.
+
+Python-Markdown supports Python versions 2.6, 2.7, 3.1, 3.2, and 3.3.
+
+Backwards-incompatible Changes
+------------------------------
+
+* Support has been dropped for Python 2.5. No guarantees are made that the
+ library will work in any version of Python lower than 2.6. As all supported
+ Python versions include the ElementTree library, Python-Markdown will no
+ longer try to import a third-party installation of ElementTree.
+
+* All classes are now "new-style" classes. In other words, all classes subclass
+ from 'object'. While this is not likely to affect most users, extension
+ authors may need to make a few minor adjustments to their code.
+
+* "safe_mode" has been further restricted. Markdown formatted links must be of a
+ known white-listed scheme when in "safe_mode" or the URL is discarded. The
+ white-listed schemes are: 'HTTP', 'HTTPS', 'FTP', 'FTPS', 'MAILTO', and
+ 'news'. Schemeless URLs are also permitted, but are checked in other ways - as
+ they have been for some time.
+
+* The ids assigned to footnotes now contain a dash (`-`) rather than a colon
+ (`:`) when `output_format` it set to `"html5"` or `"xhtml5"`. If you are
+ making reference to those ids in your JavaScript or CSS and using the HTML5
+ output, you will need to update your code accordingly. No changes are
+ necessary if you are outputting XHTML (the default) or HTML4.
+
+* The `force_linenos` configuration setting of the CodeHilite extension has been
+ marked as **Pending Deprecation** and a new setting `linenums` has been added
+ to replace it. See documentation for the [CodeHilite Extension] for an
+ explanation of the new `linenums` setting. The new setting will honor the old
+ `force_linenos` if it is set, but it will raise a `PendingDeprecationWarning`
+ and will likely be removed in a future version of Python-Markdown.
+
+[CodeHilite Extension]: ../extensions/code_hilite.md
+
+* The "RSS" extension has been removed and no longer ships with Python-Markdown.
+ If you would like to continue using the extension (not recommended), it is
+ archived on [GitHub](https://gist.github.com/waylan/4773365).
+
+* The "HTML Tidy" Extension has been removed and no longer ships with
+ Python-Markdown. If you would like to continue using the extension (not
+ recommended), it is archived on
+ [GitHub](https://gist.github.com/waylan/5152650). Note that the underlying
+ library, uTidylib, is not Python 3 compatible. Instead, it is recommended that
+ the newer [PyTidyLib] (version 0.2.2+ for Python 3 comparability - install
+ from GitHub not PyPI) be used. As the API for that library is rather simple,
+ it is recommended that the output of Markdown be wrapped in a call to
+ PyTidyLib rather than using an extension (for example:
+ `tidylib.tidy_fragment(markdown.markdown(source), options={...})`).
+
+[PyTidyLib]: http://countergram.com/open-source/pytidylib
+
+What's New in Python-Markdown 2.3
+---------------------------------
+
+* The entire code base now universally runs in Python 2 and Python 3 without any
+ need for running the 2to3 conversion tool. This not only simplifies testing,
+ but by using Unicode_literals, results in more consistent behavior across
+ Python versions. Additionally, the relative imports (made possible in Python 2
+ via absolute_import) allows the entire library to more easily be embedded in a
+ sub-directory of another project. The various files within the library will
+ still import each other properly even though 'markdown' may not be in Python's
+ root namespace.
+
+* The [Admonition Extension] has been added, which implements [rST-style][rST]
+ admonitions in the Markdown syntax. However, be warned that this extension is
+ experimental and the syntax and behavior is still subject to change. Please
+ try it out and report bugs and/or improvements.
+
+[Admonition Extension]: ../extensions/admonition.md
+[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
+
+* Various bug fixes have been made. See the [commit
+ log](https://github.com/Python-Markdown/markdown/commits/master) for a
+ complete history of the changes.
--- /dev/null
+title: Release Notes for v2.4
+
+Python-Markdown 2.4 Release Notes
+=================================
+
+We are pleased to release Python-Markdown 2.4 which adds one new extension
+and fixes various bugs. See the list of changes below for details.
+
+Python-Markdown supports Python versions 2.6, 2.7, 3.1, 3.2, and 3.3.
+
+Backwards-incompatible Changes
+------------------------------
+
+* The `force_linenos` configuration setting of the CodeHilite extension has been
+ marked as **Deprecated**. It had previously been marked as "Pending
+ Deprecation" in version 2.3 when a new setting `linenums` was added to replace
+ it. See documentation for the [CodeHilite Extension] for an explanation of the
+ new `linenums` setting. The new setting will honor the old `force_linenos` if
+ it is set, but `force_linenos` will raise a `DeprecationWarning` and will
+ likely be removed in a future version of Python-Markdown.
+
+[CodeHilite Extension]: ../extensions/code_hilite.md
+
+* URLs are no longer percent-encoded. This improves compatibility with the
+ original (written in Perl) Markdown implementation. Please percent-encode your
+ URLs manually when needed.
+
+What's New in Python-Markdown 2.4
+---------------------------------
+
+* Thanks to the hard work of [Dmitry Shachnev] the [Smarty Extension] has been
+ added, which implements [SmartyPants] using Python-Markdown's Extension API.
+ This offers a few benefits over a third party script. The HTML does not need
+ to be "tokenized" twice, no hacks are required to combine SmartyPants and code
+ highlighting, and we get markdown's escaping feature for free. Please try it
+ out and report bugs and/or improvements.
+
+[Dmitry Shachnev]: https://github.com/mitya57
+[Smarty Extension]: ../extensions/smarty.md
+[SmartyPants]: http://daringfireball.net/projects/smartypants/
+
+* The [Table of Contents Extension] now supports new `permalink` option for
+ creating [Sphinx]-style anchor links.
+
+[Table of Contents Extension]: ../extensions/toc.md
+[Sphinx]: http://sphinx-doc.org/
+
+* It is now possible to enable Markdown formatting inside HTML blocks by
+ appending `markdown=1` to opening tag attributes. See [Markdown Inside HTML
+ Blocks] section for details. Thanks to [ryneeverett] for implementing this
+ feature.
+
+[Markdown Inside HTML Blocks]: ../extensions/extra.md#nested-markdown-inside-html-blocks
+[ryneeverett]: https://github.com/ryneeverett
+
+* The code blocks now support emphasizing some of the code lines. To use this
+ feature, specify `hl_lines` option after language name, for example (using the
+ [Fenced Code Extension]):
+
+ ```.python hl_lines="1 3"
+ # This line will be emphasized.
+ # This one won't.
+ # This one will be also emphasized.
+ ```
+
+ Thanks to [A. Jesse Jiryu Davis] for implementing this feature.
+
+[Fenced Code Extension]: ../extensions/fenced_code_blocks.md
+[A. Jesse Jiryu Davis]: https://github.com/ajdavis
+
+* Various bug fixes have been made. See the [commit
+ log](https://github.com/Python-Markdown/markdown/commits/master) for a
+ complete history of the changes.
--- /dev/null
+title: Release Notes for v2.5
+
+Python-Markdown 2.5 Release Notes
+=================================
+
+We are pleased to release Python-Markdown 2.5 which adds a few new features
+and fixes various bugs. See the list of changes below for details.
+
+Python-Markdown version 2.5 supports Python versions 2.7, 3.2, 3.3, and 3.4.
+
+Backwards-incompatible Changes
+------------------------------
+
+* Python-Markdown no longer supports Python version 2.6. You must be using Python
+ versions 2.7, 3.2, 3.3, or 3.4.
+
+[importlib]: https://pypi.python.org/pypi/importlib
+
+* The `force_linenos` configuration key on the [CodeHilite Extension] has been **deprecated**
+ and will raise a `KeyError` if provided. In the previous release (2.4), it was
+ issuing a `DeprecationWarning`. The [`linenums`][linenums] keyword should be used
+ instead, which provides more control of the output.
+
+[CodeHilite Extension]: ../extensions/code_hilite.md
+[linenums]: ../extensions/code_hilite.md#usage
+
+* Both `safe_mode` and the associated `html_replacement_text` keywords will be
+ deprecated in version 2.6 and will raise a **`PendingDeprecationWarning`** in
+ 2.5. The so-called "safe mode" was never actually "safe" which has resulted in
+ many people having a false sense of security when using it. As an alternative,
+ the developers of Python-Markdown recommend that any untrusted content be
+ passed through an HTML sanitizer (like [Bleach]) after being converted to HTML
+ by markdown.
+
+ If your code previously looked like this:
+
+ html = markdown.markdown(text, same_mode=True)
+
+ Then it is recommended that you change your code to read something like this:
+
+ import bleach
+ html = bleach.clean(markdown.markdown(text))
+
+ If you are not interested in sanitizing untrusted text, but simply desire to
+ escape raw HTML, then that can be accomplished through an extension which
+ removes HTML parsing:
+
+ from markdown.extensions import Extension
+
+ class EscapeHtml(Extension):
+ def extendMarkdown(self, md, md_globals):
+ del md.preprocessors['html_block']
+ del md.inlinePatterns['html']
+
+ html = markdown.markdown(text, extensions=[EscapeHtml()])
+
+ As the HTML would not be parsed with the above Extension, then the
+ serializer will escape the raw HTML, which is exactly what happens now when
+ `safe_mode="escape"`.
+
+[Bleach]: https://bleach.readthedocs.io/
+
+* Positional arguments on the `markdown.Markdown()` are pending deprecation as are
+ all except the `text` argument on the `markdown.markdown()` wrapper function.
+ Only keyword arguments should be used. For example, if your code previously
+ looked like this:
+
+ html = markdown.markdown(text, ['extra'])
+
+ Then it is recommended that you change it to read something like this:
+
+ html = markdown.markdown(text, extensions=['extra'])
+
+ !!! Note
+ This change is being made as a result of deprecating `"safe_mode"` as the
+ `safe_mode` argument was one of the positional arguments. When that argument
+ is removed, the two arguments following it will no longer be at the correct
+ position. It is recommended that you always use keywords when they are supported
+ for this reason.
+
+* In previous versions of Python-Markdown, the built-in extensions received
+ special status and did not require the full path to be provided. Additionally,
+ 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.md#extensions) for a full explanation
+ of the current behavior.
+
+* The previously documented method of appending the extension configuration 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.md#extension_configs) keyword should
+ be used instead. See the [documentation](../reference.md#extension-configs)
+ for a full explanation of the current behavior.
+
+What's New in Python-Markdown 2.5
+---------------------------------
+
+* The [Smarty Extension] has had a number of additional configuration settings
+ added, which allows one to define their own substitutions to better support
+ languages other than English. Thanks to [Martin Altmayer] for implementing this
+ feature.
+
+[Smarty Extension]: ../extensions/smarty.md
+[Martin Altmayer]:https://github.com/MartinAltmayer
+
+* Named Extensions (strings passed to the [`extensions`][ex] keyword of
+ `markdown.Markdown`) can now point to any module and/or Class on your
+ PYTHONPATH. While dot notation was previously supported, a module could not
+ be at the root of your PYTHONPATH. The name had to contain at least one dot
+ (requiring it to be a sub-module). This restriction no longer exists.
+
+ Additionally, a Class may be specified in the name. The class must be at the
+ end of the name (which uses dot notation from PYTHONPATH) and be separated
+ by a colon from the module.
+
+ 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"
+
+ This allows multiple extensions to be implemented within the same module and
+ still accessible when the user is not able to import the extension directly
+ (perhaps from a template filter or the command line).
+
+ This also means that extension modules are no longer required to include the
+ `makeExtension` function which returns an instance of the extension class.
+ However, if the user does not specify the class name (she only provides
+ `"path.to.module"`) the extension will fail to load without the
+ `makeExtension` function included in the module. Extension authors will want
+ to document carefully what is required to load their extensions.
+
+[ex]: ../reference.md#extensions
+
+* The Extension Configuration code has been refactored to make it a little
+ easier for extension authors to work with configuration settings. As a
+ result, the [`extension_configs`][ec] keyword now accepts a dictionary
+ rather than requiring a list of tuples. A list of tuples is still supported
+ so no one needs to change their existing code. This should also simplify the
+ learning curve for new users.
+
+ Extension authors are encouraged to review the new methods available on the
+ `markdown.extnesions.Extension` class for handling configuration and adjust
+ their code going forward. The included extensions provide a model for best
+ practices. See the [API] documentation for a full explanation.
+
+[ec]: ../reference.md#extension_configs
+[API]: ../extensions/api.md#configsettings
+
+* The [Command Line Interface][cli] now accepts a `--extensions_config` (or
+ `-c`) option which accepts a file name and passes the parsed content of a
+ [YAML] or [JSON] file to the [`extension_configs`][ec] keyword of the
+ `markdown.Markdown` class. The contents of the YAML or JSON must map to a
+ Python Dictionary which matches the format required by the
+ `extension_configs` keyword. Note that [PyYAML] is required to parse YAML
+ files.
+
+[cli]: ../cli.md#using-extensions
+[YAML]: http://yaml.org/
+[JSON]: http://json.org/
+[PyYAML]: http://pyyaml.org/
+
+* The [Admonition Extension][ae] is no longer considered "experimental."
+
+[ae]: ../extensions/admonition.md
+
+* There have been various refactors of the testing framework. While those
+ changes will not directly effect end users, the code is being better tested
+ which will benefit everyone.
+
+* Various bug fixes have been made. See the [commit
+ log](https://github.com/Python-Markdown/markdown/commits/master) for a
+ complete history of the changes.
--- /dev/null
+title: Release Notes for v2.6
+
+# Python-Markdown 2.6 Release Notes
+
+We are pleased to release Python-Markdown 2.6 which adds a few new features
+and fixes various bugs. See the list of changes below for details.
+
+Python-Markdown version 2.6 supports Python versions 2.7, 3.2, 3.3, and 3.4 as
+well as PyPy.
+
+## Backwards-incompatible Changes
+
+### `safe_mode` Deprecated
+
+Both `safe_mode` and the associated `html_replacement_text` keywords are
+deprecated in version 2.6 and will raise a **`DeprecationWarning`**. The
+`safe_mode` and `html_replacement_text` keywords will be ignored in the next
+release. The so-called "safe mode" was never actually "safe" which has resulted
+in many people having a false sense of security when using it. As an
+alternative, the developers of Python-Markdown recommend that any untrusted
+content be passed through an HTML sanitizer (like [Bleach]) after being
+converted to HTML by markdown. In fact, [Bleach Whitelist] provides a curated
+list of tags, attributes, and styles suitable for filtering user-provided HTML
+using bleach.
+
+If your code previously looked like this:
+
+```python
+html = markdown.markdown(text, safe_mode=True)
+```
+
+Then it is recommended that you change your code to read something like this:
+
+```python
+import bleach
+from bleach_whitelist import markdown_tags, markdown_attrs
+html = bleach.clean(markdown.markdown(text), markdown_tags, markdown_attrs)
+```
+
+If you are not interested in sanitizing untrusted text, but simply desire to
+escape raw HTML, then that can be accomplished through an extension which
+removes HTML parsing:
+
+```python
+from markdown.extensions import Extension
+
+class EscapeHtml(Extension):
+ def extendMarkdown(self, md, md_globals):
+ del md.preprocessors['html_block']
+ del md.inlinePatterns['html']
+
+html = markdown.markdown(text, extensions=[EscapeHtml()])
+```
+
+As the HTML would not be parsed with the above Extension, then the serializer
+will escape the raw HTML, which is exactly what happens now when
+`safe_mode="escape"`.
+
+[Bleach]: https://bleach.readthedocs.io/
+[Bleach Whitelist]: https://github.com/yourcelf/bleach-whitelist
+
+### Positional Arguments Deprecated
+
+Positional arguments on the `markdown.Markdown()` class are deprecated as are
+all except the `text` argument on the `markdown.markdown()` wrapper function.
+Using positional arguments will raise a **`DeprecationWarning`** in 2.6 and an
+error in the next release. Only keyword arguments should be used. For example,
+if your code previously looked like this:
+
+```python
+html = markdown.markdown(text, [SomeExtension()])
+```
+
+Then it is recommended that you change it to read something like this:
+
+```python
+html = markdown.markdown(text, extensions=[SomeExtension()])
+```
+
+!!! Note
+ This change is being made as a result of deprecating `"safe_mode"` as the
+ `safe_mode` argument was one of the positional arguments. When that argument
+ is removed, the two arguments following it will no longer be at the correct
+ position. It is recommended that you always use keywords when they are
+ supported for this reason.
+
+### "Shortened" Extension Names Deprecated
+
+In previous versions of Python-Markdown, the built-in extensions received
+special status and did not require the full path to be provided. Additionally,
+third party extensions whose name started with `"mdx_"` received the same
+special treatment. This behavior is deprecated and will raise a
+**`DeprecationWarning`** in version 2.6 and an error in the next release. Ensure
+that you always use the full path to your extensions. For example, if you
+previously did the following:
+
+```python
+markdown.markdown(text, extensions=['extra'])
+```
+
+You should change your code to the following:
+
+```python
+markdown.markdown(text, extensions=['markdown.extensions.extra'])
+```
+
+The same applies to the command line:
+
+```python
+python -m markdown -x markdown.extensions.extra input.txt
+```
+
+Similarly, if you have used a third party extension (for example `mdx_math`),
+previously you might have called it like this:
+
+```python
+markdown.markdown(text, extensions=['math'])
+```
+
+As the `"mdx"` prefix will no longer be appended, you will need to change your
+code as follows (assuming the file `mdx_math.py` is installed at the root of
+your PYTHONPATH):
+
+```python
+markdown.markdown(text, extensions=['mdx_math'])
+```
+
+Extension authors will want to update their documentation to reflect the new
+behavior.
+
+See the [documentation](../reference.md#extensions) for a full explanation
+of the current behavior.
+
+### Extension Configuration as Part of Extension Name Deprecated
+
+The previously documented method of appending the extension configuration
+options as a string to the extension name is deprecated and will raise a
+**`DeprecationWarning`** in version 2.6 and an error in 2.7. The
+[`extension_configs`](../reference.md#extension_configs) keyword should be used
+instead. See the [documentation](../reference.md#extension-configs) for a full
+explanation of the current behavior.
+
+### HeaderId Extension Pending Deprecation
+
+The HeaderId Extension is pending deprecation and will raise a
+**`PendingDeprecationWarning`** in version 2.6. The extension will be deprecated
+in the next release and raise an error in the release after that. Use the [Table
+of Contents][TOC] Extension instead, which offers most of the features of the
+HeaderId Extension and more (support for meta data is missing).
+
+Extension authors who have been using the `slugify` and `unique` functions
+defined in the HeaderId Extension should note that those functions are now
+defined in the Table of Contents extension and should adjust their import
+statements accordingly (`from markdown.extensions.toc import slugify, unique`).
+
+### The `configs` Keyword is Deprecated
+
+Positional arguments and the `configs` keyword on the
+`markdown.extension.Extension` class (and its subclasses) are deprecated. Each
+individual configuration option should be passed to the class as a keyword/value
+pair. For example. one might have previously initiated an extension subclass
+like this:
+
+```python
+ext = SomeExtension(configs={'somekey': 'somevalue'})
+```
+
+That code should be updated to pass in the options directly:
+
+```python
+ext = SomeExtension(somekey='somevalue')
+```
+
+Extension authors will want to note that this affects the `makeExtension`
+function as well. Previously it was common for the function to be defined as
+follows:
+
+```python
+def makeExtension(configs=None):
+ return SomeExtension(configs=configs)
+```
+
+Extension authors will want to update their code to the following instead:
+
+```python
+def makeExtension(**kwargs):
+ return SomeExtension(**kwargs)
+```
+
+Failing to do so will result in a **`DeprecationWarning`** and will raise an
+error in the next release. See the [Extension API][mext] documentation for more
+information.
+
+In the event that an `markdown.extension.Extension` subclass overrides the
+`__init__` method and implements its own configuration handling, then the above
+may not apply. However, it is recommended that the subclass still calls the
+parent `__init__` method to handle configuration options like so:
+
+```python
+class SomeExtension(markdown.extension.Extension):
+ def __init__(**kwargs):
+ # Do pre-config stuff here
+ # Set config defaults
+ self.config = {
+ 'option1' : ['value1', 'description1'],
+ 'option2' : ['value2', 'description2']
+ }
+ # Set user defined configs
+ super(MyExtension, self).__init__(**kwargs)
+ # Do post-config stuff here
+```
+
+Note the call to `super` to get the benefits of configuration handling from the
+parent class. See the [documentation][config] for more information.
+
+[config]: ../extensions/api.md#configsettings
+[mext]: ../extensions/api.md#makeextension
+
+## What's New in Python-Markdown 2.6
+
+### Official Support for PyPy
+
+Official support for [PyPy] has been added. While Python-Markdown has most
+likely worked on PyPy for some time, it is now officially supported and tested
+on PyPy.
+
+[PyPy]: http://pypy.org/
+
+### YAML Style Meta-Data
+
+<del>The [Meta-Data] Extension now includes optional support for [YAML] style
+meta-data.</del> By default, the YAML deliminators are recognized, however, the
+actual data is parsed as previously. This follows the syntax of [MultiMarkdown],
+which inspired this extension.
+
+<del>Alternatively, if the `yaml` option is set, then the data is parsed as
+YAML.</del> <ins>As the `yaml` option was buggy, it was removed in 2.6.1. It is
+suggested that a preprocessor (like [docdata]) or a third party extension be
+used if you want true YAML support. See [Issue #390][#390] for a full
+explanation.</ins>
+
+[MultiMarkdown]: http://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata
+[Meta-Data]: ../extensions/meta_data.md
+[YAML]: http://yaml.org/
+[#390]: https://github.com/Python-Markdown/markdown/issues/390
+[docdata]: https://github.com/waylan/docdata
+
+### Table of Contents Extension Refactored
+
+The [Table of Contents][TOC] Extension has been refactored and some new features
+have been added. See the documentation for a full explanation of each feature
+listed below:
+
+* The extension now assigns the Table of Contents to the `toc` attribute of
+ the Markdown class regardless of whether a "marker" was found in the
+ document. Third party frameworks no longer need to insert a "marker," run
+ the document through Markdown, then extract the Table of Contents from the
+ document.
+
+* The Table of Contents Extension is now a "registered extension." Therefore,
+ when the `reset` method of the Markdown class is called, the `toc` attribute
+ on the Markdown class is cleared (set to an empty string).
+
+* When the `marker` configuration option is set to an empty string, the parser
+ completely skips the process of searching the document for markers. This
+ should save parsing time when the Table of Contents Extension is being used
+ only to assign ids to headers.
+
+* A `separator` configuration option has been added allowing users to override
+ the separator character used by the slugify function.
+
+* A `baselevel` configuration option has been added allowing users to set the
+ base level of headers in their documents (h1-h6). This allows the header
+ levels to be automatically adjusted to fit within the hierarchy of an HTML
+ template.
+
+[TOC]: ../extensions/toc.md
+
+### Pygments can now be disabled
+
+The [CodeHilite][ch] Extension has gained a new configuration option:
+`use_pygments`. The option is `True` by default, however, it allows one to turn
+off Pygments code highlighting (set to `False`) while preserving the language
+detection features of the extension. Note that Pygments language guessing is not
+used as that would 'use Pygments'. If a language is defined for a code block, it
+will be assigned to the `<code>` tag as a class in the manner suggested by the
+[HTML5 spec][spec] (alternate output will not be entertained) and could
+potentially be used by a JavaScript library in the browser to highlight the code
+block.
+
+[ch]: ../extensions/code_hilite.md
+[spec]: http://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
+
+### Miscellaneous
+
+Test coverage has been improved including running [flake8]. While those changes
+will not directly effect end users, the code is being better tested which will
+benefit everyone.
+
+[flake8]: https://flake8.readthedocs.io/en/latest/
+
+Various bug fixes have been made. See the
+[commit log](https://github.com/Python-Markdown/markdown/commits/master)
+for a complete history of the changes.
--- /dev/null
+title: Release Notes for v3.0
+
+# Python-Markdown 3.0 Release Notes
+
+We are pleased to release Python-Markdown 3.0 which adds a few new features and
+fixes various bugs and deprecates various old features. See the list of changes
+below for details.
+
+Python-Markdown version 3.0 supports Python versions 2.7, 3.4, 3.5, 3.6, 3.7,
+PyPy and PyPy3.
+
+## Backwards-incompatible changes
+
+### `enable_attributes` keyword deprecated
+
+The `enable_attributes` keyword is deprecated in version 3.0 and will be
+ignored. Previously the keyword was `True` by default and enabled an
+undocumented way to define attributes on document elements. The feature has been
+removed from version 3.0. As most users did not use the undocumented feature, it
+should not affect most users. For the few who did use the feature, it can be
+enabled by using the [Legacy Attributes](../extensions/legacy_attr.md)
+extension.
+
+### `smart_emphasis` keyword and `smart_strong` extension deprecated
+
+The `smart_emphasis` keyword is deprecated in version 3.0 and will be ignored.
+Previously the keyword was `True` by default and caused the parser to ignore
+middle-word emphasis. Additionally, the optional `smart_strong` extension
+provided the same behavior for strong emphasis. Both of those features are now
+part of the default behavior, and the [Legacy
+Emphasis](../extensions/legacy_em.md) extension is available to disable that
+behavior.
+
+### `output_formats` simplified to `html` and `xhtml`.
+
+The `output_formats` keyword now only accepts two options: `html` and `xhtml`
+Note that if `(x)html1`, `(x)html4` or `(x)html5` are passed in, the number is
+stripped and ignored.
+
+### `safe_mode` and `html_replacement_text` keywords deprecated
+
+Both `safe_mode` and the associated `html_replacement_text` keywords are
+deprecated in version 3.0 and will be ignored. The so-called "safe mode" was
+never actually "safe" which has resulted in many people having a false sense of
+security when using it. As an alternative, the developers of Python-Markdown
+recommend that any untrusted content be passed through an HTML sanitizer (like
+[Bleach]) after being converted to HTML by markdown. In fact, [Bleach Whitelist]
+provides a curated list of tags, attributes, and styles suitable for filtering
+user-provided HTML using bleach.
+
+If your code previously looked like this:
+
+```python
+html = markdown.markdown(text, safe_mode=True)
+```
+
+Then it is recommended that you change your code to read something like this:
+
+```python
+import bleach
+from bleach_whitelist import markdown_tags, markdown_attrs
+html = bleach.clean(markdown.markdown(text), markdown_tags, markdown_attrs)
+```
+
+If you are not interested in sanitizing untrusted text, but simply desire to
+escape raw HTML, then that can be accomplished through an extension which
+removes HTML parsing:
+
+```python
+from markdown.extensions import Extension
+
+class EscapeHtml(Extension):
+ def extendMarkdown(self, md):
+ md.preprocessors.deregister('html_block')
+ md.inlinePatterns.deregister('html')
+
+html = markdown.markdown(text, extensions=[EscapeHtml()])
+```
+
+As the HTML would not be parsed with the above Extension, then the serializer
+will escape the raw HTML, which is exactly what happened in previous versions
+with `safe_mode="escape"`.
+
+[Bleach]: https://bleach.readthedocs.io/
+[Bleach Whitelist]: https://github.com/yourcelf/bleach-whitelist
+
+### Positional arguments deprecated
+
+Positional arguments on the `markdown.Markdown()` class are deprecated as are
+all except the `text` argument on the `markdown.markdown()` wrapper function.
+Using positional arguments will raise an error. Only keyword arguments should be
+used. For example, if your code previously looked like this:
+
+```python
+html = markdown.markdown(text, [SomeExtension()])
+```
+
+Then it is recommended that you change it to read something like this:
+
+```python
+html = markdown.markdown(text, extensions=[SomeExtension()])
+```
+
+!!! Note
+ This change is being made as a result of deprecating `"safe_mode"` as the
+ `safe_mode` argument was one of the positional arguments. When that argument
+ is removed, the two arguments following it will no longer be at the correct
+ position. It is recommended that you always use keywords when they are
+ supported for this reason.
+
+### Extension name behavior has changed
+
+In previous versions of Python-Markdown, the built-in extensions received
+special status and did not require the full path to be provided. Additionally,
+third party extensions whose name started with `"mdx_"` received the same
+special treatment. This is no longer the case.
+
+Support has been added for extensions to define an [entry
+point](../extensions/api.md#entry_point). An entry point is a string name which
+can be used to point to an `Extension` class. The built-in extensions now have
+entry points which match the old short names. And any third-party extensions
+which define entry points can now get the same behavior. See the documentation
+for each specific extension to find the assigned name.
+
+If an extension does not define an entry point, then the full path to the
+extension must be used. See the [documentation](../reference.md#extensions) for
+a full explanation of the current behavior.
+
+### Extension configuration as part of extension name deprecated
+
+The previously documented method of appending the extension configuration
+options as a string to the extension name is deprecated and will raise an error.
+The [`extension_configs`](../reference.md#extension_configs) keyword should be
+used instead. See the [documentation](../reference.md#extension_configs) for a
+full explanation of the current behavior.
+
+### HeaderId extension deprecated
+
+The HeaderId Extension is deprecated and will raise an error if specified. Use
+the [Table of Contents](../extensions/toc.md) Extension instead, which offers
+most of the features of the HeaderId Extension and more (support for meta data
+is missing).
+
+Extension authors who have been using the `slugify` and `unique` functions
+defined in the HeaderId Extension should note that those functions are now
+defined in the Table of Contents extension and should adjust their import
+statements accordingly (`from markdown.extensions.toc import slugify, unique`).
+
+### Homegrown `OrderedDict` has been replaced with a purpose-built `Registry`
+
+All processors and patterns now get "registered" to a
+[Registry](../extensions/api.md#registry). A backwards compatible shim is
+included so that existing simple extensions should continue to work.
+A `DeprecationWarning` will be raised for any code which calls the old API.
+
+### Markdown class instance references.
+
+Previously, instances of the `Markdown` class were represented as any one of
+`md`, `md_instance`, or `markdown`. This inconsistency made it difficult when
+developing extensions, or just maintaining the existing code. Now, all instances
+are consistently represented as `md`.
+
+The old attributes on class instances still exist, but raise a
+`DeprecationWarning` when accessed. Also on classes where the instance was
+optional, the attribute always exists now and is simply `None` if no instance
+was provided (previously the attribute would not exist).
+
+### `markdown.util.isBlockLevel` deprecated
+
+The `markdown.util.isBlockLevel` function is deprecated and will raise a
+`DeprecationWarning`. Instead, extensions should use the `isBlockLevel` method
+of the `Markdown` class instance. Additionally, a list of block level elements
+is defined in the `block_level_elements` attribute of the `Markdown` class which
+extensions can access to alter the list of elements which are treated as block
+level elements.
+
+### `md_globals` keyword deprecated from extension API
+
+Previously, the `extendMarkdown` method of a `markdown.extensions.Extension`
+subclasses accepted an `md_globals` keyword, which contained the value returned
+by Python's `globals()` built-in function. As all of the configuration is now
+held within the `Markdown` class instance, access to the globals is no longer
+necessary and any extensions which expect the keyword will raise a
+`DeprecationWarning`. A future release will raise an error.
+
+### Added new, more flexible `InlineProcessor` class
+
+A new `InlineProcessor` class handles inline processing much better and allows
+for more flexibility. The new `InlineProcessor` classes no longer utilize
+unnecessary pretext and post-text captures. New class can accept the buffer that
+is being worked on and manually process the text without regular expressions and
+return new replacement bounds. This helps us to handle links in a better way and
+handle nested brackets and logic that is too much for regular expression.
+
+## New features
+
+The following new features have been included in the release:
+
+* A new [testing framework](../test_tools.md) is included as a part of the
+ Markdown library, which can also be used by third party extensions.
+
+* A new `toc_depth` parameter has been added to the
+ [Table of Contents Extension](../extensions/toc.md).
+
+* A new `toc_tokens` attribute has been added to the Markdown class by the
+ [Table of Contents Extension](../extensions/toc.md), which contains the raw
+ tokens used to build the Table of Contents. Users can use this to build their
+ own custom Table of Contents rather than needing to parse the HTML available
+ on the `toc` attribute of the Markdown class.
+
+* When the [Table of Contents Extension](../extensions/toc.md) is used in
+ conjunction with the [Attribute Lists Extension](../extensions/attr_list.md)
+ and a `data-toc-label` attribute is defined on a header, the content of the
+ `data-toc-label` attribute is now used as the content of the Table of Contents
+ item for that header.
+
+* Additional CSS class names can be appended to
+ [Admonitions](../extensions/admonition.md).
--- /dev/null
+title: Command Line
+
+Using Python-Markdown on the Command Line
+=========================================
+
+While Python-Markdown is primarily a python library, a command line script is
+included as well. While there are many other command line implementations
+of Markdown, you may not have them installed, or you may prefer to use
+Python-Markdown's various extensions.
+
+Generally, you will want to have the Markdown library fully installed on your
+system to run the command line script. See the
+[Installation instructions](install.md) for details.
+
+Python-Markdown's command line script takes advantage of Python's `-m` flag.
+Therefore, assuming the python executable is on your system path, use the
+following format:
+
+```bash
+python -m markdown [options] [args]
+```
+
+That will run the module as a script with the options and arguments provided.
+
+At its most basic usage, one would simply pass in a file name as the only argument:
+
+```bash
+python -m markdown input_file.txt
+```
+
+Piping input and output (on `STDIN` and `STDOUT`) is fully supported as well.
+For example:
+
+```bash
+echo "Some **Markdown** text." | python -m markdown > output.html
+```
+
+Use the `--help` option for a list all available options and arguments:
+
+```bash
+python -m markdown --help
+```
+
+If you don't want to call the python executable directly (using the `-m` flag),
+follow the instructions below to use a wrapper script:
+
+Setup
+-----
+
+Upon installation, the `markdown_py` script will have been copied to
+your Python "Scripts" directory. Different systems require different methods to
+ensure that any files in the Python "Scripts" directory are on your system
+path.
+
+* **Windows**:
+
+ Assuming a default install of Python on Windows, your "Scripts" directory
+ is most likely something like `C:\\Python26\Scripts`. Verify the location
+ of your "Scripts" directory and add it to you system path.
+
+ Calling `markdown_py` from the command line will call the wrapper batch
+ file `markdown_py.bat` in the `"Scripts"` directory created during install.
+
+* __*nix__ (Linux, OSX, BSD, Unix, etc.):
+
+ As each \*nix distribution is different and we can't possibly document all
+ of them here, we'll provide a few helpful pointers:
+
+ * Some systems will automatically install the script on your path. Try it
+ and see if it works. Just run `markdown_py` from the command line.
+
+ * Other systems may maintain a separate "Scripts" ("bin") directory which
+ you need to add to your path. Find it (check with your distribution) and
+ either add it to your path or make a symbolic link to it from your path.
+
+ * If you are sure `markdown_py` is on your path, but it still is not being
+ found, check the permissions of the file and make sure it is executable.
+
+ As an alternative, you could just `cd` into the directory which contains
+ the source distribution, and run it from there. However, remember that your
+ markdown text files will not likely be in that directory, so it is much
+ more convenient to have `markdown_py` on your path.
+
+!!!Note
+ Python-Markdown uses `"markdown_py"` as a script name because the Perl
+ implementation has already taken the more obvious name "markdown".
+ Additionally, the default Python configuration on some systems would cause a
+ script named `"markdown.py"` to fail by importing itself rather than the
+ markdown library. Therefore, the script has been named `"markdown_py"` as a
+ compromise. If you prefer a different name for the script on your system, it
+ is suggested that you create a symbolic link to `markdown_py` with your
+ preferred name.
+
+Usage
+-----
+
+To use `markdown_py` from the command line, run it as
+
+```bash
+markdown_py input_file.txt
+```
+
+or
+
+```bash
+markdown_py input_file.txt > output_file.html
+```
+
+For a complete list of options, run
+
+```bash
+markdown_py --help
+```
+
+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 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:MyExtClass input.txt
+```
+
+To load multiple extensions, specify an `-x` option for each extension:
+
+```bash
+python -m markdown -x myext -x path.to.module:MyExtClass input.txt
+```
+
+If the extension supports configuration options (see the documentation for the
+extension you are using to determine what settings it supports, if any), you
+can pass them in as well:
+
+```bash
+python -m markdown -x myext -c config.yml input.txt
+```
+
+The `-c` (or `--extension_configs`) option accepts a file name. The file must be
+in either the [YAML] or [JSON] format and contain YAML or JSON data that would
+map to a Python Dictionary in the format required by the
+[`extension_configs`][ec] keyword of the `markdown.Markdown` class. Therefore,
+the file `config.yaml` referenced in the above example might look like this:
+
+```yaml
+myext:
+ option1: 'value1'
+ option2: True
+```
+
+Note that while the `--extension_configs` option does specify the
+`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.
+
+[ec]: reference.md#extension_configs
+[YAML]: http://yaml.org/
+[JSON]: http://json.org/
+[PyYAML]: http://pyyaml.org/
+[2.5 release notes]: change_log/release-2.5.md
+++ /dev/null
-title: Command Line
-prev_title: Library Reference
-prev_url: reference.html
-next_title: Extensions
-next_url: extensions/index.html
-
-
-Using Python-Markdown on the Command Line
-=========================================
-
-While Python-Markdown is primarily a python library, a command line script is
-included as well. While there are many other command line implementations
-of Markdown, you may not have them installed, or you may prefer to use
-Python-Markdown's various extensions.
-
-Generally, you will want to have the Markdown library fully installed on your
-system to run the command line script. See the
-[Installation instructions](install.html) for details.
-
-Python-Markdown's command line script takes advantage of Python's `-m` flag.
-Therefore, assuming the python executable is on your system path, use the
-following format:
-
- $ python -m markdown [options] [args]
-
-That will run the module as a script with the options and arguments provided.
-
-At its most basic usage, one would simply pass in a file name as the only argument:
-
- $ python -m markdown input_file.txt
-
-Piping input and output (on `STDIN` and `STDOUT`) is fully supported as well.
-For example:
-
- $ echo "Some **Markdown** text." | python -m markdown > output.html
-
-Use the `--help` option for a list all available options and arguments:
-
- $ python -m markdown --help
-
-If you don't want to call the python executable directly (using the `-m` flag),
-follow the instructions below to use a wrapper script:
-
-Setup
------
-
-Upon installation, the `markdown_py` script will have been copied to
-your Python "Scripts" directory. Different systems require different methods to
-ensure that any files in the Python "Scripts" directory are on your system
-path.
-
-* **Windows**:
-
- Assuming a default install of Python on Windows, your "Scripts" directory
- is most likely something like `C:\\Python26\Scripts`. Verify the location
- of your "Scripts" directory and add it to you system path.
-
- Calling `markdown_py` from the command line will call the wrapper batch
- file `markdown_py.bat` in the `"Scripts"` directory created during install.
-
-* __*nix__ (Linux, OSX, BSD, Unix, etc.):
-
- As each *nix distribution is different and we can't possibly document all
- of them here, we'll provide a few helpful pointers:
-
- * Some systems will automatically install the script on your path. Try it
- and see if it works. Just run `markdown_py` from the command line.
-
- * Other systems may maintain a separate "Scripts" ("bin") directory which
- you need to add to your path. Find it (check with your distribution) and
- either add it to your path or make a symbolic link to it from your path.
-
- * If you are sure `markdown_py` is on your path, but it still is not being
- found, check the permissions of the file and make sure it is executable.
-
- As an alternative, you could just `cd` into the directory which contains
- the source distribution, and run it from there. However, remember that your
- markdown text files will not likely be in that directory, so it is much
- more convenient to have `markdown_py` on your path.
-
-!!!Note
- Python-Markdown uses `"markdown_py"` as a script name because
- the Perl implementation has already taken the more obvious name "markdown".
- Additionally, the default Python configuration on some systems would cause a
- script named `"markdown.py"` to fail by importing itself rather than the markdown
- library. Therefore, the script has been named `"markdown_py"` as a compromise. If
- you prefer a different name for the script on your system, it is suggested that
- you create a symbolic link to `markdown_py` with your preferred name.
-
-Usage
------
-
-To use `markdown_py` from the command line, run it as
-
- $ markdown_py input_file.txt
-
-or
-
- $ markdown_py input_file.txt > output_file.html
-
-For a complete list of options, run
-
- $ markdown_py --help
-
-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.html) for details). The extension can
-then be invoked by the name of that module using Python's dot syntax:
-
- $ python -m markdown -x path.to.module input.txt
-
-To load multiple extensions, specify an `-x` option for each extension:
-
- $ python -m markdown -x markdown.extensions.footnotes -x markdown.extensions.codehilite input.txt
-
-If the extension supports configuration options (see the documentation for the
-extension you are using to determine what settings it supports, if any), you
-can pass them in as well:
-
- $ python -m markdown -x markdown.extensions.footnotes -c config.yml input.txt
-
-The `-c` (or `--extension_configs`) option accepts a file name. The file must be in
-either the [YAML] or [JSON] format and contain YAML or JSON data that would map to
-a Python Dictionary in the format required by the [`extension_configs`][ec] keyword
-of the `markdown.Markdown` class. Therefore, the file `config.yaml` referenced in the
-above example might look like this:
-
- markdown.extensions.footnotes:
- PLACE_MARKER: ~~~~~~~~
- UNIQUE_IDS: 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.
-
-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/
-[PyYAML]: http://pyyaml.org/
-[2.5 release notes]: release-2.5.txt
-
+++ /dev/null
-/*
- * default.css_t
- * ~~~~~~~~~~~~~
- *
- * Sphinx stylesheet -- default theme.
- *
- * :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
- * :license: BSD, see LICENSE for details.
- *
- */
-
-@import url("basic.css");
-
-/* -- page layout ----------------------------------------------------------- */
-
-body {
- font-family: sans-serif;
- font-size: 100%;
- background-color: #11303d;
- color: #000;
- margin: 0;
- padding: 0;
-}
-
-div.document {
- background-color: #1c4e63;
-}
-
-div.documentwrapper {
- float: left;
- width: 100%;
-}
-
-div.bodywrapper {
- margin: 0 0 0 230px;
-}
-
-div.body {
- background-color: #ffffff;
- color: #000000;
- padding: 0 20px 30px 20px;
-}
-
-div.footer {
- color: #ffffff;
- width: 100%;
- padding: 9px 0 9px 0;
- text-align: center;
- font-size: 75%;
-}
-
-div.footer a {
- color: #ffffff;
- text-decoration: underline;
-}
-
-div.related {
- background-color: #133f52;
- line-height: 30px;
- color: #ffffff;
-}
-
-div.related a {
- color: #ffffff;
-}
-
-div.sphinxsidebar {
-}
-
-div.sphinxsidebar h3 {
- font-family: 'Trebuchet MS', sans-serif;
- color: #ffffff;
- font-size: 1.4em;
- font-weight: normal;
- margin: 0;
- padding: 0;
-}
-
-div.sphinxsidebar h3 a {
- color: #ffffff;
-}
-
-div.sphinxsidebar h4 {
- font-family: 'Trebuchet MS', sans-serif;
- color: #ffffff;
- font-size: 1.3em;
- font-weight: normal;
- margin: 5px 0 0 0;
- padding: 0;
-}
-
-div.sphinxsidebar p {
- color: #ffffff;
-}
-
-div.sphinxsidebar p.topless {
- margin: 5px 10px 10px 10px;
-}
-
-div.sphinxsidebar ul {
- margin: 10px;
- padding: 0;
- color: #ffffff;
-}
-
-div.sphinxsidebar a {
- color: #98dbcc;
-}
-
-div.sphinxsidebar input {
- border: 1px solid #98dbcc;
- font-family: sans-serif;
- font-size: 1em;
-}
-
-
-/* for collapsible sidebar */
-div#sidebarbutton {
- background-color: #3c6e83;
-}
-
-
-/* -- hyperlink styles ------------------------------------------------------ */
-
-a {
- color: #355f7c;
- text-decoration: none;
-}
-
-a:visited {
- color: #355f7c;
- text-decoration: none;
-}
-
-a:hover {
- text-decoration: underline;
-}
-
-
-
-/* -- body styles ----------------------------------------------------------- */
-
-div.body h1,
-div.body h2,
-div.body h3,
-div.body h4,
-div.body h5,
-div.body h6 {
- font-family: 'Trebuchet MS', sans-serif;
- background-color: #f2f2f2;
- font-weight: normal;
- color: #20435c;
- border-bottom: 1px solid #ccc;
- margin: 20px -20px 10px -20px;
- padding: 3px 0 3px 10px;
-}
-
-div.body h1 { margin-top: 0; font-size: 200%; }
-div.body h2 { font-size: 160%; }
-div.body h3 { font-size: 140%; }
-div.body h4 { font-size: 120%; }
-div.body h5 { font-size: 110%; }
-div.body h6 { font-size: 100%; }
-
-a.headerlink {
- color: #c60f0f;
- font-size: 0.8em;
- padding: 0 4px 0 4px;
- text-decoration: none;
-}
-
-a.headerlink:hover {
- background-color: #c60f0f;
- color: white;
-}
-
-div.body p, div.body dd, div.body li {
- text-align: justify;
- line-height: 130%;
-}
-
-div.admonition p.admonition-title + p {
- display: inline;
-}
-
-div.admonition p {
- margin-bottom: 5px;
-}
-
-div.admonition pre {
- margin-bottom: 5px;
-}
-
-div.admonition ul, div.admonition ol {
- margin-bottom: 5px;
-}
-
-div.note {
- background-color: #eee;
- border: 1px solid #ccc;
-}
-
-div.seealso {
- background-color: #ffc;
- border: 1px solid #ff6;
-}
-
-div.topic {
- background-color: #eee;
-}
-
-div.warning {
- background-color: #ffe4e4;
- border: 1px solid #f66;
-}
-
-p.admonition-title {
- display: inline;
-}
-
-p.admonition-title:after {
- content: ":";
-}
-
-pre {
- padding: 5px;
- background-color: #eeffcc;
- color: #333333;
- line-height: 120%;
- border: 1px solid #ac9;
- border-left: none;
- border-right: none;
-}
-
-tt {
- background-color: #ecf0f3;
- padding: 0 1px 0 1px;
- font-size: 0.95em;
-}
-
-th {
- background-color: #ede;
-}
-
-.warning tt {
- background: #efc2c2;
-}
-
-.note tt {
- background: #d6d6d6;
-}
-
-.viewcode-back {
- font-family: sans-serif;
-}
-
-div.viewcode-block:target {
- background-color: #f4debf;
- border-top: 1px solid #ac9;
- border-bottom: 1px solid #ac9;
-}
\ No newline at end of file
--- /dev/null
+title: Abbreviations Extension
+
+Abbreviations
+=============
+
+Summary
+-------
+
+The Abbreviations extension adds the ability to define abbreviations.
+Specifically, any defined abbreviation is wrapped in an `<abbr>` tag.
+
+The Abbreviations extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Abbreviations are defined using the syntax established in
+[PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#abbr
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+```md
+The HTML specification
+is maintained by the W3C.
+
+*[HTML]: Hyper Text Markup Language
+*[W3C]: World Wide Web Consortium
+```
+
+will be rendered as:
+
+```html
+<p>The <abbr title="Hyper Text Markup Language">HTML</abbr> specification
+is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.</p>
+```
+
+Usage
+-----
+
+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.
+++ /dev/null
-title: Abbreviations Extension
-prev_title: Extra Extension
-prev_url: extra.html
-next_title: Attribute Lists Extension
-next_url: attr_list.html
-
-Abbreviations
-=============
-
-Summary
--------
-
-The Abbreviations extension adds the ability to define abbreviations.
-Specifically, any defined abbreviation is wrapped in an `<abbr>` tag.
-
-The Abbreviations extension is included in the standard Markdown library.
-
-Syntax
-------
-
-Abbreviations are defined using the syntax established in
-[PHP Markdown Extra][php].
-
-[php]: http://www.michelf.com/projects/php-markdown/extra/#abbr
-
-Thus, the following text (taken from the above referenced PHP documentation):
-
- The HTML specification
- is maintained by the W3C.
-
- *[HTML]: Hyper Text Markup Language
- *[W3C]: World Wide Web Consortium
-
-will be rendered as:
-
- <p>The <abbr title="Hyper Text Markup Language">HTML</abbr> specification
- is maintained by the <abbr title="World Wide Web Consortium">W3C</abbr>.</p>
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.abbr`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
--- /dev/null
+title: Admonition Extension
+
+Admonition
+==========
+
+Summary
+-------
+
+The Admonition extension adds [rST-style][rST] admonitions to Markdown documents.
+
+This extension is included in the standard Markdown library.
+
+[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
+
+Syntax
+------
+
+Admonitions are created using the following syntax:
+
+```md
+!!! type "optional explicit title within double quotes"
+ Any number of other indented markdown elements.
+
+ This is the second paragraph.
+```
+
+`type` will be used as the CSS class name and as default title. It must be a
+single word. So, for instance:
+
+```md
+!!! note
+ You should note that the title will be automatically capitalized.
+```
+
+will render:
+
+```html
+<div class="admonition note">
+<p class="admonition-title">Note</p>
+<p>You should note that the title will be automatically capitalized.</p>
+</div>
+```
+
+Optionally, you can use custom titles. For instance:
+
+```md
+!!! danger "Don't try this at home"
+ ...
+```
+
+will render:
+
+```html
+<div class="admonition danger">
+<p class="admonition-title">Don't try this at home</p>
+<p>...</p>
+</div>
+```
+
+If you don't want a title, use a blank string `""`:
+
+```md
+!!! important ""
+ This is an admonition box without a title.
+```
+
+results in:
+
+```html
+<div class="admonition important">
+<p>This is an admonition box without a title.</p>
+</div>
+```
+
+You can also provide additional CSS class names separated by spaces. The first
+class should be the "type." For example:
+
+```md
+!!! danger highlight blink "Don't try this at home"
+ ...
+```
+
+will render:
+
+```html
+<div class="admonition danger highlight blink">
+<p class="admonition-title">Don't try this at home</p>
+<p>...</p>
+</div>
+```
+
+rST suggests the following "types": `attention`, `caution`, `danger`, `error`,
+`hint`, `important`, `note`, `tip`, and `warning`; however, you're free to use
+whatever you want.
+
+Styling
+-------
+
+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.
+++ /dev/null
-title: Admonition Extension
-prev_title: Smart Strong Extension
-prev_url: smart_strong.html
-next_title: CodeHilite Extension
-next_url: code_hilite.html
-
-Admonition
-==========
-
-Summary
--------
-
-The Admonition extension adds [rST-style][rST] admonitions to Markdown documents.
-
-This extension is included in the standard Markdown library.
-
-[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
-
-Syntax
-------
-
-Admonitions are created using the following syntax:
-
- !!! type "optional explicit title within double quotes"
- Any number of other indented markdown elements.
-
- This is the second paragraph.
-
-`type` will be used as the CSS class name and as default title. It must be a
-single word. So, for instance:
-
- !!! note
- You should note that the title will be automatically capitalized.
-
-will render:
-
- <div class="admonition note">
- <p class="admonition-title">Note</p>
- <p>You should note that the title will be automatically capitalized.</p>
- </div>
-
-Optionally, you can use custom titles. For instance:
-
- !!! danger "Don't try this at home"
- ...
-
-will render:
-
- <div class="admonition danger">
- <p class="admonition-title">Don't try this at home</p>
- <p>...</p>
- </div>
-
-If you don't want a title, use a blank string `""`:
-
- !!! important ""
- This is a admonition box without a title.
-
-results in:
-
- <div class="admonition important">
- <p>This is a admonition box without a title.</p>
- </div>
-
-
-rST suggests the following `types`, but you're free to use whatever you want:
- attention, caution, danger, error, hint, important, note, tip, warning.
-
-Styling
--------
-
-There is no CSS included as part of this extension. Look up the default
-[Sphinx][sphinx] theme if you need inspiration.
-
-[sphinx]: http://sphinx.pocoo.org/
--- /dev/null
+title: Extensions API
+
+# Writing Extensions for Python-Markdown
+
+Python-Markdown includes an API for extension writers to plug their own
+custom functionality and/or syntax into the parser. There are Preprocessors
+which allow you to alter the source before it is passed to the parser,
+inline patterns which allow you to add, remove or override the syntax of
+any inline elements, and Postprocessors which allow munging of the
+output of the parser before it is returned. If you really want to dive in,
+there are also Blockprocessors which are part of the core BlockParser.
+
+As the parser builds an [ElementTree][ElementTree] object which is later rendered
+as Unicode text, there are also some helpers provided to ease manipulation of
+the tree. Each part of the API is discussed in its respective section below.
+Additionally, reading the source of some [Available Extensions][] may be
+helpful. For example, the [Footnotes][] extension uses most of the features
+documented here.
+
+## Preprocessors {: #preprocessors }
+
+Preprocessors munge the source text before it is passed into the Markdown
+core. This is an excellent place to clean up bad syntax, extract things the
+parser may otherwise choke on and perhaps even store it for later retrieval.
+
+Preprocessors should inherit from `markdown.preprocessors.Preprocessor` and
+implement a `run` method with one argument `lines`. The `run` method of
+each Preprocessor will be passed the entire source text as a list of Unicode
+strings. Each string will contain one line of text. The `run` method should
+return either that list, or an altered list of Unicode strings.
+
+A pseudo example:
+
+```python
+from markdown.preprocessors import Preprocessor
+
+class MyPreprocessor(Preprocessor):
+ def run(self, lines):
+ new_lines = []
+ for line in lines:
+ m = MYREGEX.match(line)
+ if m:
+ # do stuff
+ else:
+ new_lines.append(line)
+ return new_lines
+```
+
+## Inline Patterns {: #inlinepatterns }
+
+### Legacy
+
+Inline Patterns implement the inline HTML element syntax for Markdown such as
+`*emphasis*` or `[links](http://example.com)`. Pattern objects should be
+instances of classes that inherit from `markdown.inlinepatterns.Pattern` or
+one of its children. Each pattern object uses a single regular expression and
+must have the following methods:
+
+* **`getCompiledRegExp()`**:
+
+ Returns a compiled regular expression.
+
+* **`handleMatch(m)`**:
+
+ Accepts a match object and returns an ElementTree element of a plain
+ Unicode string.
+
+Also, Inline Patterns can define the property `ANCESTOR_EXCLUDES` with either
+a list or tuple of undesirable ancestors. The pattern should not match if it
+would cause the content to be a descendant of one of the defined tag names.
+
+Note that any regular expression returned by `getCompiledRegExp` must capture
+the whole block. Therefore, they should all start with `r'^(.*?)'` and end
+with `r'(.*?)!'`. When using the default `getCompiledRegExp()` method
+provided in the `Pattern` you can pass in a regular expression without that
+and `getCompiledRegExp` will wrap your expression for you and set the
+`re.DOTALL` and `re.UNICODE` flags. This means that the first group of your
+match will be `m.group(2)` as `m.group(1)` will match everything before the
+pattern.
+
+For an example, consider this simplified emphasis pattern:
+
+```python
+from markdown.inlinepatterns import Pattern
+from markdown.util import etree
+
+class EmphasisPattern(Pattern):
+ def handleMatch(self, m):
+ el = etree.Element('em')
+ el.text = m.group(2)
+ return el
+```
+
+As discussed in [Integrating Your Code Into Markdown][], an instance of this
+class will need to be provided to Markdown. That instance would be created
+like so:
+
+```python
+# an oversimplified regex
+MYPATTERN = r'\*([^*]+)\*'
+# pass in pattern and create instance
+emphasis = EmphasisPattern(MYPATTERN)
+```
+
+Actually it would not be necessary to create that pattern (and not just because
+a more sophisticated emphasis pattern already exists in Markdown). The fact is,
+that example pattern is not very DRY. A pattern for `**strong**` text would
+be almost identical, with the exception that it would create a 'strong' element.
+Therefore, Markdown provides a number of generic pattern classes that can
+provide some common functionality. For example, both emphasis and strong are
+implemented with separate instances of the `SimpleTagPattern` listed below.
+Feel free to use or extend any of the Pattern classes found at
+`markdown.inlinepatterns`.
+
+### Future
+
+While users can still create plugins with the existing
+`markdown.inlinepatterns.Pattern`, a new, more flexible inline processor has
+been added which users are encouraged to migrate to. The new inline processor
+is found at `markdown.inlinepatterns.InlineProcessor`.
+
+The new processor is very similar to legacy with two major distinctions.
+
+1. Patterns no longer need to match the entire block, so patterns no longer
+ start with `r'^(.*?)'` and end with `r'(.*?)!'`. This was a huge
+ performance sink and this requirement has been removed. The returned match
+ object will only contain what is explicitly matched in the pattern, and
+ extension pattern groups now start with `m.group(1)`.
+
+2. The `handleMatch` method now takes an additional input called `data`,
+ which is the entire block under analysis, not just what is matched with
+ the specified pattern. The method also returns the element *and* the index
+ boundaries relative to `data` that the return element is replacing
+ (usually `m.start(0)` and `m.end(0)`). If the boundaries are returned as
+ `None`, it is assumed that the match did not take place, and nothing will
+ be altered in `data`.
+
+If all you need is the same functionality as the legacy processor, you can do
+as shown below. Most of the time, simple regular expression processing is all
+you'll need.
+
+```python
+from markdown.inlinepatterns import InlineProcessor
+from markdown.util import etree
+
+# an oversimplified regex
+MYPATTERN = r'\*([^*]+)\*'
+
+class EmphasisPattern(InlineProcessor):
+ def handleMatch(self, m, data):
+ el = etree.Element('em')
+ el.text = m.group(1)
+ return el, m.start(0), m.end(0)
+
+# pass in pattern and create instance
+emphasis = EmphasisPattern(MYPATTERN)
+```
+
+But, the new processor allows you handle much more complex patterns that are
+too much for Python's Re to handle. For instance, to handle nested brackets in
+link patterns, the built-in link inline processor uses the following pattern to
+find where a link *might* start:
+
+```python
+LINK_RE = NOIMG + r'\['
+link = LinkInlineProcessor(LINK_RE, md_instance)
+```
+
+It then uses programmed logic to actually walk the string (`data`), starting at
+where the match started (`m.start(0)`). If for whatever reason, the text
+does not appear to be a link, it returns `None` for the start and end boundary
+in order to communicate to the parser that no match was found.
+
+```python
+ # Just a snippet of the link's handleMatch
+ # method to illustrate new logic
+ def handleMatch(self, m, data):
+ text, index, handled = self.getText(data, m.end(0))
+
+ if not handled:
+ return None, None, None
+
+ href, title, index, handled = self.getLink(data, index)
+ if not handled:
+ return None, None, None
+
+ el = util.etree.Element("a")
+ el.text = text
+
+ el.set("href", href)
+
+ if title is not None:
+ el.set("title", title)
+
+ return el, m.start(0), index
+```
+
+### Generic Pattern Classes
+
+Some example processors that are available.
+
+* **`SimpleTextInlineProcessor(pattern)`**:
+
+ Returns simple text of `group(2)` of a `pattern` and the start and end
+ position of the match.
+
+* **`SimpleTagInlineProcessor(pattern, tag)`**:
+
+ Returns an element of type "`tag`" with a text attribute of `group(3)`
+ of a `pattern`. `tag` should be a string of a HTML element (i.e.: 'em').
+ It also returns the start and end position of the match.
+
+* **`SubstituteTagInlineProcessor(pattern, tag)`**:
+
+ Returns an element of type "`tag`" with no children or text (i.e.: `br`)
+ and the start and end position of the match.
+
+A very small number of the basic legacy processors are still available to
+prevent breakage of 3rd party extensions during the transition period to the
+new processors. Three of the available processors are listed below.
+
+* **`SimpleTextPattern(pattern)`**:
+
+ Returns simple text of `group(2)` of a `pattern`.
+
+* **`SimpleTagPattern(pattern, tag)`**:
+
+ Returns an element of type "`tag`" with a text attribute of `group(3)`
+ of a `pattern`. `tag` should be a string of a HTML element (i.e.: 'em').
+
+* **`SubstituteTagPattern(pattern, tag)`**:
+
+ Returns an element of type "`tag`" with no children or text (i.e.: `br`).
+
+There may be other Pattern classes in the Markdown source that you could extend
+or use as well. Read through the source and see if there is anything you can
+use. You might even get a few ideas for different approaches to your specific
+situation.
+
+## Treeprocessors {: #treeprocessors }
+
+Treeprocessors manipulate an ElementTree object after it has passed through the
+core BlockParser. This is where additional manipulation of the tree takes
+place. Additionally, the InlineProcessor is a Treeprocessor which steps through
+the tree and runs the Inline Patterns on the text of each Element in the tree.
+
+A Treeprocessor should inherit from `markdown.treeprocessors.Treeprocessor`,
+over-ride the `run` method which takes one argument `root` (an ElementTree
+object) and either modifies that root element and returns `None` or returns a
+new ElementTree object.
+
+A pseudo example:
+
+```python
+from markdown.treeprocessors import Treeprocessor
+
+class MyTreeprocessor(Treeprocessor):
+ def run(self, root):
+ root.text = 'modified content'
+```
+
+Note that Python class methods return `None` by default when no `return`
+statement is defined. Additionally all Python variables refer to objects by
+reference. Therefore, the above `run` method modifies the `root` element
+in place and returns `None`. The changes made to the `root` element and its
+children are retained.
+
+Some may be inclined to return the modified `root` element. While that would
+work, it would cause a copy of the entire ElementTree to be generated each
+time the Treeprocessor is run. Therefore, it is generally expected that
+the `run` method would only return `None` or a new ElementTree object.
+
+For specifics on manipulating the ElementTree, see
+[Working with the ElementTree][workingwithetree] below.
+
+## Postprocessors {: #postprocessors }
+
+Postprocessors manipulate the document after the ElementTree has been
+serialized into a string. Postprocessors should be used to work with the
+text just before output.
+
+A Postprocessor should inherit from `markdown.postprocessors.Postprocessor`
+and over-ride the `run` method which takes one argument `text` and returns
+a Unicode string.
+
+Postprocessors are run after the ElementTree has been serialized back into
+Unicode text. For example, this may be an appropriate place to add a table of
+contents to a document:
+
+```python
+from markdown.postprocessors import Postprocessor
+
+class TocPostprocessor(Postprocessor):
+ def run(self, text):
+ return MYMARKERRE.sub(MyToc, text)
+```
+
+## BlockParser {: #blockparser }
+
+Sometimes, Preprocessors, Treeprocessors, Postprocessors, and Inline Patterns
+are not going to do what you need. Perhaps you want a new type of block type
+that needs to be integrated into the core parsing. In such a situation, you can
+add/change/remove functionality of the core `BlockParser`. The BlockParser is
+composed of a number of Blockprocessors. The BlockParser steps through each
+block of text (split by blank lines) and passes each block to the appropriate
+Blockprocessor. That Blockprocessor parses the block and adds it to the
+ElementTree. The
+[Definition Lists][] extension would be a good example of an extension that
+adds/modifies Blockprocessors.
+
+A Blockprocessor should inherit from `markdown.blockprocessors.BlockProcessor`
+and implement both the `test` and `run` methods.
+
+The `test` method is used by BlockParser to identify the type of block.
+Therefore the `test` method must return a Boolean value. If the test returns
+`True`, then the BlockParser will call that Blockprocessor's `run` method.
+If it returns `False`, the BlockParser will move on to the next
+Blockprocessor.
+
+The **`test`** method takes two arguments:
+
+* **`parent`**: The parent ElementTree Element of the block. This can be useful
+ as the block may need to be treated differently if it is inside a list, for
+ example.
+
+* **`block`**: A string of the current block of text. The test may be a
+ simple string method (such as `block.startswith(some_text)`) or a complex
+ regular expression.
+
+The **`run`** method takes two arguments:
+
+* **`parent`**: A pointer to the parent ElementTree Element of the block. The run
+ method will most likely attach additional nodes to this parent. Note that
+ nothing is returned by the method. The ElementTree object is altered in place.
+
+* **`blocks`**: A list of all remaining blocks of the document. Your run
+ method must remove (pop) the first block from the list (which it altered in
+ place - not returned) and parse that block. You may find that a block of text
+ legitimately contains multiple block types. Therefore, after processing the
+ first type, your processor can insert the remaining text into the beginning
+ of the `blocks` list for future parsing.
+
+Please be aware that a single block can span multiple text blocks. For example,
+The official Markdown syntax rules state that a blank line does not end a
+Code Block. If the next block of text is also indented, then it is part of
+the previous block. Therefore, the BlockParser was specifically designed to
+address these types of situations. If you notice the `CodeBlockProcessor`,
+in the core, you will note that it checks the last child of the `parent`.
+If the last child is a code block (`<pre><code>...</code></pre>`), then it
+appends that block to the previous code block rather than creating a new
+code block.
+
+Each Blockprocessor has the following utility methods available:
+
+* **`lastChild(parent)`**:
+
+ Returns the last child of the given ElementTree Element or `None` if it
+ had no children.
+
+* **`detab(text)`**:
+
+ Removes one level of indent (four spaces by default) from the front of each
+ line of the given text string.
+
+* **`looseDetab(text, level)`**:
+
+ Removes "level" levels of indent (defaults to 1) from the front of each line
+ of the given text string. However, this methods allows secondary lines to
+ not be indented as does some parts of the Markdown syntax.
+
+Each Blockprocessor also has a pointer to the containing BlockParser instance at
+`self.parser`, which can be used to check or alter the state of the parser.
+The BlockParser tracks it's state in a stack at `parser.state`. The state
+stack is an instance of the `State` class.
+
+**`State`** is a subclass of `list` and has the additional methods:
+
+* **`set(state)`**:
+
+ Set a new state to string `state`. The new state is appended to the end
+ of the stack.
+
+* **`reset()`**:
+
+ Step back one step in the stack. The last state at the end is removed from
+ the stack.
+
+* **`isstate(state)`**:
+
+ Test that the top (current) level of the stack is of the given string
+ `state`.
+
+Note that to ensure that the state stack does not become corrupted, each time a
+state is set for a block, that state *must* be reset when the parser finishes
+parsing that block.
+
+An instance of the **`BlockParser`** is found at `Markdown.parser`.
+`BlockParser` has the following methods:
+
+* **`parseDocument(lines)`**:
+
+ Given a list of lines, an ElementTree object is returned. This should be
+ passed an entire document and is the only method the `Markdown` class
+ calls directly.
+
+* **`parseChunk(parent, text)`**:
+
+ Parses a chunk of markdown text composed of multiple blocks and attaches
+ those blocks to the `parent` Element. The `parent` is altered in place
+ and nothing is returned. Extensions would most likely use this method for
+ block parsing.
+
+* **`parseBlocks(parent, blocks)`**:
+
+ Parses a list of blocks of text and attaches those blocks to the `parent`
+ Element. The `parent` is altered in place and nothing is returned. This
+ method will generally only be used internally to recursively parse nested
+ blocks of text.
+
+While it is not recommended, an extension could subclass or completely replace
+the `BlockParser`. The new class would have to provide the same public API.
+However, be aware that other extensions may expect the core parser provided
+and will not work with such a drastically different parser.
+
+## Working with the ElementTree {: #working_with_et }
+
+As mentioned, the Markdown parser converts a source document to an
+[ElementTree][ElementTree] object before serializing that back to Unicode text.
+Markdown has provided some helpers to ease that manipulation within the context
+of the Markdown module.
+
+First, to get access to the ElementTree module import ElementTree from
+`markdown` rather than importing it directly. This will ensure you are using
+the same version of ElementTree as markdown. The module is found at
+`markdown.util.etree` within Markdown.
+
+```python
+from markdown.util import etree
+```
+
+`markdown.util.etree` tries to import ElementTree from any known location,
+first as a standard library module (from `xml.etree` in Python 2.5), then as
+a third party package (ElementTree). In each instance, `cElementTree` is
+tried first, then ElementTree if the faster C implementation is not
+available on your system.
+
+Sometimes you may want text inserted into an element to be parsed by
+[Inline Patterns][]. In such a situation, simply insert the text as you normally
+would and the text will be automatically run through the Inline Patterns.
+However, if you do *not* want some text to be parsed by Inline Patterns,
+then insert the text as an `AtomicString`.
+
+```python
+from markdown.util import AtomicString
+some_element.text = AtomicString(some_text)
+```
+
+Here's a basic example which creates an HTML table (note that the contents of
+the second cell (`td2`) will be run through Inline Patterns latter):
+
+```python
+table = etree.Element("table")
+table.set("cellpadding", "2") # Set cellpadding to 2
+tr = etree.SubElement(table, "tr") # Add child tr to table
+td1 = etree.SubElement(tr, "td") # Add child td1 to tr
+td1.text = markdown.util.AtomicString("Cell content") # Add plain text content
+td2 = etree.SubElement(tr, "td") # Add second td to tr
+td2.text = "*text* with **inline** formatting." # Add markup text
+table.tail = "Text after table" # Add text after table
+```
+
+You can also manipulate an existing tree. Consider the following example which
+adds a `class` attribute to `<a>` elements:
+
+```python
+def set_link_class(self, element):
+ for child in element:
+ if child.tag == "a":
+ child.set("class", "myclass") #set the class attribute
+ set_link_class(child) # run recursively on children
+```
+
+For more information about working with ElementTree see the ElementTree
+[Documentation](http://effbot.org/zone/element-index.htm)
+([Python Docs](http://docs.python.org/lib/module-xml.etree.ElementTree.html)).
+
+## Integrating Your Code Into Markdown {: #integrating_into_markdown }
+
+Once you have the various pieces of your extension built, you need to tell
+Markdown about them and ensure that they are run in the proper sequence.
+Markdown accepts an `Extension` instance for each extension. Therefore, you
+will need to define a class that extends `markdown.extensions.Extension` and
+over-rides the `extendMarkdown` method. Within this class you will manage
+configuration options for your extension and attach the various processors and
+patterns to the Markdown instance.
+
+It is important to note that the order of the various processors and patterns
+matters. For example, if we replace `http://...` links with `<a>` elements, and
+*then* try to deal with inline HTML, we will end up with a mess. Therefore, the
+various types of processors and patterns are stored within an instance of the
+Markdown class in a [Registry][]. Your `Extension` class will need to manipulate
+those registries appropriately. You may `register` instances of your processors
+and patterns with an appropriate priority, `deregister` built-in instances, or
+replace a built-in instance with your own.
+
+### `extendMarkdown` {: #extendmarkdown }
+
+The `extendMarkdown` method of a `markdown.extensions.Extension` class
+accepts one argument:
+
+* **`md`**:
+
+ A pointer to the instance of the Markdown class. You should use this to
+ access the [Registries][Registry] of processors and patterns. They are
+ found under the following attributes:
+
+ * `md.preprocessors`
+ * `md.inlinePatterns`
+ * `md.parser.blockprocessors`
+ * `md.treeprocessors`
+ * `md.postprocessors`
+
+ Some other things you may want to access in the markdown instance are:
+
+ * `md.htmlStash`
+ * `md.output_formats`
+ * `md.set_output_format()`
+ * `md.output_format`
+ * `md.serializer`
+ * `md.registerExtension()`
+ * `md.tab_length`
+ * `md.block_level_elements`
+ * `md.isBlockLevel()`
+
+!!! Warning
+ With access to the above items, theoretically you have the option to
+ change anything through various [monkey_patching][] techniques. However,
+ you should be aware that the various undocumented parts of markdown may
+ change without notice and your monkey_patches may break with a new release.
+ Therefore, what you really should be doing is inserting processors and
+ patterns into the markdown pipeline. Consider yourself warned!
+
+[monkey_patching]: http://en.wikipedia.org/wiki/Monkey_patch
+
+A simple example:
+
+```python
+from markdown.extensions import Extension
+
+class MyExtension(Extension):
+ def extendMarkdown(self, md):
+ # Register instance of 'mypattern' with a priority of 175
+ md.inlinePatterns.register(MyPattern(md), 'mypattern', 175)
+```
+
+### Registry
+
+The `markdown.util.Registry` class is a priority sorted registry which Markdown
+uses internally to determine the processing order of its various processors and
+patterns.
+
+A `Registry` instance provides two public methods to alter the data of the
+registry: `register` and `deregister`. Use `register` to add items and
+`deregister` to remove items. See each method for specifics.
+
+When registering an item, a "name" and a "priority" must be provided. All
+items are automatically sorted by "priority" from highest to lowest. The
+"name" is used to remove (`deregister`) and get items.
+
+A `Registry` instance is like a list (which maintains order) when reading
+data. You may iterate over the items, get an item and get a count (length)
+of all items. You may also check that the registry contains an item.
+
+When getting an item you may use either the index of the item or the
+string-based "name". For example:
+
+ registry = Registry()
+ registry.register(SomeItem(), 'itemname', 20)
+ # Get the item by index
+ item = registry[0]
+ # Get the item by name
+ item = registry['itemname']
+
+When checking that the registry contains an item, you may use either the
+string-based "name", or a reference to the actual item. For example:
+
+ someitem = SomeItem()
+ registry.register(someitem, 'itemname', 20)
+ # Contains the name
+ assert 'itemname' in registry
+ # Contains the item instance
+ assert someitem in registry
+
+`markdown.util.Registry` has the following methods:
+
+#### `Registry.register(self, item, name, priority)` {: #registry.register }
+
+: Add an item to the registry with the given name and priority.
+
+ Parameters:
+
+ * `item`: The item being registered.
+ * `name`: A string used to reference the item.
+ * `priority`: An integer or float used to sort against all items.
+
+ If an item is registered with a "name" which already exists, the existing
+ item is replaced with the new item. Tread carefully as the old item is lost
+ with no way to recover it. The new item will be sorted according to its
+ priority and will **not** retain the position of the old item.
+
+#### `Registry.deregister(self, name, strict=True)` {: #registry.deregister }
+
+: Remove an item from the registry.
+
+ Set `strict=False` to fail silently.
+
+#### `Registry.get_index_for_name(self, name)` {: #registry.get_index_for_name }
+
+: Return the index of the given `name`.
+
+### registerExtension {: #registerextension }
+
+Some extensions may need to have their state reset between multiple runs of the
+Markdown class. For example, consider the following use of the [Footnotes][]
+extension:
+
+```python
+md = markdown.Markdown(extensions=['footnotes'])
+html1 = md.convert(text_with_footnote)
+md.reset()
+html2 = md.convert(text_without_footnote)
+```
+
+Without calling `reset`, the footnote definitions from the first document will
+be inserted into the second document as they are still stored within the class
+instance. Therefore the `Extension` class needs to define a `reset` method
+that will reset the state of the extension (i.e.: `self.footnotes = {}`).
+However, as many extensions do not have a need for `reset`, `reset` is only
+called on extensions that are registered.
+
+To register an extension, call `md.registerExtension` from within your
+`extendMarkdown` method:
+
+```python
+def extendMarkdown(self, md):
+ md.registerExtension(self)
+ # insert processors and patterns here
+```
+
+Then, each time `reset` is called on the Markdown instance, the `reset`
+method of each registered extension will be called as well. You should also
+note that `reset` will be called on each registered extension after it is
+initialized the first time. Keep that in mind when over-riding the extension's
+`reset` method.
+
+### Configuration Settings {: #configsettings }
+
+If an extension uses any parameters that the user may want to change,
+those parameters should be stored in `self.config` of your
+`markdown.extensions.Extension` class in the following format:
+
+```python
+class MyExtension(markdown.extensions.Extension):
+ def __init__(self, **kwargs):
+ self.config = {'option1' : ['value1', 'description1'],
+ 'option2' : ['value2', 'description2'] }
+ super(MyExtension, self).__init__(**kwargs)
+```
+
+When implemented this way the configuration parameters can be over-ridden at
+run time (thus the call to `super`). For example:
+
+```python
+markdown.Markdown(extensions=[MyExtension(option1='other value'])
+```
+
+Note that if a keyword is passed in that is not already defined in
+`self.config`, then a `KeyError` is raised.
+
+The `markdown.extensions.Extension` class and its subclasses have the
+following methods available to assist in working with configuration settings:
+
+* **`getConfig(key [, default])`**:
+
+ Returns the stored value for the given `key` or `default` if the `key`
+ does not exist. If not set, `default` returns an empty string.
+
+* **`getConfigs()`**:
+
+ Returns a dict of all key/value pairs.
+
+* **`getConfigInfo()`**:
+
+ Returns all configuration descriptions as a list of tuples.
+
+* **`setConfig(key, value)`**:
+
+ Sets a configuration setting for `key` with the given `value`. If `key` is
+ unknown, a `KeyError` is raised. If the previous value of `key` was
+ a Boolean value, then `value` is converted to a Boolean value. If
+ the previous value of `key` is `None`, then `value` is converted to
+ a Boolean value except when it is `None`. No conversion takes place
+ when the previous value of `key` is a string.
+
+* **`setConfigs(items)`**:
+
+ Sets multiple configuration settings given a dict of key/value pairs.
+
+### 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
+extensions.
+
+For example:
+
+```python
+import markdown
+from path.to.module import MyExtension
+md = markdown.Markdown(extensions=[MyExtension(option='value')])
+```
+
+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).
+
+#### 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=['myextension'])
+```
+
+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.
+
+Therefore, if you were to import the class like this:
+
+```python
+from path.to.module import MyExtension
+```
+
+Then the extension can be loaded as follows:
+
+```python
+markdown.markdown(text, extensions=['path.to.module:MyExtension'])
+```
+
+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.
+
+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
+not want to require that your users include the class name in their string, you
+must define only one extension per module and that module must contain a
+module-level function called `makeExtension` that accepts `**kwargs` and returns
+an extension instance.
+
+For example:
+
+```python
+class MyExtension(markdown.extensions.Extension)
+ # Define extension here...
+
+def makeExtension(**kwargs):
+ return MyExtension(**kwargs)
+```
+
+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
+[Treeprocessors]: #treeprocessors
+[Postprocessors]: #postprocessors
+[BlockParser]: #blockparser
+[workingwithetree]: #working_with_et
+[Integrating your code into Markdown]: #integrating_into_markdown
+[extendMarkdown]: #extendmarkdown
+[Registry]: #registry
+[registerExtension]: #registerextension
+[Config Settings]: #configsettings
+[makeExtension]: #makeextension
+[ElementTree]: http://effbot.org/zone/element-index.htm
+[Available Extensions]: index.md
+[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/
+++ /dev/null
-title: Extensions API
-prev_title: WikiLinks Extension
-prev_url: wikilinks.html
-next_title: Test Suite
-next_url: ../test_suite.html
-
-Writing Extensions for Python-Markdown
-======================================
-
-Python-Markdown includes an API for extension writers to plug their own
-custom functionality and/or syntax into the parser. There are Preprocessors
-which allow you to alter the source before it is passed to the parser,
-inline patterns which allow you to add, remove or override the syntax of
-any inline elements, and Postprocessors which allow munging of the
-output of the parser before it is returned. If you really want to dive in,
-there are also Blockprocessors which are part of the core BlockParser.
-
-As the parser builds an [ElementTree][ElementTree] object which is later rendered
-as Unicode text, there are also some helpers provided to ease manipulation of
-the tree. Each part of the API is discussed in its respective section below.
-Additionally, reading the source of some [Available Extensions][] may be
-helpful. For example, the [Footnotes][] extension uses most of the features
-documented here.
-
-Preprocessors {: #preprocessors }
----------------------------------
-
-Preprocessors munge the source text before it is passed into the Markdown
-core. This is an excellent place to clean up bad syntax, extract things the
-parser may otherwise choke on and perhaps even store it for later retrieval.
-
-Preprocessors should inherit from `markdown.preprocessors.Preprocessor` and
-implement a `run` method with one argument `lines`. The `run` method of
-each Preprocessor will be passed the entire source text as a list of Unicode
-strings. Each string will contain one line of text. The `run` method should
-return either that list, or an altered list of Unicode strings.
-
-A pseudo example:
-
- from markdown.preprocessors import Preprocessor
-
- class MyPreprocessor(Preprocessor):
- def run(self, lines):
- new_lines = []
- for line in lines:
- m = MYREGEX.match(line)
- if m:
- # do stuff
- else:
- new_lines.append(line)
- return new_lines
-
-Inline Patterns {: #inlinepatterns }
-------------------------------------
-
-Inline Patterns implement the inline HTML element syntax for Markdown such as
-`*emphasis*` or `[links](http://example.com)`. Pattern objects should be
-instances of classes that inherit from `markdown.inlinepatterns.Pattern` or
-one of its children. Each pattern object uses a single regular expression and
-must have the following methods:
-
-* **`getCompiledRegExp()`**:
-
- Returns a compiled regular expression.
-
-* **`handleMatch(m)`**:
-
- Accepts a match object and returns an ElementTree element of a plain
- Unicode string.
-
-Note that any regular expression returned by `getCompiledRegExp` must capture
-the whole block. Therefore, they should all start with `r'^(.*?)'` and end
-with `r'(.*?)!'`. When using the default `getCompiledRegExp()` method
-provided in the `Pattern` you can pass in a regular expression without that
-and `getCompiledRegExp` will wrap your expression for you and set the
-`re.DOTALL` and `re.UNICODE` flags. This means that the first group of your
-match will be `m.group(2)` as `m.group(1)` will match everything before the
-pattern.
-
-For an example, consider this simplified emphasis pattern:
-
- from markdown.inlinepatterns import Pattern
- from markdown.util import etree
-
- class EmphasisPattern(Pattern):
- def handleMatch(self, m):
- el = etree.Element('em')
- el.text = m.group(3)
- return el
-
-As discussed in [Integrating Your Code Into Markdown][], an instance of this
-class will need to be provided to Markdown. That instance would be created
-like so:
-
- # an oversimplified regex
- MYPATTERN = r'\*([^*]+)\*'
- # pass in pattern and create instance
- emphasis = EmphasisPattern(MYPATTERN)
-
-Actually it would not be necessary to create that pattern (and not just because
-a more sophisticated emphasis pattern already exists in Markdown). The fact is,
-that example pattern is not very DRY. A pattern for `**strong**` text would
-be almost identical, with the exception that it would create a 'strong' element.
-Therefore, Markdown provides a number of generic pattern classes that can
-provide some common functionality. For example, both emphasis and strong are
-implemented with separate instances of the `SimpleTagPattern` listed below.
-Feel free to use or extend any of the Pattern classes found at
-`markdown.inlinepatterns`.
-
-**Generic Pattern Classes**
-
-* **`SimpleTextPattern(pattern)`**:
-
- Returns simple text of `group(2)` of a `pattern`.
-
-* **`SimpleTagPattern(pattern, tag)`**:
-
- Returns an element of type "`tag`" with a text attribute of `group(3)`
- of a `pattern`. `tag` should be a string of a HTML element (i.e.: 'em').
-
-* **`SubstituteTagPattern(pattern, tag)`**:
-
- Returns an element of type "`tag`" with no children or text (i.e.: `br`).
-
-There may be other Pattern classes in the Markdown source that you could extend
-or use as well. Read through the source and see if there is anything you can
-use. You might even get a few ideas for different approaches to your specific
-situation.
-
-Treeprocessors {: #treeprocessors }
------------------------------------
-
-Treeprocessors manipulate an ElementTree object after it has passed through the
-core BlockParser. This is where additional manipulation of the tree takes
-place. Additionally, the InlineProcessor is a Treeprocessor which steps through
-the tree and runs the Inline Patterns on the text of each Element in the tree.
-
-A Treeprocessor should inherit from `markdown.treeprocessors.Treeprocessor`,
-over-ride the `run` method which takes one argument `root` (an ElementTree
-object) and either modifies that root element and returns `None` or returns a
-new ElementTree object.
-
-A pseudo example:
-
- from markdown.treeprocessors import Treeprocessor
-
- class MyTreeprocessor(Treeprocessor):
- def run(self, root):
- root.text = 'modified content'
-
-Note that Python class methods return `None` by default when no `return`
-statement is defined. Additionally all Python variables refer to objects by
-reference. Therefore, the above `run` method modifies the `root` element
-in place and returns `None`. The changes made to the `root` element and its
-children are retained.
-
-Some may be inclined to return the modified `root` element. While that would
-work, it would cause a copy of the entire ElementTree to be generated each
-time the Treeprocessor is run. Therefore, it is generally expected that
-the `run` method would only return `None` or a new ElementTree object.
-
-For specifics on manipulating the ElementTree, see
-[Working with the ElementTree][workingwithetree] below.
-
-Postprocessors {: #postprocessors }
------------------------------------
-
-Postprocessors manipulate the document after the ElementTree has been
-serialized into a string. Postprocessors should be used to work with the
-text just before output.
-
-A Postprocessor should inherit from `markdown.postprocessors.Postprocessor`
-and over-ride the `run` method which takes one argument `text` and returns
-a Unicode string.
-
-Postprocessors are run after the ElementTree has been serialized back into
-Unicode text. For example, this may be an appropriate place to add a table of
-contents to a document:
-
- from markdown.postprocessors import Postprocessor
-
- class TocPostprocessor(Postprocessor):
- def run(self, text):
- return MYMARKERRE.sub(MyToc, text)
-
-BlockParser {: #blockparser }
------------------------------
-
-Sometimes, Preprocessors, Treeprocessors, Postprocessors, and Inline Patterns
-are not going to do what you need. Perhaps you want a new type of block type
-that needs to be integrated into the core parsing. In such a situation, you can
-add/change/remove functionality of the core `BlockParser`. The BlockParser is
-composed of a number of Blockprocessors. The BlockParser steps through each
-block of text (split by blank lines) and passes each block to the appropriate
-Blockprocessor. That Blockprocessor parses the block and adds it to the
-ElementTree. The
-[Definition Lists][] extension would be a good example of an extension that
-adds/modifies Blockprocessors.
-
-A Blockprocessor should inherit from `markdown.blockprocessors.BlockProcessor`
-and implement both the `test` and `run` methods.
-
-The `test` method is used by BlockParser to identify the type of block.
-Therefore the `test` method must return a Boolean value. If the test returns
-`True`, then the BlockParser will call that Blockprocessor's `run` method.
-If it returns `False`, the BlockParser will move on to the next
-Blockprocessor.
-
-The **`test`** method takes two arguments:
-
-* **`parent`**: The parent ElementTree Element of the block. This can be useful
- as the block may need to be treated differently if it is inside a list, for
- example.
-
-* **`block`**: A string of the current block of text. The test may be a
- simple string method (such as `block.startswith(some_text)`) or a complex
- regular expression.
-
-The **`run`** method takes two arguments:
-
-* **`parent`**: A pointer to the parent ElementTree Element of the block. The run
- method will most likely attach additional nodes to this parent. Note that
- nothing is returned by the method. The ElementTree object is altered in place.
-
-* **`blocks`**: A list of all remaining blocks of the document. Your run
- method must remove (pop) the first block from the list (which it altered in
- place - not returned) and parse that block. You may find that a block of text
- legitimately contains multiple block types. Therefore, after processing the
- first type, your processor can insert the remaining text into the beginning
- of the `blocks` list for future parsing.
-
-Please be aware that a single block can span multiple text blocks. For example,
-The official Markdown syntax rules state that a blank line does not end a
-Code Block. If the next block of text is also indented, then it is part of
-the previous block. Therefore, the BlockParser was specifically designed to
-address these types of situations. If you notice the `CodeBlockProcessor`,
-in the core, you will note that it checks the last child of the `parent`.
-If the last child is a code block (`<pre><code>...</code></pre>`), then it
-appends that block to the previous code block rather than creating a new
-code block.
-
-Each Blockprocessor has the following utility methods available:
-
-* **`lastChild(parent)`**:
-
- Returns the last child of the given ElementTree Element or `None` if it
- had no children.
-
-* **`detab(text)`**:
-
- Removes one level of indent (four spaces by default) from the front of each
- line of the given text string.
-
-* **`looseDetab(text, level)`**:
-
- Removes "level" levels of indent (defaults to 1) from the front of each line
- of the given text string. However, this methods allows secondary lines to
- not be indented as does some parts of the Markdown syntax.
-
-Each Blockprocessor also has a pointer to the containing BlockParser instance at
-`self.parser`, which can be used to check or alter the state of the parser.
-The BlockParser tracks it's state in a stack at `parser.state`. The state
-stack is an instance of the `State` class.
-
-**`State`** is a subclass of `list` and has the additional methods:
-
-* **`set(state)`**:
-
- Set a new state to string `state`. The new state is appended to the end
- of the stack.
-
-* **`reset()`**:
-
- Step back one step in the stack. The last state at the end is removed from
- the stack.
-
-* **`isstate(state)`**:
-
- Test that the top (current) level of the stack is of the given string
- `state`.
-
-Note that to ensure that the state stack does not become corrupted, each time a
-state is set for a block, that state *must* be reset when the parser finishes
-parsing that block.
-
-An instance of the **`BlockParser`** is found at `Markdown.parser`.
-`BlockParser` has the following methods:
-
-* **`parseDocument(lines)`**:
-
- Given a list of lines, an ElementTree object is returned. This should be
- passed an entire document and is the only method the `Markdown` class
- calls directly.
-
-* **`parseChunk(parent, text)`**:
-
- Parses a chunk of markdown text composed of multiple blocks and attaches
- those blocks to the `parent` Element. The `parent` is altered in place
- and nothing is returned. Extensions would most likely use this method for
- block parsing.
-
-* **`parseBlocks(parent, blocks)`**:
-
- Parses a list of blocks of text and attaches those blocks to the `parent`
- Element. The `parent` is altered in place and nothing is returned. This
- method will generally only be used internally to recursively parse nested
- blocks of text.
-
-While is is not recommended, an extension could subclass or completely replace
-the `BlockParser`. The new class would have to provide the same public API.
-However, be aware that other extensions may expect the core parser provided
-and will not work with such a drastically different parser.
-
-Working with the ElementTree {: #working_with_et }
---------------------------------------------------
-
-As mentioned, the Markdown parser converts a source document to an
-[ElementTree][ElementTree] object before serializing that back to Unicode text.
-Markdown has provided some helpers to ease that manipulation within the context
-of the Markdown module.
-
-First, to get access to the ElementTree module import ElementTree from
-`markdown` rather than importing it directly. This will ensure you are using
-the same version of ElementTree as markdown. The module is found at
-`markdown.util.etree` within Markdown.
-
- from markdown.util import etree
-
-`markdown.util.etree` tries to import ElementTree from any known location,
-first as a standard library module (from `xml.etree` in Python 2.5), then as
-a third party package (ElementTree). In each instance, `cElementTree` is
-tried first, then ElementTree if the faster C implementation is not
-available on your system.
-
-Sometimes you may want text inserted into an element to be parsed by
-[Inline Patterns][]. In such a situation, simply insert the text as you normally
-would and the text will be automatically run through the Inline Patterns.
-However, if you do *not* want some text to be parsed by Inline Patterns,
-then insert the text as an `AtomicString`.
-
- from markdown.util import AtomicString
- some_element.text = AtomicString(some_text)
-
-Here's a basic example which creates an HTML table (note that the contents of
-the second cell (`td2`) will be run through Inline Patterns latter):
-
- table = etree.Element("table")
- table.set("cellpadding", "2") # Set cellpadding to 2
- tr = etree.SubElement(table, "tr") # Add child tr to table
- td1 = etree.SubElement(tr, "td") # Add child td1 to tr
- td1.text = markdown.util.AtomicString("Cell content") # Add plain text content
- td2 = etree.SubElement(tr, "td") # Add second td to tr
- td2.text = "*text* with **inline** formatting." # Add markup text
- table.tail = "Text after table" # Add text after table
-
-You can also manipulate an existing tree. Consider the following example which
-adds a `class` attribute to `<a>` elements:
-
- def set_link_class(self, element):
- for child in element:
- if child.tag == "a":
- child.set("class", "myclass") #set the class attribute
- set_link_class(child) # run recursively on children
-
-For more information about working with ElementTree see the ElementTree
-[Documentation](http://effbot.org/zone/element-index.htm)
-([Python Docs](http://docs.python.org/lib/module-xml.etree.ElementTree.html)).
-
-Integrating Your Code Into Markdown {: #integrating_into_markdown }
--------------------------------------------------------------------
-
-Once you have the various pieces of your extension built, you need to tell
-Markdown about them and ensure that they are run in the proper sequence.
-Markdown accepts an `Extension` instance for each extension. Therefore, you
-will need to define a class that extends `markdown.extensions.Extension` and
-over-rides the `extendMarkdown` method. Within this class you will manage
-configuration options for your extension and attach the various processors and
-patterns to the Markdown instance.
-
-It is important to note that the order of the various processors and patterns
-matters. For example, if we replace `http://...` links with `<a>` elements,
-and *then* try to deal with inline HTML, we will end up with a mess.
-Therefore, the various types of processors and patterns are stored within an
-instance of the Markdown class in [OrderedDict][]s. Your `Extension` class
-will need to manipulate those OrderedDicts appropriately. You may insert
-instances of your processors and patterns into the appropriate location in an
-OrderedDict, remove a built-in instance, or replace a built-in instance with
-your own.
-
-### `extendMarkdown` {: #extendmarkdown }
-
-The `extendMarkdown` method of a `markdown.extensions.Extension` class
-accepts two arguments:
-
-* **`md`**:
-
- A pointer to the instance of the Markdown class. You should use this to
- access the [OrderedDict][]s of processors and patterns. They are found
- under the following attributes:
-
- * `md.preprocessors`
- * `md.inlinePatterns`
- * `md.parser.blockprocessors`
- * `md.treeprocessors`
- * `md.postprocessors`
-
- Some other things you may want to access in the markdown instance are:
-
- * `md.htmlStash`
- * `md.output_formats`
- * `md.set_output_format()`
- * `md.output_format`
- * `md.serializer`
- * `md.registerExtension()`
- * `md.html_replacement_text`
- * `md.tab_length`
- * `md.enable_attributes`
- * `md.smart_emphasis`
-
-* **`md_globals`**:
-
- Contains all the various global variables within the markdown module.
-
-!!! Warning
- With access to the above items, theoretically you have the option to
- change anything through various [monkey_patching][] techniques. However,
- you should be aware that the various undocumented parts of markdown may
- change without notice and your monkey_patches may break with a new release.
- Therefore, what you really should be doing is inserting processors and
- patterns into the markdown pipeline. Consider yourself warned!
-
-[monkey_patching]: http://en.wikipedia.org/wiki/Monkey_patch
-
-A simple example:
-
- from markdown.extensions import Extension
-
- class MyExtension(Extension):
- def extendMarkdown(self, md, md_globals):
- # Insert instance of 'mypattern' before 'references' pattern
- md.inlinePatterns.add('mypattern', MyPattern(md), '<references')
-
-### OrderedDict {: #ordereddict }
-
-An OrderedDict is a dictionary like object that retains the order of it's
-items. The items are ordered in the order in which they were appended to
-the OrderedDict. However, an item can also be inserted into the OrderedDict
-in a specific location in relation to the existing items.
-
-Think of OrderedDict as a combination of a list and a dictionary as it has
-methods common to both. For example, you can get and set items using the
-`od[key] = value` syntax and the methods `keys()`, `values()`, and
-`items()` work as expected with the keys, values and items returned in the
-proper order. At the same time, you can use `insert()`, `append()`, and
-`index()` as you would with a list.
-
-Generally speaking, within Markdown extensions you will be using the special
-helper method `add()` to add additional items to an existing OrderedDict.
-
-The `add()` method accepts three arguments:
-
-* **`key`**: A string. The key is used for later reference to the item.
-
-* **`value`**: The object instance stored in this item.
-
-* **`location`**: Optional. The items location in relation to other items.
-
- Note that the location can consist of a few different values:
-
- * The special strings `"_begin"` and `"_end"` insert that item at the
- beginning or end of the OrderedDict respectively.
-
- * A less-than sign (`<`) followed by an existing key (i.e.:
- `"<somekey"`) inserts that item before the existing key.
-
- * A greater-than sign (`>`) followed by an existing key (i.e.:
- `">somekey"`) inserts that item after the existing key.
-
-Consider the following example:
-
- >>> from markdown.odict import OrderedDict
- >>> od = OrderedDict()
- >>> od['one'] = 1 # The same as: od.add('one', 1, '_begin')
- >>> od['three'] = 3 # The same as: od.add('three', 3, '>one')
- >>> od['four'] = 4 # The same as: od.add('four', 4, '_end')
- >>> od.items()
- [("one", 1), ("three", 3), ("four", 4)]
-
-Note that when building an OrderedDict in order, the extra features of the
-`add` method offer no real value and are not necessary. However, when
-manipulating an existing OrderedDict, `add` can be very helpful. So let's
-insert another item into the OrderedDict.
-
- >>> od.add('two', 2, '>one') # Insert after 'one'
- >>> od.values()
- [1, 2, 3, 4]
-
-Now let's insert another item.
-
- >>> od.add('two-point-five', 2.5, '<three') # Insert before 'three'
- >>> od.keys()
- ["one", "two", "two-point-five", "three", "four"]
-
-Note that we also could have set the location of "two-point-five" to be 'after two'
-(i.e.: `'>two'`). However, it's unlikely that you will have control over the
-order in which extensions will be loaded, and this could affect the final
-sorted order of an OrderedDict. For example, suppose an extension adding
-"two-point-five" in the above examples was loaded before a separate extension
-which adds 'two'. You may need to take this into consideration when adding your
-extension components to the various markdown OrderedDicts.
-
-Once an OrderedDict is created, the items are available via key:
-
- MyNode = od['somekey']
-
-Therefore, to delete an existing item:
-
- del od['somekey']
-
-To change the value of an existing item (leaving location unchanged):
-
- od['somekey'] = MyNewObject()
-
-To change the location of an existing item:
-
- t.link('somekey', '<otherkey')
-
-### registerExtension {: #registerextension }
-
-Some extensions may need to have their state reset between multiple runs of the
-Markdown class. For example, consider the following use of the [Footnotes][]
-extension:
-
- md = markdown.Markdown(extensions=['footnotes'])
- html1 = md.convert(text_with_footnote)
- md.reset()
- html2 = md.convert(text_without_footnote)
-
-Without calling `reset`, the footnote definitions from the first document will
-be inserted into the second document as they are still stored within the class
-instance. Therefore the `Extension` class needs to define a `reset` method
-that will reset the state of the extension (i.e.: `self.footnotes = {}`).
-However, as many extensions do not have a need for `reset`, `reset` is only
-called on extensions that are registered.
-
-To register an extension, call `md.registerExtension` from within your
-`extendMarkdown` method:
-
-
- def extendMarkdown(self, md, md_globals):
- md.registerExtension(self)
- # insert processors and patterns here
-
-Then, each time `reset` is called on the Markdown instance, the `reset`
-method of each registered extension will be called as well. You should also
-note that `reset` will be called on each registered extension after it is
-initialized the first time. Keep that in mind when over-riding the extension's
-`reset` method.
-
-### Configuration Settings {: #configsettings }
-
-If an extension uses any parameters that the user may want to change,
-those parameters should be stored in `self.config` of your
-`markdown.extensions.Extension` class in the following format:
-
- class MyExtension(markdown.extensions.Extension):
- def __init__(self, **kwargs):
- self.config = {'option1' : ['value1', 'description1'],
- 'option2' : ['value2', 'description2'] }
- super(MyExtension, self).__init__(**kwargs)
-
-When implemented this way the configuration parameters can be over-ridden at
-run time (thus the call to `super`). For example:
-
- markdown.Markdown(extensions=[MyExtension(option1='other value'])
-
-Note that if a keyword is passed in that is not already defined in
-`self.config`, then a `KeyError` is raised.
-
-The `markdown.extensions.Extension` class and its subclasses have the
-following methods available to assist in working with configuration settings:
-
-* **`getConfig(key [, default])`**:
-
- Returns the stored value for the given `key` or `default` if the `key`
- does not exist. If not set, `default` returns an empty string.
-
-* **`getConfigs()`**:
-
- Returns a dict of all key/value pairs.
-
-* **`getConfigInfo()`**:
-
- Returns all configuration descriptions as a list of tuples.
-
-* **`setConfig(key, value)`**:
-
- Sets a configuration setting for `key` with the given `value`. If `key` is
- unknown, a `KeyError` is raised. If the previous value of `key` was
- a Boolean value, then `value` is converted to a Boolean value. If
- the previous value of `key` is `None`, then `value` is converted to
- a Boolean value except when it is `None`. No conversion takes place
- when the previous value of `key` is a string.
-
-* **`setConfigs(items)`**:
-
- Sets multiple configuration settings given a dict of key/value pairs.
-
-### `makeExtension` {: #makeextension }
-
-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
-extensions.
-
-For example:
-
- import markdown
- import myextension
- myext = myextension.MyExtension(option='value')
- md = markdown.Markdown(extensions=[myext])
-
-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).
-
-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.
-
-The string can also include the name of the class separated by a colon.
-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"
-
-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.
-
-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 not want to require
-that your users include the class name in their string, you must define only one extension
-per module and that module must contain a module-level function called `makeExtension`
-that accepts `**kwargs` and returns an extension instance.
-
-For example:
-
- class MyExtension(markdown.extensions.Extension)
- # Define extension here...
-
- 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.
-
-
-[Preprocessors]: #preprocessors
-[Inline Patterns]: #inlinepatterns
-[Treeprocessors]: #treeprocessors
-[Postprocessors]: #postprocessors
-[BlockParser]: #blockparser
-[workingwithetree]: #working_with_et
-[Integrating your code into Markdown]: #integrating_into_markdown
-[extendMarkdown]: #extendmarkdown
-[OrderedDict]: #ordereddict
-[registerExtension]: #registerextension
-[Config Settings]: #configsettings
-[makeExtension]: #makeextension
-[ElementTree]: http://effbot.org/zone/element-index.htm
-[Available Extensions]: index.html
-[Footnotes]: footnotes.html
-[Definition Lists]: definition_lists.html
-[library reference]: ../reference.html
--- /dev/null
+title: Attribute Lists Extension
+
+# Attribute Lists
+
+## Summary
+
+The Attribute Lists extension adds a syntax to define attributes on the various
+HTML elements in markdown's output.
+
+This extension is included in the standard Markdown library.
+
+## Syntax
+
+The basic syntax was inspired by [Maruku][]'s Attribute Lists feature.
+
+[Maruku]: http://maruku.rubyforge.org/proposal.html#attribute_lists
+
+### The List
+
+An example attribute list might look like this:
+
+```text
+{: #someid .someclass somekey='some value' }
+```
+
+A word which starts with a hash (`#`) will set the id of an element.
+
+A word which starts with a dot (`.`) will be added to the list of classes
+assigned to an element.
+
+A key/value pair (`somekey='some value'`) will assign that pair to the element.
+
+Be aware that while the dot syntax will add to a class, using key/value pairs
+will always override the previously defined attribute. Consider the following:
+
+```text
+{: #id1 .class1 id=id2 class="class2 class3" .class4 }
+```
+
+The above example would result in the following attributes being defined:
+
+```text
+id="id2" class="class2 class3 class4"
+```
+
+### Block Level
+
+To define attributes for a block level element, the attribute list should
+be defined on the last line of the block by itself.
+
+```text
+This is a paragraph.
+{: #an_id .a_class }
+```
+
+The above results in the following output:
+
+```html
+<p id="an_id" class="a_class">This is a paragraph.</p>
+```
+
+The one exception is headers, as they are only ever allowed on one line.
+
+```text
+A setext style header {: #setext}
+=================================
+
+### A hash style header ### {: #hash }
+```
+
+The above results in the following output:
+
+```html
+<h1 id="setext">A setext style header</h1>
+<h3 id="hash">A hash style header</h3>
+```
+
+### Inline
+
+To define attributes on inline elements, the attribute list should be defined
+immediately after the inline element with no white space.
+
+```text
+[link](http://example.com){: class="foo bar" title="Some title!" }
+```
+
+The above results in the following output:
+
+```html
+<p><a href="http://example.com" class="foo bar" title="Some title!">link</a></p>
+```
+
+## Usage
+
+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.
+++ /dev/null
-title: Attribute Lists Extension
-prev_title: Abbreviations Extension
-prev_url: abbreviations.html
-next_title: Definition Lists Extension
-next_url: definition_lists.html
-
-Attribute Lists
-===============
-
-Summary
--------
-
-The Attribute Lists extension adds a syntax to define attributes on the various
-HTML elements in markdown's output.
-
-This extension is included in the standard Markdown library.
-
-Syntax
-------
-
-The basic syntax was inspired by [Maruku][]'s Attribute Lists feature.
-
-[Maruku]: http://maruku.rubyforge.org/proposal.html#attribute_lists
-
-### The List ###
-
-An example attribute list might look like this:
-
- {: #someid .someclass somekey='some value' }
-
-A word which starts with a hash (`#`) will set the id of an element.
-
-A word which starts with a dot (`.`) will be added to the list of classes
-assigned to an element.
-
-A key/value pair (`somekey='some value'`) will assign that pair to the element.
-
-Be aware that while the dot syntax will add to a class, using key/value pairs
-will always override the previously defined attribute. Consider the following:
-
- {: #id1 .class1 id=id2 class="class2 class3" .class4 }
-
-The above example would result in the following attributes being defined:
-
- id="id2" class="class2 class3 class4"
-
-### Block Level ###
-
-To define attributes for a block level element, the attribute list should
-be defined on the last line of the block by itself.
-
- This is a paragraph.
- {: #an_id .a_class }
-
-The above results in the following output:
-
- <p id="an_id" class="a_class">This is a paragraph.</p>
-
-The one exception is headers, as they are only ever allowed on one line.
-
- A setext style header {: #setext}
- =================================
-
- ### A hash style header ### {: #hash }
-
-The above results in the following output:
-
- <h1 id="setext">A setext style header</h1>
- <h3 id="hash">A hash style header</h3>
-
-### Inline ###
-
-To define attributes on inline elements, the attribute list should be defined
-immediately after the inline element with no white space.
-
- [link](http://example.com){: class="foo bar" title="Some title!" }
-
-The above results in the following output:
-
- <p><a href="http://example.com" class="foo bar" title="Some title!">link</a></p>
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.attr_list`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
--- /dev/null
+title: CodeHilite Extension
+
+# CodeHilite
+
+## Summary
+
+The CodeHilite extension adds code/syntax highlighting to standard
+Python-Markdown code blocks using [Pygments][].
+
+[Pygments]: http://pygments.org/
+
+This extension is included in the standard Markdown library.
+
+## Setup
+
+### Step 1: Download and Install Pygments
+
+You will also need to [download][dl] and install the Pygments package on your
+`PYTHONPATH`. The CodeHilite extension will produce HTML output without
+Pygments, but it won't highlight anything (same behavior as setting
+`use_pygments` to `False`).
+
+[dl]: http://pygments.org/download/
+
+### Step 2: Add CSS Classes
+
+You will need to define the appropriate CSS classes with appropriate rules.
+The CSS rules either need to be defined in or linked from the header of your
+HTML templates. Pygments can generate CSS rules for you. Just run the following
+command from the command line:
+
+```bash
+pygmentize -S default -f html -a .codehilite > styles.css
+```
+
+If you are using a different `css_class` (default: `.codehilite`), then
+set the value of the `-a` option to that class name. The CSS rules will be
+written to the `styles.css` file which you can copy to your site and link from
+your HTML templates.
+
+If you would like to use a different theme, swap out `default` for the desired
+theme. For a list of themes installed on your system (additional themes can be
+installed via Pygments plugins), run the following command:
+
+```bash
+pygmentize -L style
+```
+
+See Pygments' excellent [documentation] for more details. If no language is
+defined, Pygments will attempt to guess the language. When that fails, the code
+block will not be highlighted.
+
+!!! note "See Also"
+
+ GitHub user [richeland] has provided a number of different [CSS style
+ sheets][rich] which work with Pygments along with a [preview] of each theme.
+ The `css_class` used is the same as the default value for that option
+ (`.codehilite`). However, the Python-Markdown project makes no guarantee that
+ richeland's CSS styles will work with the version of Pygments you are using.
+ To ensure complete compatibility, you should generate the CSS rules from
+ your own installation of Pygments.
+
+[richeland]: https://github.com/richleland
+[rich]: https://github.com/richleland/pygments-css
+[preview]: http://richleland.github.io/pygments-css/
+[documentation]: http://pygments.org/docs/
+
+## Syntax
+
+The CodeHilite extension follows the same [syntax][] as regular Markdown code
+blocks, with one exception. The highlighter needs to know what language to use for
+the code block. There are three ways to tell the highlighter what language the
+code block contains and each one has a different result.
+
+!!! Note
+ The format of the language identifier only effects the display of line numbers
+ if `linenums` is set to `None` (the default). If set to `True` or `False`
+ (see [Usage](#usage) below) the format of the identifier has no effect on the
+ display of line numbers -- it only serves as a means to define the language
+ of the code block.
+
+[syntax]: http://daringfireball.net/projects/markdown/syntax#precode
+
+### Shebang (with path)
+
+If the first line of the code block contains a shebang, the language is derived
+from that and line numbers are used.
+
+```md
+ #!/usr/bin/python
+ # Code goes here ...
+```
+
+Will result in:
+
+ #!/usr/bin/python
+ # Code goes here ...
+
+### Shebang (no path)
+
+If the first line contains a shebang, but the shebang line does not contain a
+path (a single `/` or even a space), then that line is removed from the code
+block before processing. Line numbers are used.
+
+```md
+ #!python
+ # Code goes here ...
+```
+
+Will result in:
+
+ #!python
+ # Code goes here ...
+
+### Colons
+
+If the first line begins with three or more colons, the text following the
+colons identifies the language. The first line is removed from the code block
+before processing and line numbers are not used.
+
+```md
+ :::python
+ # Code goes here ...
+```
+
+Will result in:
+
+ :::python
+ # Code goes here ...
+
+Certain lines can be selected for emphasis with the colon syntax. When
+using Pygments' default CSS styles, emphasized lines have a yellow background.
+This is useful to direct the reader's attention to specific lines.
+
+```md
+ :::python hl_lines="1 3"
+ # This line is emphasized
+ # This line isn't
+ # This line is emphasized
+```
+
+Will result in:
+
+ :::python hl_lines="1 3"
+ # This line is emphasized
+ # This line isn't
+ # This line is emphasized
+
+!!! Note
+ `hl_lines` is named for Pygments' option meaning "highlighted lines".
+
+### When No Language is Defined
+
+CodeHilite is completely backwards compatible so that if a code block is
+encountered that does not define a language, the block is simply wrapped in
+`<pre>` tags and output.
+
+```md
+ # Code goes here ...
+```
+
+Will result in:
+
+ # Code goes here ...
+
+Lets see the source for that:
+
+```html
+<div class="codehilite"><pre><code># Code goes here ...
+</code></pre></div>
+```
+
+!!! Note
+ When no language is defined, the Pygments highlighting engine will try to guess
+ the language (unless `guess_lang` is set to `False`). Upon failure, the same
+ behavior will happen as described above.
+
+## Usage
+
+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.
+
+The following options are provided to configure the output:
+
+* **`linenums`**:
+ Use line numbers. Possible values are `True` for yes, `False` for no and
+ `None` for auto. Defaults to `None`.
+
+ Using `True` will force every code block to have line numbers, even when
+ using colons (`:::`) for language identification.
+
+ Using `False` will turn off all line numbers, even when using shebangs
+ (`#!`) for language identification.
+
+* **`guess_lang`**:
+ Automatic language detection. Defaults to `True`.
+
+ Using `False` will prevent Pygments from guessing the language, and thus
+ highlighting blocks only when you explicitly set the language.
+
+* **`css_class`**:
+ Set CSS class name for the wrapper `<div>` tag. Defaults to
+ `codehilite`.
+
+* **`pygments_style`**:
+ Pygments HTML Formatter Style (`ColorScheme`). Defaults to `default`.
+
+ !!! Note
+ This is useful only when `noclasses` is set to `True`, otherwise the
+ CSS styles must be provided by the end user.
+
+* **`noclasses`**:
+ Use inline styles instead of CSS classes. Defaults to `False`.
+
+* **`use_pygments`**:
+ Defaults to `True`. Set to `False` to disable the use of Pygments.
+ If a language is defined for a code block, it will be assigned to the
+ `<code>` tag as a class in the manner suggested by the [HTML5 spec][spec]
+ (alternate output will not be entertained) and might be used by a JavaScript
+ library in the browser to highlight the code block.
+
+[spec]: http://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
+++ /dev/null
-title: CodeHilite Extension
-prev_title: Admonition Extension
-prev_url: admonition.html
-next_title: HeaderId Extension
-next_url: header_id.html
-
-CodeHilite
-==========
-
-Summary
--------
-
-The CodeHilite extension adds code/syntax highlighting to standard
-Python-Markdown code blocks using [Pygments][].
-
-[Pygments]: http://pygments.org/
-
-This extension is included in the standard Markdown library.
-
-Setup
------
-
-You will also need to [download][dl] and install the Pygments package on your
-`PYTHONPATH`. You will need to determine the appropriate CSS classes and create
-appropriate rules for them, which are either defined in or linked from the
-header of your HTML templates. See the excellent [documentation][] for more
-details. If no language is defined, Pygments will attempt to guess the
-language. When that fails, the code block will not be highlighted.
-
-[dl]: http://pygments.org/download/
-[documentation]: http://pygments.org/docs
-
-!!! Note
- The CSS and/or JavaScript is not included as part of this extension
- but must be provided by the end user. The Pygments project provides
- default CSS styles which you may find to be a useful starting point.
-
-Syntax
-------
-
-The CodeHilite extension follows the same [syntax][] as regular Markdown code
-blocks, with one exception. The highlighter needs to know what language to use for
-the code block. There are three ways to tell the highlighter what language the
-code block contains and each one has a different result.
-
-!!! Note
- The format of the language identifier only effects the display of line numbers
- if `linenums` is set to `None` (the default). If set to `True` or `False`
- (see [Usage](#usage) below) the format of the identifier has no effect on the
- display of line numbers -- it only serves as a means to define the language
- of the code block.
-
-[syntax]: http://daringfireball.net/projects/markdown/syntax#precode
-
-### Shebang (with path) ###
-
-If the first line of the code block contains a shebang, the language is derived
-from that and line numbers are used.
-
- #!/usr/bin/python
- # Code goes here ...
-
-Will result in:
-
- #!/usr/bin/python
- # Code goes here ...
-
-### Shebang (no path) ###
-
-If the first line contains a shebang, but the shebang line does not contain a
-path (a single `/` or even a space), then that line is removed from the code
-block before processing. Line numbers are used.
-
- #!python
- # Code goes here ...
-
-Will result in:
-
- # Code goes here ...
-
-### Colons ###
-
-If the first line begins with three or more colons, the text following the
-colons identifies the language. The first line is removed from the code block
-before processing and line numbers are not used.
-
- :::python
- # Code goes here ...
-
-Will result in:
-
- # Code goes here ...
-
-Certain lines can be selected for emphasis with the colon syntax. When
-using Pygments' default CSS styles, emphasized lines have a yellow background.
-This is useful to direct the reader's attention to specific lines.
-
- :::python hl_lines="1 3"
- # This line is emphasized
- # This line isn't
- # This line is emphasized
-
-!!! Note
- `hl_lines` is named for Pygments' option meaning "highlighted lines".
-
-### When No Language is Defined ###
-
-CodeHilite is completely backwards compatible so that if a code block is
-encountered that does not define a language, the block is simply wrapped in
-`<pre>` tags and output.
-
- # Code goes here ...
-
-Will result in:
-
- # Code goes here ...
-
-Lets see the source for that:
-
- <div class="codehilite"><pre><code># Code goes here ...
- </code></pre></div>
-
-!!! Note
- When no language is defined, the Pygments highlighting engine will try to guess
- the language (unless `guess_lang` is set to `False`). Upon failure, the same
- behavior will happen as described above.
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.codehilite`
-as the name of the extension.
-
-See the [Library Reference](../reference.html#extensions) for information about
-configuring extensions.
-
-The following options are provided to configure the output:
-
-* **`linenums`**:
- Use line numbers. Possible values are `True` for yes, `False` for no and
- `None` for auto. Defaults to `None`.
-
- Using `True` will force every code block to have line numbers, even when
- using colons (`:::`) for language identification.
-
- Using `False` will turn off all line numbers, even when using shebangs
- (`#!`) for language identification.
-
-* **`guess_lang`**:
- Automatic language detection. Defaults to `True`.
-
- Using `False` will prevent Pygments from guessing the language, and thus
- highlighting blocks only when you explicitly set the language.
-
-* **`css_class`**:
- Set CSS class name for the wrapper `<div>` tag. Defaults to
- `codehilite`.
-
-* **`pygments_style`**:
- Pygments HTML Formatter Style (`ColorScheme`). Defaults to `default`.
-
- !!! Note
- This is useful only when `noclasses` is set to `True`, otherwise the
- CSS styles must be provided by the end user.
-
-* **`noclasses`**:
- Use inline styles instead of CSS classes. Defaults to `False`.
-
-* **`use_pygments`**:
- Defaults to `True`. Set to `False` to disable the use of Pygments.
- If a language is defined for a code block, it will be assigned to the
- `<code>` tag as a class in the manner suggested by the [HTML5 spec][spec]
- (alternate output will not be entertained) and might be used by a JavaScript
- library in the browser to highlight the code block.
-
-[spec]: http://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
--- /dev/null
+title: Definition Lists Extension
+
+Definition Lists
+================
+
+Summary
+-------
+
+The Definition Lists extension adds the ability to create definition lists in
+Markdown documents.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Definition lists are defined using the syntax established in
+[PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#def-list
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+```md
+Apple
+: Pomaceous fruit of plants of the genus Malus in
+ the family Rosaceae.
+
+Orange
+: The fruit of an evergreen tree of the genus Citrus.
+```
+
+will be rendered as:
+
+```html
+<dl>
+<dt>Apple</dt>
+<dd>Pomaceous fruit of plants of the genus Malus in
+the family Rosaceae.</dd>
+
+<dt>Orange</dt>
+<dd>The fruit of an evergreen tree of the genus Citrus.</dd>
+</dl>
+```
+
+Usage
+-----
+
+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.
+++ /dev/null
-title: Definition Lists Extension
-prev_title: Attribute Lists Extension
-prev_url: attr_list.html
-next_title: Fenced Code Blocks Extension
-next_url: fenced_code_blocks.html
-
-Definition Lists
-================
-
-Summary
--------
-
-The Definition Lists extension adds the ability to create definition lists in
-Markdown documents.
-
-This extension is included in the standard Markdown library.
-
-Syntax
-------
-
-Definition lists are defined using the syntax established in
-[PHP Markdown Extra][php].
-
-[php]: http://www.michelf.com/projects/php-markdown/extra/#def-list
-
-Thus, the following text (taken from the above referenced PHP documentation):
-
- Apple
- : Pomaceous fruit of plants of the genus Malus in
- the family Rosaceae.
-
- Orange
- : The fruit of an evergreen tree of the genus Citrus.
-
-will be rendered as:
-
- <dl>
- <dt>Apple</dt>
- <dd>Pomaceous fruit of plants of the genus Malus in
- the family Rosaceae.</dd>
-
- <dt>Orange</dt>
- <dd>The fruit of an evergreen tree of the genus Citrus.</dd>
- </dl>
-
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.def_list`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
--- /dev/null
+title: Extra Extension
+
+# Python-Markdown Extra
+
+## Summary
+
+A compilation of various Python-Markdown extensions that (mostly) imitates
+[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/).
+
+The supported extensions include:
+
+* [Abbreviations](abbreviations.md)
+* [Attribute Lists](attr_list.md)
+* [Definition Lists](definition_lists.md)
+* [Fenced Code Blocks](fenced_code_blocks.md)
+* [Footnotes](footnotes.md)
+* [Tables](tables.md)
+
+See each individual extension for syntax documentation. Extra and all its
+supported extensions are included in the standard Markdown library.
+
+## Usage
+
+From the Python interpreter:
+
+```pycon
+>>> import markdown
+>>> html = markdown.markdown(text, ['extra'])
+```
+
+There may be [additional extensions](index.md) that are distributed with
+Python-Markdown that are not included here in Extra. The features
+of those extensions are not part of PHP Markdown Extra, and
+therefore, not part of Python-Markdown Extra. If you really would
+like Extra to include additional extensions, we suggest creating
+your own clone of Extra under a different name
+(see the [Extension API](api.md)).
+
+### Markdown Inside HTML Blocks
+
+Unlike the other Extra features, this feature is built into the markdown core and
+is turned on when `markdown.extensions.extra` is enabled.
+
+The content of any raw HTML block element can be Markdown-formatted simply by
+adding a `markdown` attribute to the opening tag. The markdown attribute will be
+stripped from the output, but all other attributes will be preserved.
+
+If the markdown value is set to `1` (recommended) or any value other than `span`
+or `block`, the default behavior will be executed: `p`,`h[1-6]`,`li`,`dd`,`dt`,
+`td`,`th`,`legend`, and `address` elements skip block parsing while others do not.
+If the default is overridden by a value of `span`, *block parsing will be skipped*
+regardless of tag. If the default is overridden by a value of `block`,
+*block parsing will occur* regardless of tag.
+
+#### Simple Example:
+
+```md
+This is *true* markdown text.
+
+<div markdown="1">
+This is *true* markdown text.
+</div>
+```
+
+#### Result:
+
+```html
+<p>This is <em>true</em> markdown text.</p>
+<div>
+<p>This is <em>true</em> markdown text.</p>
+</div>
+```
+
+### Nested Markdown Inside HTML Blocks
+
+Nested elements are more sensitive and must be used cautiously. To avoid
+unexpected results:
+
+* Only nest elements within block mode elements.
+* Follow the closing tag of inner elements with a blank line.
+* Only have one level of nesting.
+
+#### Complex Example:
+
+```md
+<div markdown="1" name="Example">
+
+The text of the `Example` element.
+
+<div markdown="1" name="DefaultBlockMode">
+This text gets wrapped in `p` tags.
+</div>
+
+The tail of the `DefaultBlockMode` subelement.
+
+<p markdown="1" name="DefaultSpanMode">
+This text *is not* wrapped in additional `p` tags.
+</p>
+
+The tail of the `DefaultSpanMode` subelement.
+
+<div markdown="span" name="SpanModeOverride">
+This `div` block is not wrapped in paragraph tags.
+Note: Subelements are not required to have tail text.
+</div>
+
+<p markdown="block" name="BlockModeOverride">
+This `p` block *is* foolishly wrapped in further paragraph tags.
+</p>
+
+The tail of the `BlockModeOverride` subelement.
+
+<div name="RawHtml">
+Raw HTML blocks may also be nested.
+</div>
+
+</div>
+
+This text is after the markdown in HTML.
+```
+
+#### Complex Result:
+
+```html
+<div name="Example">
+<p>The text of the <code>Example</code> element.</p>
+<div name="DefaultBlockMode">
+<p>This text gets wrapped in <code>p</code> tags.</p>
+</div>
+<p>The tail of the <code>DefaultBlockMode</code> subelement.</p>
+<p name="DefaultSpanMode">
+This text <em>is not</em> wrapped in additional <code>p</code> tags.</p>
+<p>The tail of the <code>DefaultSpanMode</code> subelement.</p>
+<div name="SpanModeOverride">
+This <code>div</code> block is not wrapped in paragraph tags.
+Note: Subelements are not required to have tail text.</div>
+<p name="BlockModeOverride">
+<p>This <code>p</code> block <em>is</em> foolishly wrapped in further paragraph tags.</p>
+</p>
+<p>The tail of the <code>BlockModeOverride</code> subelement.</p>
+<div name="RawHtml">
+Raw HTML blocks may also be nested.
+</div>
+
+</div>
+<p>This text is after the markdown in HTML.</p>
+```
+++ /dev/null
-title: Extra Extension
-prev_title: Extensions
-prev_url: index.html
-next_title: Abbreviations Extension
-next_url: abbreviations.html
-
-Python-Markdown Extra
-=====================
-
-Summary
--------
-
-A compilation of various Python-Markdown extensions that (mostly) imitates
-[PHP Markdown Extra](http://michelf.com/projects/php-markdown/extra/).
-
-The supported extensions include:
-
-* [Abbreviations](abbreviations.html)
-* [Attribute Lists](attr_list.html)
-* [Definition Lists](definition_lists.html)
-* [Fenced Code Blocks](fenced_code_blocks.html)
-* [Footnotes](footnotes.html)
-* [Tables](tables.html)
-* [Smart Strong](smart_strong.html)
-
-See each individual extension for syntax documentation. Extra and all its
-supported extensions are included in the standard Markdown library.
-
-Usage
------
-
-From the Python interpreter:
-
- >>> import markdown
- >>> html = markdown.markdown(text, ['markdown.extensions.extra'])
-
-There may be [additional extensions](index.html) that are distributed with
-Python-Markdown that are not included here in Extra. The features
-of those extensions are not part of PHP Markdown Extra, and
-therefore, not part of Python-Markdown Extra. If you really would
-like Extra to include additional extensions, we suggest creating
-your own clone of Extra under a different name
-(see the [Extension API](api.html)).
-
-Markdown Inside HTML Blocks
----------------------------
-
-Unlike the other Extra features, this feature is built into the markdown core and
-is turned on when `markdown.extensions.extra` is enabled.
-
-The content of any raw HTML block element can be Markdown-formatted simply by
-adding a `markdown` attribute to the opening tag. The markdown attribute will be
-stripped from the output, but all other attributes will be preserved.
-
-If the markdown value is set to `1` (recommended) or any value other than `span`
-or `block`, the default behavior will be executed: `p`,`h[1-6]`,`li`,`dd`,`dt`,
-`td`,`th`,`legend`, and `address` elements skip block parsing while others do not.
-If the default is overridden by a value of `span`, *block parsing will be skipped*
-regardless of tag. If the default is overridden by a value of `block`,
-*block parsing will occur* regardless of tag.
-
-#### Simple Example:
-```
-This is *true* markdown text.
-
-<div markdown="1">
-This is *true* markdown text.
-</div>
-```
-#### Result:
-```
-<p>This is <em>true</em> markdown text.</p>
-<div>
-<p>This is <em>true</em> markdown text.</p>
-</div>
-```
-
-### Nested Markdown Inside HTML Blocks
-Nested elements are more sensitive and must be used cautiously. To avoid
-unexpected results:
-
-* Only nest elements within block mode elements.
-* Follow the closing tag of inner elements with a blank line.
-* Only have one level of nesting.
-
-#### Complex Example:
-```
-<div markdown="1" name="Example">
-
-The text of the `Example` element.
-
-<div markdown="1" name="DefaultBlockMode">
-This text gets wrapped in `p` tags.
-</div>
-
-The tail of the `DefaultBlockMode` subelement.
-
-<p markdown="1" name="DefaultSpanMode">
-This text *is not* wrapped in additional `p` tags.
-</p>
-
-The tail of the `DefaultSpanMode` subelement.
-
-<div markdown="span" name="SpanModeOverride">
-This `div` block is not wrapped in paragraph tags.
-Note: Subelements are not required to have tail text.
-</div>
-
-<p markdown="block" name="BlockModeOverride">
-This `p` block *is* foolishly wrapped in further paragraph tags.
-</p>
-
-The tail of the `BlockModeOverride` subelement.
-
-<div name="RawHtml">
-Raw HTML blocks may also be nested.
-</div>
-
-</div>
-
-This text is after the markdown in HTML.
-```
-#### Result:
-```
-<div name="Example">
-<p>The text of the <code>Example</code> element.</p>
-<div name="DefaultBlockMode">
-<p>This text gets wrapped in <code>p</code> tags.</p>
-</div>
-<p>The tail of the <code>DefaultBlockMode</code> subelement.</p>
-<p name="DefaultSpanMode">
-This text <em>is not</em> wrapped in additional <code>p</code> tags.</p>
-<p>The tail of the <code>DefaultSpanMode</code> subelement.</p>
-<div name="SpanModeOverride">
-This <code>div</code> block is not wrapped in paragraph tags.
-Note: Subelements are not required to have tail text.</div>
-<p name="BlockModeOverride">
-<p>This <code>p</code> block <em>is</em> foolishly wrapped in further paragraph tags.</p>
-</p>
-<p>The tail of the <code>BlockModeOverride</code> subelement.</p>
-<div name="RawHtml">
-Raw HTML blocks may also be nested.
-</div>
-
-</div>
-<p>This text is after the markdown in HTML.</p>
-```
--- /dev/null
+title: Fenced Code Blocks Extension
+
+# Fenced Code Blocks
+
+## Summary
+
+The Fenced Code Blocks extension adds a secondary way to define code blocks,
+which overcomes a few limitations of the indented code blocks.
+
+This extension is included in the standard Markdown library.
+
+## Syntax
+
+Fenced Code Blocks are defined using the syntax established in
+[PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#fenced-code-blocks
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+```md
+This is a paragraph introducing:
+
+~~~~~~~~~~~~~~~~~~~~~
+a one-line code block
+~~~~~~~~~~~~~~~~~~~~~
+```
+
+Fenced code blocks can have a blank line as the first and/or last line of a
+code block and they can also come immediately after a list item without becoming
+part of the list.
+
+!!! warning
+
+ Fenced Code Blocks are only supported at the document root level.
+ Therefore, they cannot be nested inside lists or blockquotes.
+
+### Language
+
+In addition to PHP Extra's syntax, you can define the language of the code
+block for use by syntax highlighters etc. The language will be assigned as a
+class attribute of the ``<code>`` element in the output. Therefore, you should
+define the language as you would a CSS class - ``.language``. For consistency
+with other markdown syntax, the language can *optionally* be wrapped in curly
+brackets:
+
+```md
+~~~~{.python}
+# python code
+~~~~
+
+~~~~.html
+<p>HTML Document</p>
+~~~~
+```
+
+The above will output:
+
+```html
+<pre><code class="python"># python code
+</code></pre>
+
+<pre><code class="html"><p>HTML Document</p>
+</code></pre>
+```
+
+[GitHub][]'s backtick (`\``) syntax is also supported:
+
+````md
+```python
+# more python code
+```
+````
+
+[GitHub]: http://github.github.com/github-flavored-markdown/
+
+### Emphasized Lines
+
+If you would like to have your fenced code blocks highlighted with the
+[CodeHilite][] extension, simply enable that extension (remember that
+[Pygments][] is its dependency) and the language of your fenced code blocks
+will be passed in and highlighted appropriately.
+
+Similar to the [colon][] syntax of the CodeHilite extension, fenced code blocks
+can also have emphasized certain lines of code.
+
+The lines can be specified with PHP Extra's syntax:
+
+```md
+~~~~{.python hl_lines="1 3"}
+# This line is emphasized
+# This line isn't
+# This line is emphasized
+~~~~
+```
+
+... or with GitHub's:
+
+````md
+```python hl_lines="1 3"
+# This line is emphasized
+# This line isn't
+# This line is emphasized
+```
+````
+
+[CodeHilite]: code_hilite.md
+[Pygments]: http://pygments.org/
+[colon]: code_hilite.md#colons
+
+## Usage
+
+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.
+++ /dev/null
-title: Fenced Code Blocks Extension
-prev_title: Definition Lists Extension
-prev_url: definition_lists.html
-next_title: Footnotes Extension
-next_url: footnotes.html
-
-Fenced Code Blocks
-==================
-
-Summary
--------
-
-The Fenced Code Blocks extension adds a secondary way to define code blocks,
-which overcomes a few limitations of the indented code blocks.
-
-This extension is included in the standard Markdown library.
-
-Syntax
-------
-
-Fenced Code Blocks are defined using the syntax established in
-[PHP Markdown Extra][php].
-
-[php]: http://www.michelf.com/projects/php-markdown/extra/#fenced-code-blocks
-
-Thus, the following text (taken from the above referenced PHP documentation):
-
- This is a paragraph introducing:
-
- ~~~~~~~~~~~~~~~~~~~~
- a one-line code block
- ~~~~~~~~~~~~~~~~~~~~
-
-Fenced code blocks can have a blank line as the first and/or last line of a
-code block and they can also come immediately after a list item without becoming
-part of the list.
-
-!!! warning
-
- Fenced Code Blocks are only supported at the document root level.
- Therefore, they cannot be nested inside lists or blockquotes.
-
-### Language ###
-
-In addition to PHP Extra's syntax, you can define the language of the code
-block for use by syntax highlighters etc. The language will be assigned as a
-class attribute of the ``<code>`` element in the output. Therefore, you should
-define the language as you would a CSS class - ``.language``. For consistency
-with other markdown syntax, the language can *optionally* be wrapped in curly
-brackets:
-
- ~~~~{.python}
- # python code
- ~~~~
-
- ~~~~.html
- <p>HTML Document</p>
- ~~~~
-
-The above will output:
-
- <pre><code class="python"># python code
- </code></pre>
-
- <pre><code class="html"><p>HTML Document</p>
- </code></pre>
-
-[GitHub][]'s backtick (`\``) syntax is also supported:
-
- ```python
- # more python code
- ```
-
-[GitHub]: http://github.github.com/github-flavored-markdown/
-
-### Emphasized Lines ###
-
-If you would like to have your fenced code blocks highlighted with the
-[CodeHilite][] extension, simply enable that extension (remember that
-[Pygments][] is its dependency) and the language of your fenced code blocks
-will be passed in and highlighted appropriately.
-
-Similar to the [colon][] syntax of the CodeHilite extension, fenced code blocks
-can also have emphasized certain lines of code.
-
-The lines can be specified with PHP Extra's syntax:
-
- ~~~~{.python hl_lines="1 3"}
- # This line is emphasized
- # This line isn't
- # This line is emphasized
- ~~~~
-
-... or with GitHub's:
-
- ```python hl_lines="1 3"
- # This line is emphasized
- # This line isn't
- # This line is emphasized
- ```
-
-[CodeHilite]: code_hilite.html
-[Pygments]: http://pygments.org/
-[colon]: code_hilite.html#colons
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.fenced_code`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
--- /dev/null
+title: Footnotes Extension
+
+Footnotes
+=========
+
+Summary
+-------
+
+The Footnotes extension adds syntax for defining footnotes in Markdown
+documents.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Python-Markdown's Footnote syntax follows the generally accepted syntax of the
+Markdown community at large and almost exactly matches [PHP Markdown Extra][]'s
+implementation of footnotes. The only differences involve a few subtleties in
+the output.
+
+[PHP Markdown Extra]: http://michelf.com/projects/php-markdown/extra/#footnotes
+
+Example:
+
+```md
+Footnotes[^1] have a label[^@#$%] and the footnote's content.
+
+[^1]: This is a footnote content.
+[^@#$%]: A footnote on the label: "@#$%".
+```
+
+A footnote label must start with a caret `^` and may contain any inline text
+(including spaces) between a set of square brackets `[]`. Only the first
+caret has any special meaning.
+
+A footnote content must start with the label followed by a colon and at least
+one space. The label used to define the content must exactly match the label used
+in the body (including capitalization and white space). The content would then
+follow the label either on the same line or on the next line. The content may
+contain multiple lines, paragraphs, code blocks, blockquotes and most any other
+markdown syntax. The additional lines must be indented one level (four spaces or
+one tab).
+
+When working with multiple blocks, it may be helpful to start the content on a
+separate line from the label which defines the content. This way the entire block
+is indented consistently and any errors are more easily discernible by the author.
+
+```md
+[^1]:
+ The first paragraph of the definition.
+
+ Paragraph two of the definition.
+
+ > A blockquote with
+ > multiple lines.
+
+ a code block
+
+ A final paragraph.
+```
+
+Usage
+-----
+
+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.
+
+The following options are provided to configure the output:
+
+* **`PLACE_MARKER`**:
+ A text string used to mark the position where the footnotes are rendered.
+ Defaults to `///Footnotes Go Here///`.
+
+ If the place marker text is not found in the document, the footnote
+ definitions are placed at the end of the resulting HTML document.
+
+* **`UNIQUE_IDS`**:
+ Whether to avoid collisions across multiple calls to `reset()`. Defaults to
+ `False`.
+
+* **`BACKLINK_TEXT`**:
+ The text string that links from the footnote definition back to the position
+ in the document. Defaults to `↩`.
+
+* **`BACKLINK_TITLE`**:
+ 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`
+++ /dev/null
-title: Footnotes Extension
-prev_title: Fenced Code Blocks Extension
-prev_url: fenced_code_blocks.html
-next_title: Tables Extension
-next_url: tables.html
-
-Footnotes
-=========
-
-Summary
--------
-
-The Footnotes extension adds syntax for defining footnotes in Markdown
-documents.
-
-This extension is included in the standard Markdown library.
-
-Syntax
-------
-
-Python-Markdown's Footnote syntax follows the generally accepted syntax of the
-Markdown community at large and almost exactly matches [PHP Markdown Extra][]'s
-implementation of footnotes. The only differences involve a few subtleties in
-the output.
-
-[PHP Markdown Extra]: http://michelf.com/projects/php-markdown/extra/#footnotes
-
-Example:
-
- Footnotes[^1] have a label[^@#$%] and the footnote's content.
-
- [^1]: This is a footnote content.
- [^@#$%]: A footnote on the label: "@#$%".
-
-A footnote label must start with a caret `^` and may contain any inline text
-(including spaces) between a set of square brackets `[]`. Only the first
-caret has any special meaning.
-
-A footnote content must start with the label followed by a colon and at least
-one space. The label used to define the content must exactly match the label used
-in the body (including capitalization and white space). The content would then
-follow the label either on the same line or on the next line. The content may
-contain multiple lines, paragraphs, code blocks, blockquotes and most any other
-markdown syntax. The additional lines must be indented one level (four spaces or
-one tab).
-
-When working with multiple blocks, it may be helpful to start the content on a
-separate line from the label which defines the content. This way the entire block
-is indented consistently and any errors are more easily discernible by the author.
-
- [^1]:
- The first paragraph of the definition.
-
- Paragraph two of the definition.
-
- > A blockquote with
- > multiple lines.
-
- a code block
-
- A final paragraph.
-
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.footnotes`
-as the name of the extension.
-
-See the [Library Reference](../reference.html#extensions) for information about
-configuring extensions.
-
-The following options are provided to configure the output:
-
-* **`PLACE_MARKER`**:
- A text string used to mark the position where the footnotes are rendered.
- Defaults to `///Footnotes Go Here///`.
-
- If the place marker text is not found in the document, the footnote
- definitions are placed at the end of the resulting HTML document.
-
-* **`UNIQUE_IDS`**:
- Whether to avoid collisions across multiple calls to `reset()`. Defaults to
- `False`.
-
-* **`BACKLINK_TEXT`**:
- The text string that links from the footnote definition back to the position
- in the document. Defaults to `↩`.
+++ /dev/null
-title: HeaderId Extension
-prev_title: CodeHilite Extension
-prev_url: code_hilite.html
-next_title: Meta-Data Extension
-next_url: meta_data.html
-
-HeaderId
-========
-
-Summary
--------
-
-The HeaderId extension automatically generates `id` attributes for the header
-elements (`h1`-`h6`) in the resulting HTML document.
-
-This extension is included in the standard Markdown library.
-
-!!! warning
- This extension is **Pending Deprecation**. The [Table of Contents][toc]
- Extension should be used instead, which offers most the features of this
- extension and more.
-
-[toc]: toc.html
-
-Syntax
-------
-
-By default, all headers will automatically have unique `id` attributes
-generated based upon the text of the header (see below to turn this off).
-Note this example, in which all three headers would have the same `id`:
-
- #Header
- #Header
- #Header
-
-Results in:
-
- <h1 id="header">Header</h1>
- <h1 id="header_1">Header</h1>
- <h1 id="header_2">Header</h1>
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.headerid`
-as the name of the extension.
-
-See the [Library Reference](../reference.html#extensions) for information about
-configuring extensions.
-
-The following options are provided to configure the output:
-
-* **`level`**: Base level for headers.
-
- Default: `1`
-
- The `level` setting allows you to automatically adjust the header levels to
- fit within the hierarchy of your HTML templates. For example, suppose the
- markdown text for a page should not contain any headers higher than level 3
- (`<h3>`). The following will accomplish that:
-
- >>> text = '''
- ... #Some Header
- ... ## Next Level'''
- >>> from markdown.extensions.headerid import HeaderIdExtension
- >>> html = markdown.markdown(text, extensions=[HeaderIdExtension(level=3)])
- >>> print html
- <h3 id="some_header">Some Header</h3>
- <h4 id="next_level">Next Level</h4>'
-
-* **`forceid`**: Force all headers to have an id.
-
- Default: `True`
-
- The `forceid` setting turns on or off the automatically generated ids for
- headers that do not have one explicitly defined (using the
- [Attribute List](attr_list.html) extension).
-
- >>> text = '''
- ... # Some Header
- ... # Header with ID # { #foo }'''
- >>> html = markdown.markdown(text,
- extensions=['markdown.extensions.attr_list',
- HeaderIdExtension(forceid=False)])
- >>> print html
- <h1>Some Header</h1>
- <h1 id="foo">Header with ID</h1>
-
-* **`separator`**: Word separator. Character which replaces white space in id.
-
- Default: `-`
-
-* **`slugify`**: Callable to generate anchors.
-
- Default: `markdown.extensions.headerid.slugify`
-
- If you would like to use a different algorithm to define the ids, you can
- pass in a callable which takes two arguments:
-
- * `value`: The string to slugify.
- * `separator`: The Word Separator.
-
-Using with Meta-Data
---------------------
-
-The HeaderId extension also supports the [Meta-Data](meta_data.html) extension.
-Please see the documentation for that extension for specifics. The supported
-meta-data keywords are:
-
-* `header_level`
-* `header_forceid`
-
-When used, the meta-data will override the settings provided through the
-`extension_configs` interface.
-
-This document:
-
- header_level: 2
- header_forceid: Off
-
- # A Header
-
-
-Will result in the following output:
-
- <h2>A Header</h2>
--- /dev/null
+title: 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
+actual source files.
+
+To use an extension, pass it to markdown with the `extensions` keyword.
+
+```python
+markdown.markdown(some_text, extensions=[MyExtClass(), 'myext', 'path.to.my.ext:MyExtClass'])
+```
+
+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 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.
+
+!!! seealso "See Also"
+ If you would like to write your own extensions, see the
+ [Extension API](api.md) for details.
+
+Officially Supported Extensions
+-------------------------------
+
+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 "Entry Point" name listed in the second column below.
+
+Extension | Entry Point | Dot Notation
+------------------------------------ | -------------- | ------------
+[Extra] | `extra` | `markdown.extensions.extra`
+ [Abbreviations][] | `abbr` | `markdown.extensions.abbr`
+ [Attribute Lists][] | `attr_list` | `markdown.extensions.attr_list`
+ [Definition Lists][] | `def_list` | `markdown.extensions.def_list`
+ [Fenced Code Blocks][] | `fenced_code` | `markdown.extensions.fenced_code`
+ [Footnotes][] | `footnotes` | `markdown.extensions.footnotes`
+ [Tables][] | `tables` | `markdown.extensions.tables`
+[Admonition][] | `admonition` | `markdown.extensions.admonition`
+[CodeHilite][] | `codehilite` | `markdown.extensions.codehilite`
+[Legacy Attributes][] | `legacy_attr` | `markdown.extensions.legacy_attr`
+[Legacy Emphasis][] | `legacy_em` | `markdown.extensions.legacy_em`
+[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
+[Attribute Lists]: attr_list.md
+[Definition Lists]: definition_lists.md
+[Fenced Code Blocks]: fenced_code_blocks.md
+[Footnotes]: footnotes.md
+[Tables]: tables.md
+[Admonition]: admonition.md
+[CodeHilite]: code_hilite.md
+[Legacy Attributes]: legacy_attr.md
+[Legacy Emphasis]: legacy_em.md
+[Meta-Data]: meta_data.md
+[New Line to Break]: nl2br.md
+[Sane Lists]: sane_lists.md
+[SmartyPants]: smarty.md
+[Table of Contents]: toc.md
+[WikiLinks]: wikilinks.md
+
+Third Party Extensions
+----------------------
+
+Various individuals and/or organizations have developed extensions which they
+have made available to the public. A [list of third party extensions][list]
+is maintained on the wiki for your convenience. The Python-Markdown team
+offers no official support for these extensions. Please see the developer of
+each extension for support.
+
+[list]: https://github.com/Python-Markdown/markdown/wiki/Third-Party-Extensions
+++ /dev/null
-title: Extensions
-prev_title: Command Line
-prev_url: ../cli.html
-next_title: Extra Extension
-next_url: extra.html
-
-
-Available 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
-actual source files.
-
-To use an extension, pass it to markdown with the `extensions` keyword.
-
- markdown.markdown(some_text, extensions=[MyExtension(), 'path.to.my.ext', 'markdown.extensions.footnotes'])
-
-See the [Library Reference](../reference.html#extensions) for more details.
-
-From the command line, specify an extension with the `-x` option.
-
- $ python -m markdown -x markdown.extensions.footnotes -x markdown.extensions.tables input.txt > output.html
-
-See the [Command Line docs](../cli.html) or use the `--help` option for more details.
-
-!!! seealso "See Also"
- If you would like to write your own extensions, see the
- [Extension API](api.html) for details.
-
-Officially Supported Extensions
--------------------------------
-
-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`
- [Abbreviations][] | `markdown.extensions.abbr`
- [Attribute Lists][] | `markdown.extensions.attr_list`
- [Definition Lists][] | `markdown.extensions.def_list`
- [Fenced Code Blocks][] | `markdown.extensions.fenced_code`
- [Footnotes][] | `markdown.extensions.footnotes`
- [Tables][] | `markdown.extensions.tables`
- [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`
-
-[Extra]: extra.html
-[Abbreviations]: abbreviations.html
-[Attribute Lists]: attr_list.html
-[Definition Lists]: definition_lists.html
-[Fenced Code Blocks]: fenced_code_blocks.html
-[Footnotes]: footnotes.html
-[Tables]: tables.html
-[Smart Strong]: smart_strong.html
-[Admonition]: admonition.html
-[CodeHilite]: code_hilite.html
-[HeaderId]: header_id.html
-[Meta-Data]: meta_data.html
-[New Line to Break]: nl2br.html
-[Sane Lists]: sane_lists.html
-[SmartyPants]: smarty.html
-[Table of Contents]: toc.html
-[WikiLinks]: wikilinks.html
-
-Third Party Extensions
-----------------------
-
-Various individuals and/or organizations have developed extensions which they
-have made available to the public. A [list of third party extensions][list]
-is maintained on the wiki for your convenience. The Python-Markdown team
-offers no official support for these extensions. Please see the developer of
-each extension for support.
-
-[list]: https://github.com/waylan/Python-Markdown/wiki/Third-Party-Extensions
--- /dev/null
+title: Legacy Attributes Extension
+
+# Legacy Attributes
+
+## Summary
+
+The Legacy Attributes extension restores Python-Markdown's original attribute
+setting syntax. Older versions of Python Markdown (prior to 3.0) included
+built-in and undocumented support for defining attributes on elements. Most
+users have never made use of the syntax and it has been deprecated in favor of
+[Attribute Lists](attr_list.md). This extension restores the legacy behavior for
+users who have existing documents which use the syntax.
+
+## Syntax
+
+Attributes are defined by including the following within the element you wish to
+assign the attributes to:
+
+```md
+{@key=value}
+```
+
+For example, to define a class to a paragraph:
+
+```md
+A paragraph with the attribute defined {@class=foo}anywhere within.
+```
+
+Which results in the following output:
+
+```html
+<p class="foo">A paragraph with the attribute defined anywhere within.</p>
+```
+
+The same applies for inline elements:
+
+```md
+Some *emphasized{@id=bar}* text.
+```
+
+```html
+<p>Some <em id="bar">emphasized</em> text.</p>
+
+You can also define attributes in images:
+
+```md
+
+```
+
+```html
+<p><img alt="Alt text" id="baz" src="path/to/image.jpg" /></p>
+```
+
+## Usage
+
+See [Extensions](index.md) for general extension usage. Use `legacy_attr` as the
+name of the extension.
+
+This extension does not accept any special configuration options.
--- /dev/null
+title: Legacy EM Extension
+
+# Legacy EM
+
+## Summary
+
+The Legacy EM extension restores Markdown's original behavior for emphasis and
+strong syntax when using underscores.
+
+By default Python-Markdown treats `_connected_words_` intelligently by
+recognizing that mid-word underscores should not be used for emphasis. In other
+words, by default, that input would result in this output:
+`<em>connected_words</em>`.
+
+However, that behavior is not consistent with the original rules or the behavior
+of the reference implementation. Therefore, this extension can be used to better
+match the reference implementation. With the extension enabled, the above input
+would result in this output: `<em>connected</em>words_`.
+
+## Usage
+
+See [Extensions](index.md) for general extension usage. Use `legacy_em` as the
+name of the extension.
+
+This extension does not accept any special configuration options.
--- /dev/null
+title: Meta-Data Extension
+
+Meta-Data
+=========
+
+Summary
+-------
+
+The Meta-Data extension adds a syntax for defining meta-data about a document.
+It is inspired by and follows the syntax of [MultiMarkdown][]. Currently,
+this extension does not use the meta-data in any way, but simply provides it as
+a `Meta` attribute of a Markdown instance for use by other extensions or
+directly by your python code.
+
+This extension is included in the standard Markdown library.
+
+[MultiMarkdown]: http://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata
+
+Syntax
+------
+
+Meta-data consists of a series of keywords and values defined at the beginning
+of a markdown document like this:
+
+```md
+Title: My Document
+Summary: A brief description of my document.
+Authors: Waylan Limberg
+ John Doe
+Date: October 2, 2007
+blank-value:
+base_url: http://example.com
+
+This is the first paragraph of the document.
+```
+
+The keywords are case-insensitive and may consist of letters, numbers,
+underscores and dashes and must end with a colon. The values consist of
+anything following the colon on the line and may even be blank.
+
+If a line is indented by 4 or more spaces, that line is assumed to be an
+additional line of the value for the previous keyword. A keyword may have as
+many lines as desired.
+
+The first blank line ends all meta-data for the document. Therefore, the first
+line of a document must not be blank.
+
+Alternatively, You may use YAML style deliminators to mark the start and/or end
+of your meta-data. When doing so, the first line of your document must be `---`.
+The meta-data ends at the first blank line or the first line containing an end
+deliminator (either `---` or `...`), whichever comes first. Even though YAML
+deliminators are supported, meta-data is not parsed as YAML.
+
+All meta-data is stripped from the document prior to any further processing
+by Markdown.
+
+Usage
+-----
+
+See [Extensions](index.md) for general extension usage. Use `meta` as the name
+of the extension.
+
+Accessing the Meta-Data
+-----------------------
+
+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 = ['meta'])
+>>> html = md.convert(text)
+>>> # Meta-data has been stripped from output
+>>> print html
+<p>This is the first paragraph of the document.</p>
+
+>>> # View meta-data
+>>> print md.Meta
+{
+'title' : ['My Document'],
+'summary' : ['A brief description of my document.'],
+'authors' : ['Waylan Limberg', 'John Doe'],
+'date' : ['October 2, 2007'],
+'blank-value' : [''],
+'base_url' : ['http://example.com']
+}
+```
+
+Note that the keys are all lowercase and the values consist of a list of
+strings where each item is one line for that key. This way, one could preserve
+line breaks if desired. Or the items could be joined where appropriate. No
+assumptions are made regarding the data. It is simply passed as found to the
+`Meta` attribute.
+
+Perhaps the meta-data could be passed into a template system, or used by
+various Markdown extensions. The possibilities are left to the imagination of
+the developer.
+
+Compatible Extensions
+---------------------
+
+The following extensions are currently known to work with the Meta-Data
+extension. The keywords they are known to support are also listed.
+
+* [WikiLinks](wikilinks.md)
+ * `wiki_base_url`
+ * `wiki_end_url`
+ * `wiki_html_class`
+++ /dev/null
-title: Meta-Data Extension
-prev_title: HeaderId Extension
-prev_url: header_id.html
-next_title: New Line to Break Extension
-next_url: nl2br.html
-
-Meta-Data
-=========
-
-Summary
--------
-
-The Meta-Data extension adds a syntax for defining meta-data about a document.
-It is inspired by and follows the syntax of [MultiMarkdown][]. Currently,
-this extension does not use the meta-data in any way, but simply provides it as
-a `Meta` attribute of a Markdown instance for use by other extensions or
-directly by your python code.
-
-This extension is included in the standard Markdown library.
-
-[MultiMarkdown]: http://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata
-
-Syntax
-------
-
-Meta-data consists of a series of keywords and values defined at the beginning
-of a markdown document like this:
-
- Title: My Document
- Summary: A brief description of my document.
- Authors: Waylan Limberg
- John Doe
- Date: October 2, 2007
- blank-value:
- base_url: http://example.com
-
- This is the first paragraph of the document.
-
-The keywords are case-insensitive and may consist of letters, numbers,
-underscores and dashes and must end with a colon. The values consist of
-anything following the colon on the line and may even be blank.
-
-If a line is indented by 4 or more spaces, that line is assumed to be an
-additional line of the value for the previous keyword. A keyword may have as
-many lines as desired.
-
-The first blank line ends all meta-data for the document. Therefore, the first
-line of a document must not be blank.
-
-Alternatively, if the first line in the document is `---`, a YAML document
-separator, then the meta-data is searched for between it and the next `---`
-(or `...`) line. Even though YAML deliminators are supported, meta-data is
-not parsed as YAML unless the `yaml` option is set (see below).
-
-All meta-data is stripped from the document prior to any further processing
-by Markdown.
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.meta`
-as the name of the extension.
-
-The following options are provided to configure the output:
-
-* **`yaml`**: Support meta-data specified in YAML format.
-
- Default: `False`
-
- If `yaml` is set to `True`, the lines between `---` separators are parsed
- as a full YAML object. PyYAML is required for this, and a warning is
- issued if PyYAML (or equivalent) is not available.
-
-Accessing the Meta-Data
------------------------
-
-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:
-
- >>> md = markdown.Markdown(extensions = ['markdown.extensions.meta'])
- >>> html = md.convert(text)
- >>> # Meta-data has been stripped from output
- >>> print html
- <p>This is the first paragraph of the document.</p>
-
- >>> # View meta-data
- >>> print md.Meta
- {
- 'title' : ['My Document'],
- 'summary' : ['A brief description of my document.'],
- 'authors' : ['Waylan Limberg', 'John Doe'],
- 'date' : ['October 2, 2007'],
- 'blank-value' : [''],
- 'base_url' : ['http://example.com']
- }
-
-Note that the keys are all lowercase and the values consist of a list of
-strings where each item is one line for that key. This way, one could preserve
-line breaks if desired. Or the items could be joined where appropriate. No
-assumptions are made regarding the data. It is simply passed as found to the
-`Meta` attribute.
-
-Note, if `yaml` option is set, the resulting `Meta` attribute is the object as
-returned by `yaml.load()` and may deviate significantly from the above
-description (e.g. may be a list of dictionaries, with value objects other than
-strings, ...).
-
-Perhaps the meta-data could be passed into a template system, or used by
-various Markdown extensions. The possibilities are left to the imagination of
-the developer.
-
-Compatible Extensions
----------------------
-
-The following extensions are currently known to work with the Meta-Data
-extension. The keywords they are known to support are also listed.
-
-* [HeaderId](header_id.html)
- * `header_level`
- * `header_forceid`
-* [WikiLinks](wikilinks.html)
- * `wiki_base_url`
- * `wiki_end_url`
- * `wiki_html_class`
-
--- /dev/null
+title: New Line to Break Extension
+
+New-Line-to-Break Extension
+===========================
+
+Summary
+-------
+
+The New-Line-to-Break (`nl2br`) Extension will cause newlines to be treated as
+hard breaks; like StackOverflow and [GitHub][] flavored Markdown do.
+
+[Github]: http://github.github.com/github-flavored-markdown/
+
+Example
+-------
+
+```pycon
+>>> import markdown
+>>> text = """
+... Line 1
+... Line 2
+... """
+>>> html = markdown.markdown(text, extensions=['nl2br'])
+>>> print html
+<p>Line 1<br />
+Line 2</p>
+```
+
+Usage
+-----
+
+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.
+++ /dev/null
-title: New Line to Break Extension
-prev_title: Meta-Data Extension
-prev_url: meta_data.html
-next_title: Sane Lists Extension
-next_url: sane_lists.html
-
-New-Line-to-Break Extension
-===========================
-
-Summary
--------
-
-The New-Line-to-Break (`nl2b`) Extension will cause newlines to be treated as hard breaks; like
-StackOverflow and [GitHub][] flavored Markdown do.
-
-[Github]: http://github.github.com/github-flavored-markdown/
-
-Example
--------
-
- >>> import markdown
- >>> text = """
- ... Line 1
- ... Line 2
- ... """
- >>> html = markdown.markdown(text, extensions=['markdown.extensions.nl2br'])
- >>> print html
- <p>Line 1<br />
- Line 2</p>
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.nl2br`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
--- /dev/null
+title: Sane Lists Extension
+
+Sane Lists
+==========
+
+Summary
+-------
+
+The Sane Lists extension alters the behavior of the Markdown List syntax
+to be less surprising.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Sane Lists do not allow the mixing of list types. In other words, an ordered
+list will not continue when an unordered list item is encountered and
+vice versa. For example:
+
+```md
+1. Ordered item 1
+2. Ordered item 2
+
+* Unordered item 1
+* Unordered item 2
+```
+
+will result in the following output:
+
+```html
+<ol>
+ <li>Ordered item 1</li>
+ <li>Ordered item 2</li>
+</ol>
+
+<ul>
+ <li>Unordered item 1</li>
+ <li>Unordered item 2</li>
+</ul>
+```
+
+Whereas the default Markdown behavior would be to generate an unordered list.
+
+Note that, unlike the default Markdown behavior, if a blank line is not
+included between list items, the different list type is ignored completely.
+This corresponds to the behavior of paragraphs. For example:
+
+```md
+A Paragraph.
+* Not a list item.
+
+1. Ordered list item.
+* Not a separate list item.
+```
+
+With this extension the above will result in the following output:
+
+```html
+<p>A Paragraph.
+* Not a list item.</p>
+
+<ol>
+ <li>Ordered list item.
+ * Not a separate list item.</li>
+</ol>
+```
+
+Sane lists also recognize the number used in ordered lists. Given the following
+list:
+
+```md
+4. Apples
+5. Oranges
+6. Pears
+```
+
+By default markdown will ignore the fact that the first line started
+with item number "4" and the HTML list will start with a number "1".
+This extension will result in the following HTML output:
+
+```html
+<ol start="4">
+ <li>Apples</li>
+ <li>Oranges</li>
+ <li>Pears</li>
+</ol>
+```
+
+In all other ways, Sane Lists should behave as normal Markdown lists.
+
+Usage
+-----
+
+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.
+++ /dev/null
-title: Sane Lists Extension
-prev_title: New Line to Break Extension
-prev_url: nl2br.html
-next_title: SmartyPants Extension
-next_url: smarty.html
-
-Sane Lists
-==========
-
-Summary
--------
-
-The Sane Lists extension alters the behavior of the Markdown List syntax
-to be less surprising.
-
-This extension is included in the standard Markdown library.
-
-Syntax
-------
-
-Sane Lists do not allow the mixing of list types. In other words, an ordered
-list will not continue when an unordered list item is encountered and
-vice versa. For example:
-
- 1. Ordered item 1
- 2. Ordered item 2
-
- * Unordered item 1
- * Unordered item 2
-
-will result in the following output:
-
- <ol>
- <li>Ordered item 1</li>
- <li>Ordered item 2</li>
- </ol>
-
- <ul>
- <li>Unordered item 1</li>
- <li>Unordered item 2</li>
- </ul>
-
-Whereas the default Markdown behavior would be to generate an unordered list.
-
-Note that, unlike the default Markdown behavior, if a blank line is not
-included between list items, the different list type is ignored completely.
-This corresponds to the behavior of paragraphs. For example:
-
- A Paragraph.
- * Not a list item.
-
- 1. Ordered list item.
- * Not a separate list item.
-
-With this extension the above will result in the following output:
-
- <p>A Paragraph.
- * Not a list item.</p>
-
- <ol>
- <li>Ordered list item.
- * Not a separate list item.</li>
- </ol>
-
-In all other ways, Sane Lists should behave as normal Markdown lists.
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.sane_lists`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
+++ /dev/null
-title: Smart Strong Extension
-prev_title: Tables Extension
-prev_url: tables.html
-next_title: Admonition Extension
-next_url: admonition.html
-
-Smart_Strong
-============
-
-Summary
--------
-
-The Smart_Strong extension adds smarter handling of double underscores within
-words. This does for double underscores what [smart_emphasis][] does for single
-underscores.
-
-The Smart_Strong extension is included in the standard Markdown library.
-
-[smart_emphasis]: ../reference.html#smart_emphasis
-
-Example
--------
-
- >>> import markdown
- >>> markdown.markdown('Text with double__underscore__words.', \
- extensions=['markdown.extensions.smart_strong'])
- u'<p>Text with double__underscore__words.</p>'
- >>> markdown.markdown('__Strong__ still works.', \
- extensions=['markdown.extensions.smart_strong'])
- u'<p><strong>Strong</strong> still works.</p>'
- >>> markdown.markdown('__this__works__too__.', \
- extensions=['markdown.extensions.smart_strong'])
- u'<p><strong>this__works__too</strong>.</p>'
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.smart_strong`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
--- /dev/null
+title: SmartyPants Extension
+
+SmartyPants
+===========
+
+Summary
+-------
+
+The SmartyPants extension converts ASCII dashes, quotes and ellipses to
+their HTML entity equivalents.
+
+ASCII symbol | Replacements | HTML Entities | Substitution Keys
+------------ | --------------- | ------------------- | ----------------------------------------
+`'` | ‘ ’ | `‘` `’` | `'left-single-quote'`, `'right-single-quote'`
+`"` | “ ” | `“` `”` | `'left-double-quote'`, `'right-double-quote'`
+`<< >>` | « » | `«` `»` | `'left-angle-quote'`, `'right-angle-quote'`
+`...` | … | `…` | `'ellipsis'`
+`--` | – | `–` | `'ndash'`
+`---` | — | `—` | `'mdash'`
+
+Using the configuration option 'substitutions' you can overwrite the
+default substitutions. Just pass a dict mapping (a subset of) the
+keys to the substitution strings.
+
+For example, one might use the following configuration to get correct quotes for
+the German language:
+
+```python
+extension_configs = {
+ 'smarty': {
+ 'substitutions': {
+ 'left-single-quote': '‚', # sb is not a typo!
+ 'right-single-quote': '‘',
+ 'left-double-quote': '„',
+ 'right-double-quote': '“'
+ }
+ }
+}
+```
+
+!!! note
+ This extension re-implements the Python [SmartyPants]
+ library by integrating it into the markdown parser.
+ While this does not provide any additional features,
+ it does offer a few advantages. Notably, it will not
+ try to work on highlighted code blocks (using the
+ [CodeHilite] Extension) like the third party library
+ has been known to do.
+
+[SmartyPants]: http://pythonhosted.org/smartypants/
+[CodeHilite]: code_hilite.md
+
+Usage
+-----
+
+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.
+
+The following options are provided to configure the output:
+
+Option | Default value | Description
+------ | ------------- | -----------
+`smart_dashes` | `True` | whether to convert dashes
+`smart_quotes` | `True` | whether to convert straight quotes
+`smart_angled_quotes` | `False` | whether to convert angled quotes
+`smart_ellipses` | `True` | whether to convert ellipses
+`substitutions` | `{}` | overwrite default substitutions
+
+Further reading
+---------------
+
+SmartyPants extension is based on the original SmartyPants implementation
+by John Gruber. Please read its [documentation][1] for details.
+
+[1]: http://daringfireball.net/projects/smartypants/
+++ /dev/null
-title: SmartyPants Extension
-prev_title: Sane Lists Extension
-prev_url: sane_lists.html
-next_title: Table of Contents Extension
-next_url: toc.html
-
-SmartyPants
-===========
-
-Summary
--------
-
-The SmartyPants extension converts ASCII dashes, quotes and ellipses to
-their HTML entity equivalents.
-
-ASCII symbol | Replacements | HTML Entities | Substitution Keys
------------- | --------------- | ------------------- | ----------------------------------------
-`'` | ‘ ’ | `‘` `’` | `'left-single-quote'`, `'right-single-quote'`
-`"` | “ ” | `“` `”` | `'left-double-quote'`, `'right-double-quote'`
-`<< >>` | « » | `«` `»` | `'left-angle-quote'`, `'right-angle-quote'`
-`...` | … | `…` | `'ellipsis'`
-`--` | – | `–` | `'ndash'`
-`---` | — | `—` | `'mdash'`
-
-Using the configuration option 'substitutions' you can overwrite the
-default substitutions. Just pass a dict mapping (a subset of) the
-keys to the substitution strings.
-
-For example, one might use the following configuration to get correct quotes for
-the German language:
-
- extensionConfigs = {
- 'smarty': {
- 'substitutions': {
- 'left-single-quote': '‚', # sb is not a typo!
- 'right-single-quote': '‘',
- 'left-double-quote': '„',
- 'right-double-quote': '“'
- }
- }
- }
-
-!!! note
- This extension re-implements the Python [SmartyPants]
- library by integrating it into the markdown parser.
- While this does not provide any additional features,
- it does offer a few advantages. Notably, it will not
- try to work on highlighted code blocks (using the
- [CodeHilite] Extension) like the third party library
- has been known to do.
-
-[SmartyPants]: http://pythonhosted.org/smartypants/
-[CodeHilite]: code_hilite.html
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify
-`markdown.extensions.smarty` as the name of the extension.
-
-See the [Library Reference](../reference.html#extensions) for information about
-configuring extensions.
-
-The following options are provided to configure the output:
-
-Option | Default value | Description
------- | ------------- | -----------
-`smart_dashes` | `True` | whether to convert dashes
-`smart_quotes` | `True` | whether to convert straight quotes
-`smart_angled_quotes` | `False` | whether to convert angled quotes
-`smart_ellipses` | `True` | whether to convert ellipses
-`substitutions` | `{}` | overwrite default substitutions
-
-Further reading
----------------
-
-SmartyPants extension is based on the original SmartyPants implementation
-by John Gruber. Please read it's [documentation][1] for details.
-
-[1]: http://daringfireball.net/projects/smartypants/
--- /dev/null
+title: Tables Extension
+
+Tables
+======
+
+Summary
+-------
+
+The Tables extension adds the ability to create tables in Markdown documents.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+Tables are defined using the syntax established in [PHP Markdown Extra][php].
+
+[php]: http://www.michelf.com/projects/php-markdown/extra/#table
+
+Thus, the following text (taken from the above referenced PHP documentation):
+
+```md
+First Header | Second Header
+------------- | -------------
+Content Cell | Content Cell
+Content Cell | Content Cell
+```
+
+will be rendered as:
+
+```html
+<table>
+ <thead>
+ <tr>
+ <th>First Header</th>
+ <th>Second Header</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td>Content Cell</td>
+ <td>Content Cell</td>
+ </tr>
+ <tr>
+ <td>Content Cell</td>
+ <td>Content Cell</td>
+ </tr>
+ </tbody>
+</table>
+```
+
+Usage
+-----
+
+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.
+++ /dev/null
-title: Tables Extension
-prev_title: Footnotes Extension
-prev_url: footnotes.html
-next_title: Smart Strong Extension
-next_url: smart_strong.html
-
-Tables
-======
-
-Summary
--------
-
-The Tables extension adds the ability to create tables in Markdown documents.
-
-This extension is included in the standard Markdown library.
-
-Syntax
-------
-
-Tables are defined using the syntax established in [PHP Markdown Extra][php].
-
-[php]: http://www.michelf.com/projects/php-markdown/extra/#table
-
-Thus, the following text (taken from the above referenced PHP documentation):
-
- First Header | Second Header
- ------------- | -------------
- Content Cell | Content Cell
- Content Cell | Content Cell
-
-will be rendered as:
-
- <table>
- <thead>
- <tr>
- <th>First Header</th>
- <th>Second Header</th>
- </tr>
- </thead>
- <tbody>
- <tr>
- <td>Content Cell</td>
- <td>Content Cell</td>
- </tr>
- <tr>
- <td>Content Cell</td>
- <td>Content Cell</td>
- </tr>
- </tbody>
- </table>
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.tables`
-as the name of the extension.
-
-This extension does not accept any special configuration options.
--- /dev/null
+title: Table of Contents Extension
+
+Table of Contents
+=================
+
+Summary
+-------
+
+The Table of Contents extension generates a Table of Contents from a Markdown
+document and adds it into the resulting HTML document.
+
+This extension is included in the standard Markdown library.
+
+Syntax
+------
+
+By default, all headers will automatically have unique `id` attributes
+generated based upon the text of the header. Note this example, in which all
+three headers would have the same `id`:
+
+```md
+#Header
+#Header
+#Header
+```
+
+Results in:
+
+```html
+<h1 id="header">Header</h1>
+<h1 id="header_1">Header</h1>
+<h1 id="header_2">Header</h1>
+```
+
+Place a marker in the document where you would like the Table of Contents to
+appear. Then, a nested list of all the headers in the document will replace the
+marker. The marker defaults to `[TOC]` so the following document:
+
+```md
+[TOC]
+
+# Header 1
+
+## Header 2
+```
+
+would generate the following output:
+
+```html
+<div class="toc">
+ <ul>
+ <li><a href="#header-1">Header 1</a></li>
+ <ul>
+ <li><a href="#header-2">Header 2</a></li>
+ </ul>
+ </ul>
+</div>
+<h1 id="header-1">Header 1</h1>
+<h2 id="header-2">Header 2</h2>
+```
+
+Regardless of whether a `marker` is found in the document (or disabled), the
+Table of Contents is available as an attribute (`toc`) on the Markdown class.
+This allows one to insert the Table of Contents elsewhere in their page
+template. For example:
+
+```pycon
+>>> md = markdown.Markdown(extensions=['toc'])
+>>> html = md.convert(text)
+>>> page = render_some_template(context={'body': html, 'toc': md.toc})
+```
+
+The `toc_tokens` attribute is also available on the Markdown class and contains
+a nested list of dict objects. For example, the above document would result in
+the following object at `md.toc_tokens`:
+
+```python
+[
+ {
+ 'level': 1,
+ 'id': 'header-1',
+ 'name': 'Header 1',
+ 'children': [
+ {'level': 2, 'id': 'header-2', 'name': 'Header 2', 'children':[]}
+ ]
+ }
+]
+```
+
+Note that the `level` refers to the `hn` level. In other words, `<h1>` is level
+`1` and `<h2>` is level `2`, etc. Be aware that improperly nested levels in the
+input may result in odd nesting of the output.
+
+### Custom Labels
+
+In most cases, the text label in the Table of Contents should match the text of
+the header. However, occasionally that is not desirable. In that case, if this
+extension is used in conjunction with the [Attribute Lists Extension] and a
+`data-toc-label` attribute is defined on the header, then the contents of that
+attribute will be used as the text label for the item in the Table of Contents.
+For example, the following Markdown:
+
+[Attribute Lists Extension]: attr_list.md
+
+```md
+[TOC]
+
+# Functions
+
+## `markdown.markdown(text [, **kwargs])` { #markdown data-toc-label='markdown.markdown' }
+```
+would generate the following output:
+
+```html
+<div class="toc">
+ <ul>
+ <li><a href="#functions">Functions</a></li>
+ <ul>
+ <li><a href="#markdown">markdown.markdown</a></li>
+ </ul>
+ </ul>
+</div>
+<h1 id="functions">Functions</h1>
+<h2 id="markdown"><code>markdown.markdown(text [, **kwargs])</code></h2>
+```
+
+Notice that the text in the Table of Contents is much cleaner and easier to read
+in the context of a Table of Contents. The `data-toc-label` is not included in
+the HTML header element. Also note that the ID was manually defined in the
+attribute list to provide a cleaner URL when linking to the header. If the ID is
+not manually defined, it is always derived from the text of the header, never
+from the `data-toc-label` attribute.
+
+Usage
+-----
+
+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.
+
+The following options are provided to configure the output:
+
+* **`marker`**:
+ Text to find and replace with the Table of Contents. Defaults to `[TOC]`.
+
+ Set to an empty string to disable searching for a marker, which may save
+ some time, especially on long documents.
+
+* **`title`**:
+ Title to insert in the Table of Contents' `<div>`. Defaults to `None`.
+
+* **`anchorlink`**:
+ Set to `True` to cause all headers to link to themselves. Default is `False`.
+
+* **`permalink`**:
+ Set to `True` or a string to generate permanent links at the end of each header.
+ Useful with Sphinx style sheets.
+
+ When set to `True` the paragraph symbol (¶ or "`¶`") is used as
+ the link text. When set to a string, the provided string is used as the link
+ text.
+
+* **`baselevel`**:
+ Base level for headers. Defaults to `1`.
+
+ The `baselevel` setting allows the header levels to be automatically
+ adjusted to fit within the hierarchy of your HTML templates. For example,
+ suppose the Markdown text for a page should not contain any headers higher
+ than level 3 (`<h3>`). The following will accomplish that:
+
+ :::pycon
+ >>> text = '''
+ ... #Some Header
+ ... ## Next Level'''
+ >>> from markdown.extensions.toc import TocExtension
+ >>> html = markdown.markdown(text, extensions=[TocExtension(baselevel=3)])
+ >>> print html
+ <h3 id="some_header">Some Header</h3>
+ <h4 id="next_level">Next Level</h4>'
+
+* **`slugify`**:
+ Callable to generate anchors.
+
+ Default: `markdown.extensions.headerid.slugify`
+
+ In order to use a different algorithm to define the id attributes, define and
+ pass in a callable which takes the following two arguments:
+
+ * `value`: The string to slugify.
+ * `separator`: The Word Separator.
+
+ The callable must return a string appropriate for use in HTML `id` attributes.
+
+* **`separator`**:
+ Word separator. Character which replaces white space in id. Defaults to "`-`".
+
+* **`toc_depth`**
+ Define up to which section level "n" (`<h1>` to `<hn>`, where `1 <= n <= 6`)
+ to include in the Table of Contents. Defaults to `6`.
+
+ When used with conjunction with `baselevel` this parameter will limit the
+ resulting (adjusted) heading. That is, if both `toc_depth` and `baselevel`
+ are 3, then only the highest level will be present in the table.
+++ /dev/null
-title: Table of Contents Extension
-prev_title: SmartyPants Extension
-prev_url: smarty.html
-next_title: WikiLinks Extension
-next_url: wikilinks.html
-
-Table of Contents
-=================
-
-Summary
--------
-
-The Table of Contents extension generates a Table of Contents from a Markdown
-document and adds it into the resulting HTML document.
-
-This extension is included in the standard Markdown library.
-
-Syntax
-------
-
-By default, all headers will automatically have unique `id` attributes
-generated based upon the text of the header. Note this example, in which all
-three headers would have the same `id`:
-
- #Header
- #Header
- #Header
-
-Results in:
-
- <h1 id="header">Header</h1>
- <h1 id="header_1">Header</h1>
- <h1 id="header_2">Header</h1>
-
-Place a marker in the document where you would like the Table of Contents to
-appear. Then, a nested list of all the headers in the document will replace the
-marker. The marker defaults to `[TOC]` so the following document:
-
- [TOC]
-
- # Header 1
-
- ## Header 2
-
-would generate the following output:
-
- <div class="toc">
- <ul>
- <li><a href="#header-1">Header 1</a></li>
- <ul>
- <li><a href="#header-2">Header 2</a></li>
- </ul>
- </ul>
- </div>
- <h1 id="header-1">Header 1</h1>
- <h1 id="header-2">Header 2</h1>
-
-Regardless of whether a `marker` is found in the document (or disabled), the Table of
-Contents is available as an attribute (`toc`) on the Markdown class. This allows
-one to insert the Table of Contents elsewhere in their page template. For example:
-
- >>> md = markdown.Markdown(extensions=['markdown.extensions.toc'])
- >>> html = md.convert(text)
- >>> page = render_some_template(context={'body': html, 'toc': md.toc})
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.toc`
-as the name of the extension.
-
-See the [Library Reference](../reference.html#extensions) for information about
-configuring extensions.
-
-The following options are provided to configure the output:
-
-* **`marker`**:
- Text to find and replace with the Table of Contents. Defaults to `[TOC]`.
-
- Set to an empty string to disable searching for a marker, which may save some time,
- especially on long documents.
-
-* **`title`**:
- Title to insert in the Table of Contents' `<div>`. Defaults to `None`.
-
-* **`anchorlink`**:
- Set to `True` to cause all headers to link to themselves. Default is `False`.
-
-* **`permalink`**:
- Set to `True` or a string to generate permanent links at the end of each header.
- Useful with Sphinx style sheets.
-
- When set to `True` the paragraph symbol (¶ or "`¶`") is used as the link
- text. When set to a string, the provided string is used as the link text.
-
-* **`baselevel`**:
- Base level for headers. Defaults to `1`.
-
- The `baselevel` setting allows the header levels to be automatically adjusted to
- fit within the hierarchy of your HTML templates. For example, suppose the
- Markdown text for a page should not contain any headers higher than level 3
- (`<h3>`). The following will accomplish that:
-
- >>> text = '''
- ... #Some Header
- ... ## Next Level'''
- >>> from markdown.extensions.toc import TocExtension
- >>> html = markdown.markdown(text, extensions=[TocExtension(baselevel=3)])
- >>> print html
- <h3 id="some_header">Some Header</h3>
- <h4 id="next_level">Next Level</h4>'
-
-* **`slugify`**:
- Callable to generate anchors.
-
- Default: `markdown.extensions.headerid.slugify`
-
- In order to use a different algorithm to define the id attributes, define and
- pass in a callable which takes the following two arguments:
-
- * `value`: The string to slugify.
- * `separator`: The Word Separator.
-
- The callable must return a string appropriate for use in HTML `id` attributes.
-
-* **`separator`**:
- Word separator. Character which replaces white space in id. Defaults to "`-`".
\ No newline at end of file
--- /dev/null
+title: WikiLinks Extension
+
+# WikiLinks
+
+## Summary
+
+The WikiLinks extension adds support for [WikiLinks][]. Specifically, any
+``[[bracketed]]`` word is converted to a link.
+
+This extension is included in the standard Markdown library.
+
+[WikiLinks]: http://en.wikipedia.org/wiki/Wikilink
+
+## Syntax
+
+A ``[[bracketed]]`` word is any combination of upper or lower case letters,
+number, dashes, underscores and spaces surrounded by double brackets. Therefore
+
+```md
+[[Bracketed]]
+```
+
+would produce the following HTML:
+
+```html
+<a href="/Bracketed/" class="wikilink">Bracketed</a>
+```
+
+Note that WikiLinks are automatically assigned `class="wikilink"` making it
+easy to style WikiLinks differently from other links on a page if one so
+desires. See below for ways to alter the class.
+
+Also note that when a space is used, the space is converted to an underscore in
+the link but left as-is in the label. Perhaps an example would illustrate this
+best:
+
+```md
+[[Wiki Link]]
+```
+
+becomes
+
+```html
+<a href="/Wiki_Link/" class="wikilink">Wiki Link</a>
+```
+
+## Usage
+
+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.
+
+The default behavior is to point each link to the document root of the current
+domain and close with a trailing slash. Additionally, each link is assigned to
+the HTML class `wikilink`.
+
+The following options are provided to change the default behavior:
+
+* **`base_url`**: String to append to beginning of URL.
+
+ Default: `'/'`
+
+* **`end_url`**: String to append to end of URL.
+
+ Default: `'/'`
+
+* **`html_class`**: CSS class. Leave blank for none.
+
+ Default: `'wikilink'`
+
+* **`build_url`**: Callable which formats the URL from its parts.
+
+### Examples
+
+For an example, let us suppose links should always point to the sub-directory
+`/wiki/` and end with `.html`
+
+```pycon
+>>> from markdown.extensions.wikilinks import WikiLinkExtension
+>>> html = markdown.markdown(text,
+... extensions=[WikiLinkExtension(base_url='/wiki/', end_url='.html')]
+... )
+```
+
+The above would result in the following link for `[[WikiLink]]`.
+
+```html
+<a href="/wiki/WikiLink.html" class="wikilink">WikiLink</a>
+```
+
+If you want to do more that just alter the base and/or end of the URL, you
+could also pass in a callable which must accept three arguments (``label``,
+``base``, and ``end``). The callable must return the URL in it's entirety.
+
+```pycon
+>>> def my_url_builder(label, base, end):
+... # do stuff
+... return url
+...
+>>> html = markdown.markdown(text,
+... extensions=[WikiLinkExtension(build_url=my_url_builder)],
+... )
+```
+
+The option is also provided to change or remove the class attribute.
+
+```pycon
+>>> html = markdown.markdown(text,
+... extensions=[WikiLinkExtension(html_class='myclass')]
+... )
+```
+
+Would cause all WikiLinks to be assigned to the class `myclass`.
+
+```html
+<a href="/WikiLink/" class="myclass">WikiLink</a>
+```
+
+## Using with Meta-Data extension
+
+The WikiLink extension also supports the [Meta-Data](meta_data.md) extension.
+Please see the documentation for that extension for specifics. The supported
+meta-data keywords are:
+
+* `wiki_base_url`
+* `wiki_end_url`
+* `wiki_html_class`
+
+When used, the meta-data will override the settings provided through the
+`extension_configs` interface.
+
+This document:
+
+```md
+wiki_base_url: http://example.com/
+wiki_end_url: .html
+wiki_html_class:
+
+A [[WikiLink]] in the first paragraph.
+```
+
+would result in the following output (notice the blank `wiki_html_class`):
+
+```html
+<p>A <a href="http://example.com/WikiLink.html">WikiLink</a> in the first paragraph.</p>
+```
+++ /dev/null
-title: WikiLinks Extension
-prev_title: Table of Contents Extension
-prev_url: toc.html
-next_title: Extension API
-next_url: api.html
-
-WikiLinks
-=========
-
-Summary
--------
-
-The WikiLinks extension adds support for [WikiLinks][]. Specifically, any
-``[[bracketed]]`` word is converted to a link.
-
-This extension is included in the standard Markdown library.
-
-[WikiLinks]: http://en.wikipedia.org/wiki/Wikilink
-
-Syntax
-------
-
-A ``[[bracketed]]`` word is any combination of upper or lower case letters,
-number, dashes, underscores and spaces surrounded by double brackets. Therefore
-
- [[Bracketed]]
-
-would produce the following HTML:
-
- <a href="/Bracketed/" class="wikilink">Bracketed</a>
-
-Note that WikiLinks are automatically assigned `class="wikilink"` making it
-easy to style WikiLinks differently from other links on a page if one so
-desires. See below for ways to alter the class.
-
-Also note that when a space is used, the space is converted to an underscore in
-the link but left as-is in the label. Perhaps an example would illustrate this
-best:
-
- [[Wiki Link]]
-
-becomes
-
- <a href="/Wiki_Link/" class="wikilink">Wiki Link</a>
-
-Usage
------
-
-See [Extensions](index.html) for general extension usage, specify `markdown.extensions.wikilinks`
-as the name of the extension.
-
-See the [Library Reference](../reference.html#extensions) for information about
-configuring extensions.
-
-The default behavior is to point each link to the document root of the current
-domain and close with a trailing slash. Additionally, each link is assigned to
-the HTML class `wikilink`.
-
-The following options are provided to change the default behavior:
-
-* **`base_url`**: String to append to beginning of URL.
-
- Default: `'/'`
-
-* **`end_url`**: String to append to end of URL.
-
- Default: `'/'`
-
-* **`html_class`**: CSS class. Leave blank for none.
-
- Default: `'wikilink'`
-
-* **`build_url`**: Callable which formats the URL from its parts.
-
-### Examples ###
-
-For an example, let us suppose links should always point to the sub-directory
-`/wiki/` and end with `.html`
-
- >>> from markdown.extensions.wikilinks import WikiLinkExtension
- >>> html = markdown.markdown(text,
- ... extensions=[WikiLinkExtension(base_url='/wiki/', end_url='.html')]
- ... )
-
-The above would result in the following link for `[[WikiLink]]`.
-
- <a href="/wiki/WikiLink.html" class="wikilink">WikiLink</a>
-
-If you want to do more that just alter the base and/or end of the URL, you
-could also pass in a callable which must accept three arguments (``label``,
-``base``, and ``end``). The callable must return the URL in it's entirety.
-
- >>> def my_url_builder(label, base, end):
- ... # do stuff
- ... return url
- ...
- >>> html = markdown.markdown(text,
- ... extensions=[WikiLinkExtension(build_url=my_url_builder)],
- ... )
-
-The option is also provided to change or remove the class attribute.
-
- >>> html = markdown.markdown(text,
- ... extensions=[WikiLinkExtension(html_class='myclass')]
- ... )
-
-Would cause all WikiLinks to be assigned to the class `myclass`.
-
- <a href="/WikiLink/" class="myclass">WikiLink</a>
-
-Using with Meta-Data extension
-------------------------------
-
-The WikiLink extension also supports the [Meta-Data](meta_data.html) extension.
-Please see the documentation for that extension for specifics. The supported
-meta-data keywords are:
-
-* `wiki_base_url`
-* `wiki_end_url`
-* `wiki_html_class`
-
-When used, the meta-data will override the settings provided through the
-`extension_configs` interface.
-
-This document:
-
- wiki_base_url: http://example.com/
- wiki_end_url: .html
- wiki_html_class:
-
- A [[WikiLink]] in the first paragraph.
-
-would result in the following output (notice the blank `wiki_html_class`):
-
- <p>A <a href="http://example.com/WikiLink.html">WikiLink</a> in the first paragraph.</p>
--- /dev/null
+title: Python-Markdown
+
+Python-Markdown
+===============
+
+This is a Python implementation of John Gruber's
+[Markdown](http://daringfireball.net/projects/markdown/).
+It is almost completely compliant with the reference implementation,
+though there are a few very minor [differences](#differences). See John's
+[Syntax Documentation](http://daringfireball.net/projects/markdown/syntax)
+for the syntax rules.
+
+To get started, see the [installation instructions](install.md), the [library
+reference](reference.md), and the [command line interface](cli.md).
+
+Goals
+-----
+
+The Python-Markdown project is developed with the following goals in mind:
+
+* Maintain a Python 2 *and* Python 3 library (with an optional CLI wrapper)
+ suited to use in web server environments (never raise an exception, never
+ write to stdout, etc.) as an implementation of the markdown parser that
+ follows the [syntax rules](http://daringfireball.net/projects/markdown/syntax)
+ and the behavior of the original (markdown.pl) implementation as reasonably as
+ possible (see [differences](#differences) for a few exceptions).
+
+* Provide an [Extension API](extensions/api.md) which makes it possible
+ to change and/or extend the behavior of the parser.
+
+Features
+--------
+
+In addition to the basic markdown syntax, Python-Markdown supports the following
+features:
+
+* __International Input__
+
+ Python-Markdown will accept [input](reference.md#text) in any language
+ supported by Unicode including bi-directional text. In fact the test suite
+ includes documents written in Russian and Arabic.
+
+* __Extensions__
+
+ Various [extensions](extensions/index.md) are provided (including
+ [extra](extensions/extra.md)) to change and/or extend the base syntax.
+ Additionally, a public [Extension API](extensions/api.md) is available
+ to write your own extensions.
+
+* __Output Formats__
+
+ Python-Markdown can output documents with either HTML or XHTML style tags.
+ See the [Library Reference](reference.md#output_format) for details.
+
+* __Command Line Interface__
+
+ In addition to being a Python Library, a
+ [command line script](cli.md) is available for your convenience.
+
+Differences
+-----------
+
+While Python-Markdown strives to fully implement markdown as described in the
+[syntax rules](http://daringfireball.net/projects/markdown/syntax), the rules
+can be interpreted in different ways and different implementations
+occasionally vary in their behavior (see the
+[Babelmark FAQ](http://johnmacfarlane.net/babelmark2/faq.html#what-are-some-examples-of-interesting-divergences-between-implementations)
+for some examples). Known and intentional differences found in Python-Markdown
+are summarized below:
+
+* __Middle-Word Emphasis__
+
+ Python-Markdown defaults to ignoring middle-word emphasis (and strong
+ emphasis). In other words, `some_long_filename.txt` will not become
+ `some<em>long</em>filename.txt`. This can be switched off if desired. See
+ the [Legacy EM Extension](extensions/legacy_em.md) for details.
+
+* __Indentation/Tab Length__
+
+ The [syntax rules](http://daringfireball.net/projects/markdown/syntax#list)
+ clearly state that when a list item consists of multiple paragraphs, "each
+ subsequent paragraph in a list item **must** be indented by either 4 spaces
+ or one tab" (emphasis added). However, many implementations do not enforce
+ this rule and allow less than 4 spaces of indentation. The implementers of
+ Python-Markdown consider it a bug to not enforce this rule.
+
+ This applies to any block level elements nested in a list, including
+ paragraphs, sub-lists, blockquotes, code blocks, etc. They **must** always
+ be indented by at least four spaces (or one tab) for each level of nesting.
+
+ In the event that one would prefer different behavior,
+ [tab_length](reference.md#tab_length) can be set to whatever length is
+ desired. Be warned however, as this will affect indentation for all aspects
+ of the syntax (including root level code blocks).
+
+* __Consecutive Lists__
+
+ While the syntax rules are not clear on this, many implementations (including
+ the original) do not end one list and start a second list when the list marker
+ (asterisks, pluses, hyphens, and numbers) changes. For consistency,
+ Python-Markdown maintains the same behavior with no plans to change in the
+ foreseeable future. That said, the [Sane List Extension](extensions/sane_lists.md)
+ is available to provide a less surprising behavior.
+
+Support
+-------
+
+You may ask for help and discuss various other issues on the [mailing list][]
+and report bugs on the [bug tracker][].
+
+[mailing list]: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
+[bug tracker]: http://github.com/Python-Markdown/markdown/issues
+++ /dev/null
-next_url: install.html
-next_title: Installation
-prev_title: Table of Contents
-prev_url: siteindex.html
-
-Python-Markdown
-===============
-
-This is a Python implementation of John Gruber's
-[Markdown](http://daringfireball.net/projects/markdown/).
-It is almost completely compliant with the reference implementation,
-though there are a few very minor [differences](#differences). See John's
-[Syntax Documentation](http://daringfireball.net/projects/markdown/syntax)
-for the syntax rules.
-
-See the [installation instructions](install.html) to get started.
-
-Goals
------
-
-The Python-Markdown project is developed with the following goals in mind:
-
-* Maintain a Python 2 *and* Python 3 library (with an optional CLI wrapper)
- suited to use in web server environments (never raise an exception, never
- write to stdout, etc.) as an implementation of the markdown parser that
- follows the [syntax rules](http://daringfireball.net/projects/markdown/syntax)
- and the behavior of the original (markdown.pl) implementation as reasonably
- as possible (see [differences](#differences) for a few exceptions).
-
-* Provide an [Extension API](extensions/api.html) which makes it possible
- to change and/or extend the behavior of the parser.
-
-Features
---------
-
-In addition to the basic markdown syntax, Python-Markdown supports the following
-features:
-
-* __International Input__
-
- Python-Markdown will accept [input](reference.html#text) in any language
- supported by Unicode including bi-directional text. In fact the test suite
- includes documents written in Russian and Arabic.
-
-* __Extensions__
-
- Various [extensions](extensions/index.html) are provided (including
- [extra](extensions/extra.html)) to change and/or extend the base syntax.
- Additionally, a public [Extension API](extensions/api.html) is available
- to write your own extensions.
-
-* __Output Formats__
-
- Python-Markdown can output documents in HTML4, XHTML and HTML5. See the
- [Library Reference](reference.html#output_format) for details.
-
-* __Command Line Interface__
-
- In addition to being a Python Library, a
- [command line script](cli.html) is available for your convenience.
-
-Differences
------------
-
-While Python-Markdown strives to fully implement markdown as described in the
-[syntax rules](http://daringfireball.net/projects/markdown/syntax), the rules
-can be interpreted in different ways and different implementations
-occasionally vary in their behavior (see the
-[Babelmark FAQ](http://johnmacfarlane.net/babelmark2/faq.html#what-are-some-examples-of-interesting-divergences-between-implementations)
-for some examples). Known and intentional differences found in Python-Markdown
-are summarized below:
-
-* __Middle-Word Emphasis__
-
- Python-Markdown defaults to ignoring middle-word emphasis. In other words,
- `some_long_filename.txt` will not become `some<em>long</em>filename.txt`.
- This can be switched off if desired. See the
- [Library Reference](reference.html#smart_emphasis) for details.
-
-* __Indentation/Tab Length__
-
- The [syntax rules](http://daringfireball.net/projects/markdown/syntax#list)
- clearly state that when a list item consists of multiple paragraphs, "each
- subsequent paragraph in a list item **must** be indented by either 4 spaces
- or one tab" (emphasis added). However, many implementations do not enforce
- this rule and allow less than 4 spaces of indentation. The implementers of
- Python-Markdown consider it a bug to not enforce this rule.
-
- This applies to any block level elements nested in a list, including
- paragraphs, sub-lists, blockquotes, code blocks, etc. They **must** always
- be indented by at least four spaces (or one tab) for each level of nesting.
-
- In the event that one would prefer different behavior,
- [tab_length](reference.html#tab_length) can be set to whatever length is
- desired. Be warned however, as this will affect indentation for all aspects
- of the syntax (including root level code blocks).
-
-* __Consecutive Lists__
-
- While the syntax rules are not clear on this, many implementations (including
- the original) do not end one list and start a second list when the list marker
- (asterisks, pluses, hyphens, and numbers) changes. For consistency,
- Python-Markdown maintains the same behavior with no plans to change in the
- foreseeable future. That said, the [Sane List Extension](extensions/sane_lists.html)
- is available to provide a less surprising behavior.
-
-
-Support
--------
-
-You may ask for help and discuss various other issues on the [mailing list][]
-and report bugs on the [bug tracker][].
-
-[mailing list]: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
-[bug tracker]: http://github.com/waylan/Python-Markdown/issues
--- /dev/null
+title: Installation
+
+# Installing Python-Markdown
+
+## The Easy Way
+
+The easiest way to install Python-Markdown is simply to type the
+following command from the command line:
+
+```bash
+pip install markdown
+```
+
+That's it! You're ready to [use](reference.md) Python-Markdown. Enjoy!
+
+For more detailed instructions on installing Python packages, see the
+[Installing Packages] tutorial in the [Python Packaging User Guide].
+
+[Installing Packages]: https://packaging.python.org/tutorials/installing-packages/
+[Python Packaging User Guide]: https://packaging.python.org/
+
+## Using the Git Repository {: #git }
+
+If you're the type that likes to live on the edge, you may want to keep up with
+the latest additions and bug fixes in the repository between releases.
+Python-Markdown is maintained in a Git repository on GitHub.com. To
+get a copy of Python-Markdown from the repository do the following from the
+command line:
+
+```bash
+pip install git+https://github.com/Python-Markdown/markdown.git
+```
+++ /dev/null
-title: Installation
-prev_title: Summary
-prev_url: index.html
-next_title: Library Reference
-next_url: reference.html
-
-Installing Python-Markdown
-==========================
-
-The Easy Way
-------------
-
-The easiest way to install Python-Markdown is simply to type one of the
-following commands from the command line as an Admin/Root user:
-
- pip install markdown
-
-or
-
- easy_install markdown
-
-That's it! You're ready to [use](reference.html) Python-Markdown. Enjoy!
-
-Installing on Windows {: #windows }
------------------------------------
-
-Download the Windows installer (`.exe`) from
-[PyPI](http://pypi.python.org/pypi/Markdown)
-
-Double-click the file and follow the instructions.
-
-If you prefer to manually install Python-Markdown in Windows, download the
-Zip file, unzip it, and on the command line in the directory you unzipped to,
-run the following command:
-
- C://path/to/python.exe setup.py install
-
-If you plan to use the provided command line script, you need to make sure your
-script directory is on your system path. On a typical Python install of Windows
-the Scripts directory is `C:\PythonXX\Scripts\` (were "XX" is the Python version
-number, i.e., "27"). Adjust the path according to your system and add to your
-system path.
-
-Installing on *nix Systems {: #linux }
---------------------------------------
-
-From the command line do the following (where 2.x is the version number):
-
- wget http://pypi.python.org/packages/source/M/Markdown/Markdown-2.x.tar.gz
- tar xvzf Markdown-2.x.tar.gz
- cd markdown-2.x/
- sudo python setup.py install
-
-See [PyPI](http://pypi.python.org/pypi/Markdown) for all available versions.
-
-Using the Git Repository {: #git }
-----------------------------------
-
-If you're the type that likes to live on the edge, you may want to keep up with
-the latest additions and bug fixes in the repository between releases.
-Python-Markdown is maintained in a Git repository on GitHub.com. To
-get a copy of Python-Markdown from the repository do the following from the
-command line:
-
- git clone git://github.com/waylan/Python-Markdown.git python-markdown
- cd python-markdown
- python setup.py install
--- /dev/null
+title: Library Reference
+
+# Using Markdown as a Python Library
+
+First and foremost, Python-Markdown is intended to be a python library module
+used by various projects to convert Markdown syntax into HTML.
+
+## The Basics
+
+To use markdown as a module:
+
+```python
+import markdown
+html = markdown.markdown(your_text_string)
+```
+
+## The Details
+
+Python-Markdown provides two public functions ([`markdown.markdown`](#markdown)
+and [`markdown.markdownFromFile`](#markdownFromFile)) both of which wrap the
+public class [`markdown.Markdown`](#Markdown). If you're processing one
+document at a time, these functions will serve your needs. However, if you need
+to process multiple documents, it may be advantageous to create a single
+instance of the `markdown.Markdown` class and pass multiple documents through
+it. If you do use a single instance though, make sure to call the `reset`
+method appropriately ([see below](#convert)).
+
+### markdown.markdown(text [, **kwargs]) {: #markdown }
+
+The following options are available on the `markdown.markdown` function:
+
+__text__{: #text }
+
+: The source Unicode string. (required)
+
+ !!! note "Important"
+ Python-Markdown expects **Unicode** as input (although
+ some simple ASCII strings *may* work) and returns output as Unicode.
+ Do not pass encoded strings to it! If your input is encoded, (e.g. as
+ UTF-8), it is your responsibility to decode it. For example:
+
+ :::python
+ input_file = codecs.open("some_file.txt", mode="r", encoding="utf-8")
+ text = input_file.read()
+ html = markdown.markdown(text)
+
+ If you want to write the output to disk, you *must* encode it yourself:
+
+ :::python
+ output_file = codecs.open("some_file.html", "w",
+ encoding="utf-8",
+ errors="xmlcharrefreplace"
+ )
+ output_file.write(html)
+
+__extensions__{: #extensions }
+
+: A list of extensions.
+
+ Python-Markdown provides an [API](extensions/api.md) for third parties to
+ write extensions to the parser adding their own additions or changes to the
+ syntax. A few commonly used extensions are shipped with the markdown
+ library. See the [extension documentation](extensions/index.md) for a
+ list of available extensions.
+
+ The list of extensions may contain instances of extensions and/or strings
+ of extension names.
+
+ :::python
+ extensions=[MyExtClass(), 'myext', 'path.to.my.ext:MyExtClass']
+
+ !!! note
+ The preferred method is to pass in an instance of an extension. Strings
+ should only be used when it is impossible 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
+ defined when initiating the class instance rather than using the
+ [`extension_configs`](#extension_configs) keyword. For example:
+
+ :::python
+ from markdown.extensions import Extension
+ class MyExtClass(Extension):
+ # define your extension here...
+
+ markdown.markdown(text, extensions=[MyExtClass(option='value')])
+
+ 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.
+
+ 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 MyExtClass
+
+ Then load the extension as follows:
+
+ :::python
+ markdown.markdown(text, extensions=['path.to.module:MyExtClass'])
+
+ 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 can only pass in
+ configuration settings to the extension by using the
+ [`extension_configs`](#extension_configs) keyword.
+
+ !!! seealso "See Also"
+ See the documentation of the [Extension API](extensions/api.md) for
+ assistance in creating extensions.
+
+__extension_configs__{: #extension_configs }
+
+: A dictionary of configuration settings for extensions.
+
+ Any configuration settings will only be passed to extensions loaded by name
+ (as a string). When loading extensions as class instances, pass the
+ configuration settings directly to the class when initializing it.
+
+ !!! Note
+ The preferred method is to pass in an instance of an extension, which
+ does not require use of the `extension_configs` keyword at all.
+ See the [extensions](#extensions) keyword for details.
+
+ The dictionary of configuration settings must be in the following format:
+
+ :::python
+ extension_configs = {
+ 'extension_name_1': {
+ 'option_1': 'value_1',
+ 'option_2': 'value_2'
+ },
+ 'extension_name_2': {
+ 'option_1': 'value_1'
+ }
+ }
+
+ 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.
+
+__output_format__{: #output_format }:
+
+: Format of output.
+
+ Supported formats are:
+
+ * `"xhtml"`: Outputs XHTML style tags. **Default**.
+ * `"html5"`: Outputs HTML style tags.
+
+ The values can be in either lowercase or uppercase.
+
+__tab_length__{: #tab_length }:
+
+: Length of tabs in the source. Default: 4
+
+### `markdown.markdownFromFile (**kwargs)` {: #markdownFromFile }
+
+With a few exceptions, `markdown.markdownFromFile` accepts the same options as
+`markdown.markdown`. It does **not** accept a `text` (or Unicode) string.
+Instead, it accepts the following required options:
+
+__input__{: #input } (required)
+
+: The source text file.
+
+ `input` may be set to one of three options:
+
+ * a string which contains a path to a readable file on the file system,
+ * a readable file-like object,
+ * or `None` (default) which will read from `stdin`.
+
+__output__{: #output }
+
+: The target which output is written to.
+
+ `output` may be set to one of three options:
+
+ * a string which contains a path to a writable file on the file system,
+ * a writable file-like object,
+ * or `None` (default) which will write to `stdout`.
+
+__encoding__{: #encoding }
+
+: The encoding of the source text file.
+
+ Defaults to `"utf-8"`. The same encoding will always be used for input and output.
+ The `xmlcharrefreplace` error handler is used when encoding the output.
+
+ !!! Note
+ This is the only place that decoding and encoding of Unicode
+ takes place in Python-Markdown. If this rather naive solution does not
+ meet your specific needs, it is suggested that you write your own code
+ to handle your encoding/decoding needs.
+
+### markdown.Markdown([**kwargs]) {: #Markdown }
+
+The same options are available when initializing the `markdown.Markdown` class
+as on the [`markdown.markdown`](#markdown) function, except that the class does
+**not** accept a source text string on initialization. Rather, the source text
+string must be passed to one of two instance methods:
+
+#### Markdown.convert(source) {: #convert }
+
+The `source` text must meet the same requirements as the [`text`](#text)
+argument of the [`markdown.markdown`](#markdown) function.
+
+You should also use this method if you want to process multiple strings
+without creating a new instance of the class for each string.
+
+```python
+md = markdown.Markdown()
+html1 = md.convert(text1)
+html2 = md.convert(text2)
+```
+
+Depending on which options and/or extensions are being used, the parser may
+need its state reset between each call to `convert`, otherwise performance
+can degrade drastically:
+
+```python
+html1 = md.convert(text1)
+md.reset()
+html2 = md.convert(text2)
+```
+
+To make this easier, you can also chain calls to `reset` together:
+
+```python
+html3 = md.reset().convert(text3)
+```
+
+#### Markdown.convertFile(**kwargs) {: #convertFile }
+
+The arguments of this method are identical to the arguments of the same
+name on the `markdown.markdownFromFile` function ([`input`](#input),
+[`output`](#output), and [`encoding`](#encoding)). As with the
+[`convert`](#convert) method, this method should be used to
+process multiple files without creating a new instance of the class for
+each document. State may need to be `reset` between each call to
+`convertFile` as is the case with `convert`.
+++ /dev/null
-title: Library Reference
-prev_title: Installation
-prev_url: install.html
-next_title: Command Line
-next_url: cli.html
-
-
-Using Markdown as a Python Library
-==================================
-
-First and foremost, Python-Markdown is intended to be a python library module
-used by various projects to convert Markdown syntax into HTML.
-
-The Basics
-----------
-
-To use markdown as a module:
-
- import markdown
- html = markdown.markdown(your_text_string)
-
-The Details
------------
-
-Python-Markdown provides two public functions ([`markdown.markdown`](#markdown)
-and [`markdown.markdownFromFile`](#markdownFromFile)) both of which wrap the
-public class [`markdown.Markdown`](#Markdown). If you're processing one
-document at a time, these functions will serve your needs. However, if you need
-to process multiple documents, it may be advantageous to create a single
-instance of the `markdown.Markdown` class and pass multiple documents through
-it. If you do use a single instance though, make sure to call the `reset`
-method appropriately ([see below](#convert)).
-
-### `markdown.markdown (text [, **kwargs])` {: #markdown }
-
-The following options are available on the `markdown.markdown` function:
-
-* __`text`__{: #text } (required): The source Unicode string.
-
- !!! note "Important"
- Python-Markdown expects **Unicode** as input (although
- some simple ASCII strings *may* work) and returns output as Unicode.
- Do not pass encoded strings to it! If your input is encoded, (e.g. as
- UTF-8), it is your responsibility to decode it. For example:
-
- input_file = codecs.open("some_file.txt", mode="r", encoding="utf-8")
- text = input_file.read()
- html = markdown.markdown(text)
-
- If you want to write the output to disk, you *must* encode it yourself:
-
- output_file = codecs.open("some_file.html", "w",
- encoding="utf-8",
- errors="xmlcharrefreplace"
- )
- output_file.write(html)
-
-* __`extensions`__{: #extensions }: A list of extensions.
-
- Python-Markdown provides an [API](extensions/api.html) for third parties to
- write extensions to the parser adding their own additions or changes to the
- syntax. A few commonly used extensions are shipped with the markdown
- library. See the [extension documentation](extensions/index.html) for a
- list of available extensions.
-
- The list of extensions may contain instances of extensions and/or strings
- of extension names.
-
- extensions=[MyExtension(), 'path.to.my.ext']
-
- !!! note
- The preferred method is to pass in an instance of an extension. Strings
- should only be used when it is impossible 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
- defined when initiating the class instance rather than using the
- [`extension_configs`](#extension_configs) keyword. For example:
-
- from markdown.extensions import Extension
- class MyExtension(Extension):
- # define your extension here...
-
- markdown.markdown(text, extensions=[MyExtension(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
- supported. Therefore, to import the 'extra' extension, one could do
- `extensions=['markdown.extensions.extra']`
-
- 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.
-
- 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. However,
- doing so will not effect the behavior of the parser.
-
- 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
- assistance in creating extensions.
-
-* __`extension_configs`__{: #extension_configs }: A dictionary of
- configuration settings for extensions.
-
- Any configuration settings will only be passed to extensions loaded by name
- (as a string). When loading extensions as class instances, pass the
- configuration settings directly to the class when initializing it.
-
- !!! Note
- The preferred method is to pass in an instance of an extension, which
- does not require use of the `extension_configs` keyword at all.
- See the [extensions](#extensions) keyword for details.
-
- The dictionary of configuration settings must be in the following format:
-
- extension_configs =
- {
- 'extension_name_1':
- {
- 'option_1': 'value_1',
- 'option_2': 'value_2'
- },
- {
- 'extension_name_2':
- {
- 'option_1': 'value_1'
- }
- }
-
- See the documentation specific to the extension you are using for help in
- specifying configuration settings for that extension.
-
-* __`output_format`__{: #output_format }: Format of output.
-
- Supported formats are:
-
- * `"xhtml1"`: Outputs XHTML 1.x. **Default**.
- * `"xhtml5"`: Outputs XHTML style tags of HTML 5
- * `"xhtml"`: Outputs latest supported version of XHTML (currently XHTML 1.1).
- * `"html4"`: Outputs HTML 4
- * `"html5"`: Outputs HTML style tags of HTML 5
- * `"html"`: Outputs latest supported version of HTML (currently HTML 4).
-
- The values can be in either lowercase or uppercase.
-
- !!! warning
- It is suggested that the more specific formats (`"xhtml1"`, `"html5"`, &
- `"html4"`) be used as the more general formats (`"xhtml"` or `"html"`) may
- change in the future if it makes sense at that time.
-
-* __`safe_mode`__{: #safe_mode }: Disallow raw HTML.
-
- !!! warning
- "`safe_mode`" is deprecated and should not be used.
-
- HTML sanitizers (like [Bleach]) provide a better solution for
- dealing with markdown text submitted by untrusted users.
-
- import markdown
- import bleach
- html = bleach.clean(markdown.markdown(untrusted_text))
-
- See the [release notes] for more info.
-
-[Bleach]: https://github.com/jsocol/bleach
-[release notes]: release-2.6.html
-
- The following values are accepted:
-
- * `False` (Default): Raw HTML is passed through unaltered.
-
- * `replace`: Replace all HTML blocks with the text assigned to
- `html_replacement_text` To maintain backward compatibility, setting
- `safe_mode=True` will have the same effect as `safe_mode='replace'`.
-
- To replace raw HTML with something other than the default, do:
-
- md = markdown.Markdown(safe_mode='replace',
- html_replacement_text='--RAW HTML NOT ALLOWED--')
-
- * `remove`: All raw HTML will be completely stripped from the text with
- no warning to the author.
-
- * `escape`: All raw HTML will be escaped and included in the document.
-
- For example, the following source:
-
- Foo <b>bar</b>.
-
- Will result in the following HTML:
-
- <p>Foo <b>bar</b>.</p>
-
- !!! Note
- "safe_mode" also alters the default value for the
- [`enable_attributes`](#enable_attributes) option.
-
-
-* __`html_replacement_text`__{: #html_replacement_text }: Text used when
- safe_mode is set to `replace`. Defaults to `[HTML_REMOVED]`.
-
- !!! warning
- "`html_replacement_text`" is deprecated and should not be used.
- See the [release notes] for more info.
-
-* __`tab_length`__{: #tab_length }: Length of tabs in the source. Default: 4
-
-* __`enable_attributes`__{: #enable_attributes}: Enable the conversion of
- attributes. Defaults to `True`, unless [`safe_mode`](#safe_mode) is enabled,
- in which case the default is `False`.
-
- !!! Note
- `safe_mode` only overrides the default. If `enable_attributes`
- is explicitly set, the explicit value is used regardless of `safe_mode`.
- However, this could potentially allow an untrusted user to inject
- JavaScript into your documents.
-
-* __`smart_emphasis`__{: #smart_emphasis }: Treat `_connected_words_`
- intelligently Default: True
-
-* __`lazy_ol`__{: #lazy_ol }: Ignore number of first item of ordered lists.
- Default: True
-
- Given the following list:
-
- 4. Apples
- 5. Oranges
- 6. Pears
-
- By default markdown will ignore the fact the the first line started
- with item number "4" and the HTML list will start with a number "1".
- If `lazy_ol` is set to `False`, then markdown will output the following
- HTML:
-
- <ol>
- <li start="4">Apples</li>
- <li>Oranges</li>
- <li>Pears</li>
- </ol>
-
-
-### `markdown.markdownFromFile (**kwargs)` {: #markdownFromFile }
-
-With a few exceptions, `markdown.markdownFromFile` accepts the same options as
-`markdown.markdown`. It does **not** accept a `text` (or Unicode) string.
-Instead, it accepts the following required options:
-
-* __`input`__{: #input } (required): The source text file.
-
- `input` may be set to one of three options:
-
- * a string which contains a path to a readable file on the file system,
- * a readable file-like object,
- * or `None` (default) which will read from `stdin`.
-
-* __`output`__{: #output }: The target which output is written to.
-
- `output` may be set to one of three options:
-
- * a string which contains a path to a writable file on the file system,
- * a writable file-like object,
- * or `None` (default) which will write to `stdout`.
-
-* __`encoding`__{: #encoding }: The encoding of the source text file. Defaults
- to `"utf-8"`. The same encoding will always be used for input and output.
- The `xmlcharrefreplace` error handler is used when encoding the output.
-
- !!! Note
- This is the only place that decoding and encoding of Unicode
- takes place in Python-Markdown. If this rather naive solution does not
- meet your specific needs, it is suggested that you write your own code
- to handle your encoding/decoding needs.
-
-### `markdown.Markdown ([**kwargs])` {: #Markdown }
-
-The same options are available when initializing the `markdown.Markdown` class
-as on the [`markdown.markdown`](#markdown) function, except that the class does
-**not** accept a source text string on initialization. Rather, the source text
-string must be passed to one of two instance methods:
-
-* `Markdown.convert(source)`{: #convert }
-
- The `source` text must meet the same requirements as the [`text`](#text)
- argument of the [`markdown.markdown`](#markdown) function.
-
- You should also use this method if you want to process multiple strings
- without creating a new instance of the class for each string.
-
- md = markdown.Markdown()
- html1 = md.convert(text1)
- html2 = md.convert(text2)
-
- Depending on which options and/or extensions are being used, the parser may
- need its state reset between each call to `convert`, otherwise performance
- can degrade drastically:
-
- html1 = md.convert(text1)
- md.reset()
- html2 = md.convert(text2)
-
- To make this easier, you can also chain calls to `reset` together:
-
- html3 = md.reset().convert(text3)
-
-
-* `Markdown.convertFile(**kwargs)`{: #convertFile }
-
- The arguments of this method are identical to the arguments of the same
- name on the `markdown.markdownFromFile` function ([`input`](#input),
- [`output`](#output), and [`encoding`](#encoding)). As with the
- [`convert`](#convert) method, this method should be used to
- process multiple files without creating a new instance of the class for
- each document. State may need to be `reset` between each call to
- `convertFile` as is the case with `convert`.
+++ /dev/null
-title: Release Notes for v2.0.1
-prev_title: Release Notes for v2.0.2
-prev_url: release-2.0.2.html
-next_title: Release Notes for v2.0
-next_url: release-2.0.html
-
-Python-Markdown 2.0.1 Release Notes
-===================================
-
-Python-Markdown 2.0.1 is a bug-fix release. No new features have been added.
-Most notably, various issues with the command line script have been fixed.
-There have also been a few fixes for minor parsing bugs in some edge cases.
-For a full list of changes, see the git log.
-
-Backwards-incompatible Changes
-------------------------------
-
-Due to various complications in how Python handles command line scripts in
-difference systems and with different installation tools, we were forced to
-rename the command line script to `markdown` (no `.py`). A matching batch
-script will get installed on Windows. Any shell scripts which call
-`markdown.py` will need to be altered to call `markdown` instead.
+++ /dev/null
-title: Release Notes for v2.0.2
-prev_title: Release Notes for v2.1.0
-prev_url: release-2.1.0.html
-next_title: Release Notes for v2.0.1
-next_url: release-2.0.1.html
-
-Python-Markdown 2.0.2 Release Notes
-===================================
-
-Python-Markdown 2.0.2 is a bug-fix release. No new features have been added.
-Most notably, the setup script has been updated to include a dependency on
-ElementTree on older versions of Python (< 2.5). There have also been a few
-fixes for minor parsing bugs in some edge cases. For a full list of changes,
-see the git log.
-
+++ /dev/null
-title: Release Notes for v2.0
-prev_title: Release Notes for v2.0.1
-prev_url: release-2.0.1.html
-next_title: Authors
-next_url: authors.html
-
-Python-Markdown 2.0 Release Notes
-=================================
-
-We are happy to release Python-Markdown 2.0, which has been over a year in the
-making. We have rewritten significant portions of the code, dramatically
-extending the extension API, increased performance, and added numerous
-extensions to the distribution (including an extension that mimics PHP Markdown
-Extra), all while maintaining backward compatibility with the end user API in
-version 1.7.
-
-Python-Markdown supports Python versions 2.3, 2.4, 2.5, and 2.6. We have even
-released a version converted to Python 3.0!
-
-Backwards-incompatible Changes
-------------------------------
-
-While Python-Markdown has experienced numerous internal changes, those changes
-should only affect extension authors. If you have not written your own
-extensions, then you should not need to make any changes to your code.
-However, you may want to ensure that any third party extensions you are using
-are compatible with the new API.
-
-The new extension API is fully [documented](extensions/api.html) in the docs.
-Below is a summary of the significant changes:
-
-* The old home-grown NanoDOM has been replaced with ElementTree. Therefore all
- extensions must use ElementTree rather than the old NanoDOM.
-* The various processors and patterns are now stored with OrderedDicts rather
- than lists. Any code adding processors and/or patterns into Python-Markdown
- will need to be adjusted to use the new API using OrderedDicts.
-* The various types of processors available have been either combined, added,
- or removed. Ensure that your processors match the currently supported types.
-
-What's New in Python-Markdown 2.0
----------------------------------
-
-Thanks to the work of Artem Yunusov as part of GSoC 2008, Python-Markdown uses
-ElementTree internally to build the (X)HTML document from markdown source text.
-This has resolved various issues with the older home-grown NanoDOM and made
-notable increases in performance.
-
-Artem also refactored the Inline Patterns to better support nested patterns
-which has resolved many inconsistencies in Python-Markdown's parsing of the
-markdown syntax.
-
-The core parser had been completely rewritten, increasing performance and, for
-the first time, making it possible to override/add/change the way block level
-content is parsed.
-
-Python-Markdown now parses markdown source text more closely to the other
-popular implementations (Perl, PHP, etc.) than it ever has before. With the
-exception of a few minor insignificant differences, any difference should be
-considered a bug, rather than a limitation of the parser.
-
-The option to return HTML4 output as apposed to XHTML has been added. In
-addition, extensions should be able to easily add additional output formats.
-
-As part of implementing markdown in the Dr. Project project (a Trac fork), among
-other things, David Wolever refactored the "extension" keyword so that it
-accepts either the extension names as strings or instances of extensions. This
-makes it possible to include multiple extensions in a single module.
-
-Numerous extensions are included in the distribution by default. See
-[available_extensions](extensions/index.html) for a complete list.
-
-See the [Change Log](change_log.html) for a full list of changes.
-
+++ /dev/null
-title: Release Notes for v2.1.0
-prev_title: Release Notes for v2.1.1
-prev_url: release-2.1.1.html
-next_title: Release Notes for v2.0.2
-next_url: release-2.0.2.html
-
-Python-Markdown 2.1.0 Release Notes
-===================================
-
-We are pleased to release Python-Markdown 2.1 which makes many
-improvements on 2.0. In fact, we consider 2.1 to be what 2.0 should have been.
-While 2.1 consists mostly of bug fixes, bringing Python-Markdown more inline
-with other implementations, some internal improvements were made to the parser,
-a few new built-in extensions were added, and HTML5 support was added.
-
-Python-Markdown supports Python versions 2.4, 2.5, 2.6, 2.7, 3.1, and 3.2 out
-of the box. In fact, the same code base installs on Python 3.1 and 3.2 with no
-extra work by the end user.
-
-Backwards-incompatible Changes
-------------------------------
-
-While Python-Markdown has received only minor internal changes since the last
-release, there are a few backward-incompatible changes to note:
-
-* Support had been dropped for Python 2.3. No guarantees are made that the
-library will work in any version of Python lower than 2.4. Additionally, while
-the library had been tested with Python 2.4, consider Python 2.4 support to be
-depreciated. It is not likely that any future versions will continue to support
-any version of Python less than 2.5. Note that Python 3.0 is not supported due
-to a bug in its 2to3 tool. If you must use Python-Markdown with Python 3.0, it
-is suggested you manually use Python 3.1's 2to3 tool to do a conversion.
-
-* Python-Markdown previously accepted positional arguments on its class and
-wrapper methods. It now expects keyword arguments. Currently, the positional
-arguments should continue to work, but the solution feels hacky and may be
-removed in a future version. All users are encouraged to use keyword arguments
-as documented in the [Library Reference](reference.html).
-
-* Past versions of Python-Markdown provided module level Global variables which
-controlled the behavior of a few different aspects of the parser. Those global
-variables have been replaced with attributes on the Markdown class.
-Additionally, those attributes are settable as keyword arguments when
-initializing a class instance. Therefore, if you were editing the global
-variables (either by editing the source or by overriding them in your code),
-you should now set them on the class. See the
-[Library Reference](reference.html) for the options available.
-
-* If you have been using the [HeaderId](extensions/header_id.html) extension
-to define custom ids on headers, you will want to switch to using the new
-[Attribute List](extensions/attr_list.html) extension. The HeaderId extension
-now only auto-generates ids on headers which have not already had ids defined.
-Note that the [Extra](extensions/extra.html) extension has been switched to use
-Attribute Lists instead of HeaderId as it did previously.
-
-* Some code was moved into the `markdown.util` namespace which was previously
-in the `markdown` namespace. Extension authors may need to adjust a few
-import statements in their extensions to work with the changes.
-
-* The command line script name was changed to `markdown_py`. The previous name
-(`markdown`) was conflicting with people (and Linux package systems) who also
-had markdown.pl installed on there system as markdown.pl's command line script
-was also named `markdown`. Be aware that installing Python-Markdown 2.1
-will not remove the old versions of the script with different names. You
-may want to remove them yourself as they are unlikely to work properly.
-
-What's New in Python-Markdown 2.1
----------------------------------
-
-Three new extensions were added. [Attribute Lists](extensions/attr_list.html),
-which was inspired by Maruku's feature of the same name,
-[Newline to Break](extensions/nl2br.html), which was inspired by GitHub
-Flavored Markdown, and [Smart Strong](extensions/smart_strong.html), which
-fills a hole in the Extra extension.
-
-HTML5 is now supported. All this really means is that new block level elements
-introduced in the HTML5 spec are now properly recognized as raw HTML. As
-valid HTML5 can consist of either HTML4 or XHTML1, there is no need to add a
-new HTML5 serializers. That said, `html5` and `xhtml5` have been added as
-aliases of the `html4` and `xhtml1` serializers respectively.
-
-An XHTML serializer has been added. Previously, ElementTree's XML serializer
-was being used for XHTML output. With the new serializer we are able to avoid
-more invalid output like empty elements (i.e., `<p />`) which can choke
-browsers.
-
-Improved support for Python 3.x. Now when running `setupy.py install` in
-Python 3.1 or greater the 2to3 tool is run automatically. Note that Python 3.0
-is not supported due to a bug in its 2to3 tool. If you must use Python-Markdown
-with Python 3.0, it is suggested you manually use Python 3.1's 2to3 tool to
-do a conversion.
-
-Methods on instances of the Markdown class that do not return results can now
-be changed allowing one to do `md.reset().convert(moretext)`.
-
-The Markdown class was refactored so that a subclass could define it's own
-`build_parser` method which would build a completely different parser. In
-other words, one could use the basic machinery in the markdown library to
-build a parser of a different markup language without the overhead of building
-the markdown parser and throwing it away.
-
-Import statements within markdown have been improved so that third party
-libraries can embed the markdown library if they desire (licensing permitting).
-
-Added support for Python's `-m` command line option. You can run the markdown
-package as a command line script. Do `python -m markdown [options] [args]`.
-Note that this is only fully supported in Python 2.7+. Python 2.5 & 2.6
-require you to call the module directly (`markdown.__main__`) rather than
-the package (`markdown`). This does not work in Python 2.4.
-
-The command line script has been renamed to `markdown_py` which avoids all the
-various problems we had with previous names. Also improved the command line
-script to accept input on `stdin`.
-
-The testing framework has been completely rebuilt using the Nose testing
-framework. This provides a number of benefits including the ability to better
-test the built-in extensions and other options available to change the parsing
-behavior. See the [Test Suite](test_suite.html) documentation for details.
-
-Various bug fixes have been made, which are too numerous to list here. See the
-[commit log](https://github.com/waylan/Python-Markdown/commits/master) for a
-complete history of the changes.
+++ /dev/null
-title: Release Notes for v2.1.1
-prev_title: Release Notes for v2.2.0
-prev_url: release-2.2.0.html
-next_title: Release Notes for v2.1.0
-next_url: release-2.1.0.html
-
-Python-Markdown 2.1.1 Release Notes
-===================================
-
-Python-Markdown 2.1.1 is a bug-fix release. No new features have been added.
-Most notably, a bug which caused raw HTML paring to run __very__ slowly has
-been fixed along with a few bugs which caused the parser to raise exceptions
-while parsing. For a full list of changes, see the git log.
+++ /dev/null
-title: Release Notes for v2.2.0\r
-prev_title: Release Notes for v2.2.1\r
-prev_url: release-2.2.1.html\r
-next_title: Release Notes for v2.1.1\r
-next_url: release-2.1.1.html\r
-\r
-Python-Markdown 2.2.0 Release Notes\r
-===================================\r
-\r
-We are pleased to release Python-Markdown 2.2 which makes improvements on 2.1. \r
-While 2.2 is primarily a bug fix release, some internal improvements were made \r
-to the parser, and a few security issues were resolved.\r
-\r
-Python-Markdown supports Python versions 2.5, 2.6, 2.7, 3.1, and 3.2 out \r
-of the box. \r
-\r
-Backwards-incompatible Changes\r
-------------------------------\r
-\r
-While Python-Markdown has received only minor internal changes since the last\r
-release, there are a few backward-incompatible changes to note:\r
-\r
-* Support had been dropped for Python 2.4. No guarantees are made that the \r
-library will work in any version of Python lower than 2.5. Additionally, while \r
-the library had been tested with Python 2.5, consider Python 2.5 support to be \r
-depreciated. It is not likely that any future versions will continue to support\r
-any version of Python less than 2.6.\r
-\r
-* For many years Python-Markdown has identified `<ins>` and `<del>` tags in \r
-raw HTML input as block level tags. As they are actually inline level tags,\r
-this behavior has been changed. This may result in slightly different output.\r
-While in most cases, the new output is more correct, there may be a few edge\r
-cases where a document author has relied on the previous incorrect behavior.\r
-It is likely that a few adjustments may need to be made to those documents.\r
-\r
-* The behavior of the `enable_attributes` keyword has been slightly altered.\r
-If authors have been using attributes in documents with `safe_mode` on, those \r
-attributes will no longer be parsed unless `enable_attributes` is explicitly\r
-set to `True`. This change was made to prevent untrusted authors from injecting\r
-potentially harmful JavaScript in documents. This change had no effect when \r
-not in `safe_mode`.\r
-\r
-What's New in Python-Markdown 2.1\r
----------------------------------\r
-\r
-The docs were refactored and can now be found at \r
-<http://packages.python.org/Markdown/>. The docs are now maintained in the\r
-Repository and are generated by the `setup.py build_docs` command.\r
-\r
-The [Sane_Lists](http://packages.python.org/Markdown/extensions/sane_lists.html)\r
-extension was added. The Sane Lists Extension alters the behavior of the \r
-Markdown List syntax to be less surprising by not allowing the mixing of list\r
-types. In other words, an ordered list will not continue when an unordered list\r
-item is encountered and vice versa.\r
-\r
-Markdown now excepts a full path to an extension module. In other words, your\r
-extensions no longer need to be in the primary namespace (and start with `mdx_`)\r
-for Markdown to find them. Just do `Markdown(extension=['path.to.some.module'])`.\r
-As long as the provided module contains a compatible extension, the extension\r
-will be loaded.\r
-\r
-The BlockParser API was slightly altered to allow `blockprocessor.run` to return\r
-`True` or `False` which provides more control to the block processor loop from \r
-within any Blockprocessor instance.\r
-\r
-Various bug fixes have been made. See the \r
-[commit log](https://github.com/waylan/Python-Markdown/commits/master) \r
-for a complete history of the changes.\r
+++ /dev/null
-title: Release Notes for v2.2.1
-prev_title: Release Notes for v2.3
-prev_url: release-2.3.html
-next_title: Release Notes for v2.2.0
-next_url: release-2.2.0.html
-
-Python-Markdown 2.2.1 Release Notes
-===================================
-
-Python-Markdown 2.2.1 is a bug-fix release. No new features have been added.
-However, at least one bug fix does not work in Python 2.4 so that version of
-Python is no longer supported. For a full list of changes, see the git log.
+++ /dev/null
-title: Release Notes for v2.3
-prev_title: Release Notes for v2.4
-prev_url: release-2.4.html
-next_title: Release Notes for v2.2.1
-next_url: release-2.2.1.html
-
-Python-Markdown 2.3 Release Notes
-=================================
-
-We are pleased to release Python-Markdown 2.3 which adds one new extension,
-removes a few old (obsolete) extensions, and now runs on both Python 2 and
-Python 3 without running the 2to3 conversion tool. See the list of changes
-below for details.
-
-Python-Markdown supports Python versions 2.6, 2.7, 3.1, 3.2, and 3.3.
-
-Backwards-incompatible Changes
-------------------------------
-
-* Support has been dropped for Python 2.5. No guarantees are made that the
-library will work in any version of Python lower than 2.6. As all supported
-Python versions include the ElementTree library, Python-Markdown will no
-longer try to import a third-party installation of ElementTree.
-
-* All classes are now "new-style" classes. In other words, all classes
-subclass from 'object'. While this is not likely to affect most users,
-extension authors may need to make a few minor adjustments to their code.
-
-* "safe_mode" has been further restricted. Markdown formatted links must be
-of a known white-listed scheme when in "safe_mode" or the URL is discarded.
-The white-listed schemes are: 'HTTP', 'HTTPS', 'FTP', 'FTPS', 'MAILTO', and
-'news'. Schemeless URLs are also permitted, but are checked in other ways -
-as they have been for some time.
-
-* The ids assigned to footnotes now contain a dash (`-`) rather than a colon
-(`:`) when `output_format` it set to `"html5"` or `"xhtml5"`. If you are making
-reference to those ids in your JavaScript or CSS and using the HTML5 output,
-you will need to update your code accordingly. No changes are necessary if
-you are outputting XHTML (the default) or HTML4.
-
-* The `force_linenos` configuration setting of the CodeHilite extension has been
-marked as **Pending Deprecation** and a new setting `linenums` has been added to
-replace it. See documentation for the [CodeHilite Extension] for an explanation
-of the new `linenums` setting. The new setting will honor the old
-`force_linenos` if it is set, but it will raise a `PendingDeprecationWarning`
-and will likely be removed in a future version of Python-Markdown.
-
-[CodeHilite Extension]: extensions/codehilite.html
-
-* The "RSS" extension has been removed and no longer ships with Python-Markdown.
-If you would like to continue using the extension (not recommended), it is
-archived on [GitHub](https://gist.github.com/waylan/4773365).
-
-* The "HTML Tidy" Extension has been removed and no longer ships with Python-Markdown.
-If you would like to continue using the extension (not recommended), it is
-archived on [GitHub](https://gist.github.com/waylan/5152650). Note that the
-underlying library, uTidylib, is not Python 3 compatible. Instead, it is
-recommended that the newer [PyTidyLib] (version 0.2.2+ for Python 3
-comparability - install from GitHub not PyPI) be used. As the API for that
-library is rather simple, it is recommended that the output of Markdown be
-wrapped in a call to PyTidyLib rather than using an extension (for example:
-`tidylib.tidy_fragment(markdown.markdown(source), options={...})`).
-
-[PyTidyLib]: http://countergram.com/open-source/pytidylib
-
-What's New in Python-Markdown 2.3
----------------------------------
-
-* The entire code base now universally runs in Python 2 and Python 3 without
-any need for running the 2to3 conversion tool. This not only simplifies testing,
-but by using Unicode_literals, results in more consistent behavior across
-Python versions. Additionally, the relative imports (made possible in Python 2
-via absolute_import) allows the entire library to more easily be embedded in a
-sub-directory of another project. The various files within the library will
-still import each other properly even though 'markdown' may not be in Python's
-root namespace.
-
-* The [Admonition Extension] has been added, which implements [rST-style][rST]
-admonitions in the Markdown syntax. However, be warned that this extension
-is experimental and the syntax and behavior is still subject to change. Please
-try it out and report bugs and/or improvements.
-
-[Admonition Extension]: extensions/admonition.html
-[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
-
-* Various bug fixes have been made. See the
-[commit log](https://github.com/waylan/Python-Markdown/commits/master)
-for a complete history of the changes.
+++ /dev/null
-title: Release Notes for v2.4
-prev_title: Release Notes for v2.5
-prev_url: release-2.5.html
-next_title: Release Notes for v2.3
-next_url: release-2.3.html
-
-Python-Markdown 2.4 Release Notes
-=================================
-
-We are pleased to release Python-Markdown 2.4 which adds one new extension
-and fixes various bugs. See the list of changes below for details.
-
-Python-Markdown supports Python versions 2.6, 2.7, 3.1, 3.2, and 3.3.
-
-Backwards-incompatible Changes
-------------------------------
-
-* The `force_linenos` configuration setting of the CodeHilite extension has been
-marked as **Deprecated**. It had previously been marked as "Pending Deprecation"
-in version 2.3 when a new setting `linenums` was added to replace it. See
-documentation for the [CodeHilite Extension] for an explanation of the new
-`linenums` setting. The new setting will honor the old `force_linenos` if it
-is set, but `force_linenos` will raise a `DeprecationWarning` and will likely
-be removed in a future version of Python-Markdown.
-
-[CodeHilite Extension]: extensions/code_hilite.html
-
-* URLs are no longer percent-encoded. This improves compatibility with the
-original (written in Perl) Markdown implementation. Please percent-encode
-your URLs manually when needed.
-
-What's New in Python-Markdown 2.4
----------------------------------
-
-* Thanks to the hard work of [Dmitry Shachnev] the [Smarty Extension] has been
-added, which implements [SmartyPants] using Python-Markdown's Extension API.
-This offers a few benefits over a third party script. The HTML does not need
-to be "tokenized" twice, no hacks are required to combine SmartyPants and
-code highlighting, and we get markdown's escaping feature for free. Please try
-it out and report bugs and/or improvements.
-
-[Dmitry Shachnev]: https://github.com/mitya57
-[Smarty Extension]: extensions/smarty.html
-[SmartyPants]: http://daringfireball.net/projects/smartypants/
-
-* The [Table of Contents Extension] now supports new `permalink` option
-for creating [Sphinx]-style anchor links.
-
-[Table of Contents Extension]: extensions/toc.html
-[Sphinx]: http://sphinx-doc.org/
-
-* It is now possible to enable Markdown formatting inside HTML blocks by
-appending `markdown=1` to opening tag attributes. See [Markdown Inside HTML
-Blocks] section for details. Thanks to [ryneeverett] for implementing this
-feature.
-
-[Markdown Inside HTML Blocks]: extensions/extra.html#nested-markdown-inside-html-blocks
-[ryneeverett]: https://github.com/ryneeverett
-
-* The code blocks now support emphasizing some of the code lines. To use this
-feature, specify `hl_lines` option after language name, for example (using
-the [Fenced Code Extension]):
-
- ```.python hl_lines="1 3"
- # This line will be emphasized.
- # This one won't.
- # This one will be also emphasized.
- ```
-
- Thanks to [A. Jesse Jiryu Davis] for implementing this feature.
-
-[Fenced Code Extension]: extensions/fenced_code_blocks.html
-[A. Jesse Jiryu Davis]: https://github.com/ajdavis
-
-* Various bug fixes have been made. See the
-[commit log](https://github.com/waylan/Python-Markdown/commits/master)
-for a complete history of the changes.
+++ /dev/null
-title: Release Notes for v2.5
-prev_title: Release Notes for v2.6
-prev_url: release-2.6.html
-next_title: Release Notes for v2.4
-next_url: release-2.4.html
-
-Python-Markdown 2.5 Release Notes
-=================================
-
-We are pleased to release Python-Markdown 2.5 which adds a few new features
-and fixes various bugs. See the list of changes below for details.
-
-Python-Markdown version 2.5 supports Python versions 2.7, 3.2, 3.3, and 3.4.
-
-Backwards-incompatible Changes
-------------------------------
-
-* Python-Markdown no longer supports Python version 2.6. You must be using Python
- versions 2.7, 3.2, 3.3, or 3.4.
-
-[importlib]: https://pypi.python.org/pypi/importlib
-
-* The `force_linenos` configuration key on the [CodeHilite Extension] has been **deprecated**
- and will raise a `KeyError` if provided. In the previous release (2.4), it was
- issuing a `DeprecationWarning`. The [`linenums`][linenums] keyword should be used
- instead, which provides more control of the output.
-
-[CodeHilite Extension]: extensions/code_hilite.html
-[linenums]: extensions/code_hilite.html#usage
-
-* Both `safe_mode` and the associated `html_replacement_text` keywords will be deprecated
- in version 2.6 and will raise a **`PendingDeprecationWarning`** in 2.5. The so-called
- "safe mode" was never actually "safe" which has resulted in many people having a false
- sense of security when using it. As an alternative, the developers of Python-Markdown
- recommend that any untrusted content be passed through an HTML sanitizer (like [Bleach])
- after being converted to HTML by markdown.
-
- If your code previously looked like this:
-
- html = markdown.markdown(text, same_mode=True)
-
- Then it is recommended that you change your code to read something like this:
-
- import bleach
- html = bleach.clean(markdown.markdown(text))
-
- If you are not interested in sanitizing untrusted text, but simply desire to escape
- raw HTML, then that can be accomplished through an extension which removes HTML parsing:
-
- from markdown.extensions import Extension
-
- class EscapeHtml(Extension):
- def extendMarkdown(self, md, md_globals):
- del md.preprocessors['html_block']
- del md.inlinePatterns['html']
-
- html = markdown.markdown(text, extensions=[EscapeHtml()])
-
- As the HTML would not be parsed with the above Extension, then the serializer will
- escape the raw HTML, which is exactly what happens now when `safe_mode="escape"`.
-
-[Bleach]: http://bleach.readthedocs.org/
-
-* Positional arguments on the `markdown.Markdown()` are pending deprecation as are
- all except the `text` argument on the `markdown.markdown()` wrapper function.
- Only keyword arguments should be used. For example, if your code previously
- looked like this:
-
- html = markdown.markdown(text, ['extra'])
-
- Then it is recommended that you change it to read something like this:
-
- html = markdown.markdown(text, extensions=['extra'])
-
- !!! Note
- This change is being made as a result of deprecating `"safe_mode"` as the
- `safe_mode` argument was one of the positional arguments. When that argument
- is removed, the two arguments following it will no longer be at the correct
- position. It is recommended that you always use keywords when they are supported
- for this reason.
-
-* In previous versions of Python-Markdown, the built-in extensions received
- special status and did not require the full path to be provided. Additionally,
- 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 explanation
- of the current behavior.
-
-* The previously documented method of appending the extension configuration 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 explanation of the current behavior.
-
-What's New in Python-Markdown 2.5
----------------------------------
-
-* The [Smarty Extension] has had a number of additional configuration settings
- added, which allows one to define their own substitutions to better support
- languages other than English. Thanks to [Martin Altmayer] for implementing this
- feature.
-
-[Smarty Extension]: extensions/smarty.html
-[Martin Altmayer]:https://github.com/MartinAltmayer
-
-* Named Extensions (strings passed to the [`extensions`][ex] keyword of
- `markdown.Markdown`) can now point to any module and/or Class on your PYTHONPATH.
- While dot notation was previously supported, a module could not be at the root of
- your PYTHONPATH. The name had to contain at least one dot (requiring it to be a
- sub-module). This restriction no longer exists.
-
- Additionally, a Class may be specified in the name. The class must be at the end of
- the name (which uses dot notation from PYTHONPATH) and be separated by a colon from
- the module.
-
- 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"
-
- This allows multiple extensions to be implemented within the same module and still
- accessible when the user is not able to import the extension directly (perhaps from
- a template filter or the command line).
-
- This also means that extension modules are no longer required to include the
- `makeExtension` function which returns an instance of the extension class. However,
- if the user does not specify the class name (she only provides `"path.to.module"`)
- the extension will fail to load without the `makeExtension` function included in
- the module. Extension authors will want to document carefully what is required to
- load their extensions.
-
-[ex]: reference.html#extensions
-
-* The Extension Configuration code has been refactored to make it a little easier
- for extension authors to work with configuration settings. As a result, the
- [`extension_configs`][ec] keyword now accepts a dictionary rather than requiring
- a list of tuples. A list of tuples is still supported so no one needs to change
- their existing code. This should also simplify the learning curve for new users.
-
- Extension authors are encouraged to review the new methods available on the
- `markdown.extnesions.Extension` class for handling configuration and adjust their
- code going forward. The included extensions provide a model for best practices.
- See the [API] documentation for a full explanation.
-
-[ec]: reference.html#extension_configs
-[API]: extensions/api.html#configsettings
-
-* The [Command Line Interface][cli] now accepts a `--extensions_config` (or `-c`)
- option which accepts a file name and passes the parsed content of a [YAML] or
- [JSON] file to the [`extension_configs`][ec] keyword of the `markdown.Markdown`
- class. The contents of the YAML or JSON must map to a Python Dictionary which
- matches the format required by the `extension_configs` keyword. Note that
- [PyYAML] is required to parse YAML files.
-
-[cli]: cli.html#using-extensions
-[YAML]: http://yaml.org/
-[JSON]: http://json.org/
-[PyYAML]: http://pyyaml.org/
-
-* The [admonition extension][ae] is no longer considered "experimental."
-
-[ae]: extensions/admonition.html
-
-* There have been various refactors of the testing framework. While those changes
- will not directly effect end users, the code is being better tested which will
- benefit everyone.
-
-* Various bug fixes have been made. See the
- [commit log](https://github.com/waylan/Python-Markdown/commits/master)
- for a complete history of the changes.
+++ /dev/null
-title: Release Notes for v2.6
-prev_title: Change Log
-prev_url: change_log.html
-next_title: Release Notes for v2.5
-next_url: release-2.5.html
-
-Python-Markdown 2.6 Release Notes
-=================================
-
-We are pleased to release Python-Markdown 2.6 which adds a few new features
-and fixes various bugs. See the list of changes below for details.
-
-Python-Markdown version 2.6 supports Python versions 2.7, 3.2, 3.3, and 3.4 as well as PyPy.
-
-Backwards-incompatible Changes
-------------------------------
-
-### `safe_mode` Deprecated
-
-Both `safe_mode` and the associated `html_replacement_text` keywords are deprecated
-in version 2.6 and will raise a **`DeprecationWarning`**. The `safe_mode` and
-`html_replacement_text` keywords will be ignored in version 2.7. The so-called
-"safe mode" was never actually "safe" which has resulted in many people having a false
-sense of security when using it. As an alternative, the developers of Python-Markdown
-recommend that any untrusted content be passed through an HTML sanitizer (like [Bleach])
-after being converted to HTML by markdown.
-
-If your code previously looked like this:
-
- html = markdown.markdown(text, safe_mode=True)
-
-Then it is recommended that you change your code to read something like this:
-
- import bleach
- html = bleach.clean(markdown.markdown(text))
-
-If you are not interested in sanitizing untrusted text, but simply desire to escape
-raw HTML, then that can be accomplished through an extension which removes HTML parsing:
-
- from markdown.extensions import Extension
-
- class EscapeHtml(Extension):
- def extendMarkdown(self, md, md_globals):
- del md.preprocessors['html_block']
- del md.inlinePatterns['html']
-
- html = markdown.markdown(text, extensions=[EscapeHtml()])
-
-As the HTML would not be parsed with the above Extension, then the serializer will
-escape the raw HTML, which is exactly what happens now when `safe_mode="escape"`.
-
-[Bleach]: http://bleach.readthedocs.org/
-
-### Positional Arguments Deprecated
-
-Positional arguments on the `markdown.Markdown()` class are deprecated as are
-all except the `text` argument on the `markdown.markdown()` wrapper function.
-Using positional arguments will raise a **`DeprecationWarning`** in 2.6 and an error
-in version 2.7. Only keyword arguments should be used. For example, if your code
-previously looked like this:
-
- html = markdown.markdown(text, [SomeExtension()])
-
-Then it is recommended that you change it to read something like this:
-
- html = markdown.markdown(text, extensions=[SomeExtension()])
-
-!!! Note
- This change is being made as a result of deprecating `"safe_mode"` as the
- `safe_mode` argument was one of the positional arguments. When that argument
- is removed, the two arguments following it will no longer be at the correct
- position. It is recommended that you always use keywords when they are supported
- for this reason.
-
-### "Shortened" Extension Names Deprecated
-
-In previous versions of Python-Markdown, the built-in extensions received
-special status and did not require the full path to be provided. Additionally,
-third party extensions whose name started with `"mdx_"` received the same
-special treatment. This behavior is deprecated and will raise a
-**`DeprecationWarning`** in version 2.6 and an error in 2.7. 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
-
-Similarly, if you have used a third party extension (for example `mdx_math`), previously
-you might have called it like this:
-
- markdown.markdown(text, extensions=['math'])
-
-As the `"mdx"` prefix will no longer be appended, you will need to change your code
-as follows (assuming the file `mdx_math.py` is installed at the root of your PYTHONPATH):
-
- markdown.markdown(text, extensions=['mdx_math'])
-
-Extension authors will want to update their documentation to reflect the new behavior.
-
-See the [documentation](reference.html#extensions) for a full explanation
-of the current behavior.
-
-### Extension Configuration as Part of Extension Name Deprecated
-
-The previously documented method of appending the extension configuration options as
-a string to the extension name is deprecated and will raise a
-**`DeprecationWarning`** in version 2.6 and an error in 2.7.
-The [`extension_configs`](reference.html#extension_configs) keyword should
-be used instead. See the [documentation](reference.html#extension-configs)
-for a full explanation of the current behavior.
-
-### HeaderId Extension Pending Deprecation
-
-The [HeaderId][hid] Extension is pending deprecation and will raise a
-**`PendingDeprecationWarning`** in version 2.6. The extension will be
-deprecated in version 2.7 and raise an error in version 2.8. Use the
-[Table of Contents][TOC] Extension instead, which offers most of the
-features of the HeaderId Extension and more (support for meta data is missing).
-
-Extension authors who have been using the `slugify` and `unique` functions
-defined in the HeaderId Extension should note that those functions are now
-defined in the Table of Contents extension and should adjust their import
-statements accordingly (`from markdown.extensions.toc import slugify, unique`).
-
-[hid]: extensions/header_id.html
-
-### The `configs` Keyword is Deprecated
-
-Positional arguments and the `configs` keyword on the `markdown.extension.Extension` class
-(and its subclasses) are deprecated. Each individual configuration option should be passed
-to the class as a keyword/value pair. For example. one might have previously initiated
-an extension subclass like this:
-
- ext = SomeExtension(configs={'somekey': 'somevalue'})
-
-That code should be updated to pass in the options directly:
-
- ext = SomeExtension(somekey='somevalue')
-
-Extension authors will want to note that this affects the `makeExtension` function as well.
-Previously it was common for the function to be defined as follows:
-
- def makeExtension(configs=None):
- return SomeExtension(configs=configs)
-
-Extension authors will want to update their code to the following instead:
-
- def makeExtension(**kwargs):
- return SomeExtension(**kwargs)
-
-Failing to do so will result in a **`DeprecationWarning`** and will raise an error in the next
-release. See the [Extension API][mext] documentation for more information.
-
-In the event that an `markdown.extension.Extension` subclass overrides the `__init__` method
-and implements its own configuration handling, then the above may not apply. However, it is
-recommended that the subclass still calls the parent `__init__` method to handle configuration
-options like so:
-
- class SomeExtension(markdown.extension.Extension):
- def __init__(**kwargs):
- # Do pre-config stuff here
- # Set config defaults
- self.config = {
- 'option1' : ['value1', 'description1'],
- 'option2' : ['value2', 'description2']
- }
- # Set user defined configs
- super(MyExtension, self).__init__(**kwargs)
- # Do post-config stuff here
-
-Note the call to `super` to get the benefits of configuration handling from the parent class.
-See the [documentation][config] for more information.
-
-[config]: extensions/api.html#configsettings
-[mext]: extensions/api.html#makeextension
-
-What's New in Python-Markdown 2.6
----------------------------------
-
-### Official Support for PyPy
-
-Official support for [PyPy] has been added. While Python-Markdown has most likely
-worked on PyPy for some time, it is now officially supported and tested on PyPy.
-
-[PyPy]: http://pypy.org/
-
-### YAML Style Meta-Data
-
-The [Meta-Data] Extension now includes optional support for [YAML] style
-meta-data. By default, the YAML deliminators are recognized, however, the
-actual data is parsed as previously. This follows the syntax of
-[MultiMarkdown], which inspired this extension.
-
-Alternatively, if the `yaml` option is set, then the data is parsed as YAML.
-
-[MultiMarkdown]: http://fletcherpenney.net/MultiMarkdown_Syntax_Guide#metadata
-[Meta-Data]: extensions/meta_data.html
-[YAML]: http://yaml.org/
-
-### Table of Contents Extension Refactored
-
-The [Table of Contents][TOC] Extension has been refactored and some new features
-have been added. See the documentation for a full explanation of each feature
-listed below:
-
-* The extension now assigns the Table of Contents to the `toc` attribute of
- the Markdown class regardless of whether a "marker" was found in the document.
- Third party frameworks no longer need to insert a "marker," run the document
- through Markdown, then extract the Table of Contents from the document.
-
-* The Table of Contents Extension is now a "registered extension." Therefore, when the `reset`
- method of the Markdown class is called, the `toc` attribute on the Markdown
- class is cleared (set to an empty string).
-
-* When the `marker` configuration option is set to an empty string, the parser completely
- skips the process of searching the document for markers. This should save parsing
- time when the Table of Contents Extension is being used only to assign ids to headers.
-
-* A `separator` configuration option has been added allowing users to override the
- separator character used by the slugify function.
-
-* A `baselevel` configuration option has been added allowing users to set the base level
- of headers in their documents (h1-h6). This allows the header levels to be
- automatically adjusted to fit within the hierarchy of an HTML template.
-
-[TOC]: extensions/toc.html
-
-### Pygments can now be disabled
-
-The [CodeHilite][ch] Extension has gained a new configuration option: `use_pygments`.
-The option is `True` by default, however, it allows one to turn off Pygments code
-highlighting (set to `False`) while preserving the language detection features of
-the extension. Note that Pygments language guessing is not used as that would 'use
-Pygments'. If a language is defined for a code block, it will be assigned to the
-`<code>` tag as a class in the manner suggested by the [HTML5 spec][spec]
-(alternate output will not be entertained) and could potentially be used by a JavaScript
-library in the browser to highlight the code block.
-
-[ch]: extensions/code_hilite.html
-[spec]: http://www.w3.org/TR/html5/text-level-semantics.html#the-code-element
-
-### Miscellaneous
-
-Test coverage has been improved including running [flake8]. While those changes
-will not directly effect end users, the code is being better tested which will
-benefit everyone.
-
-[flake8]: http://flake8.readthedocs.org/en/latest/
-
-Various bug fixes have been made. See the
-[commit log](https://github.com/waylan/Python-Markdown/commits/master)
-for a complete history of the changes.
+++ /dev/null
-title: Table of Contents
-prev_title: Authors
-prev_url: authors.html
-next_title: Summary
-next_url: index.html
-
-Table of Contents
-=================
-
-* [Python Markdown](index.html)
- * [Goals](index.html#goals)
- * [Features](index.html#features)
- * [Differences](index.html#differences)
- * [Support](index.html#support)
-* [Installation](install.html)
- * [The Easy Way](install.html#the-easy-way)
- * [Installing on Windows](install.html#windows)
- * [Installing on *nix Systems](install.html#linux)
- * [Using the Git Repository](install.html#git)
- * [Dependencies](install.html#dependencies)
-* [Library Reference](reference.html)
- * [The Basics](reference.html#the-basics)
- * [The Details](reference.html#the-details)
- * [`markdown.markdown`](reference.html#markdown)
- * [`markdown.markdownFromFile`](reference.html#markdownFromFile)
- * [`markdown.Markdown`](reference.html#Markdown)
-* [Command Line](cli.html)
- * [Setup](cli.html#setup)
- * [Usage](cli.html#usage)
- * [Using Extensions](cli.html#using-extensions)
-* [Extensions](extensions/index.html)
- * [Officially Supported Extensions](extensions/index.html#officially-supported-extensions)
- * [Extra](extensions/extra.html)
- * [Abbreviations](extensions/abbreviations.html)
- * [Attribute Lists](extensions/attr_list.html)
- * [Definition Lists](extensions/definition_lists.html)
- * [Fenced Code Blocks](extensions/fenced_code_blocks.html)
- * [Footnotes](extensions/footnotes.html)
- * [Tables](extensions/tables.html)
- * [Smart Strong](extensions/smart_strong.html)
- * [Admonition](extensions/admonition.html)
- * [CodeHilite](extensions/code_hilite.html)
- * [HeaderId](extensions/header_id.html)
- * [Meta-Data](extensions/meta_data.html)
- * [New Line to Break](extensions/nl2br.html)
- * [Sane Lists](extensions/sane_lists.html)
- * [SmartyPants](extensions/smarty.html)
- * [Table of Contents](extensions/toc.html)
- * [WikiLinks](extensions/wikilinks.html)
- * [Third Party Extensions](extensions/index.html#third-party-extensions)
- * [Extension API](extensions/api.html)
- * [Preprocessors](extensions/api.html#preprocessors)
- * [Inline Patterns](extensions/api.html#inlinepatterns)
- * [Treeprocessors](extensions/api.html#treeprocessors)
- * [Postprocessors](extensions/api.html#postprocessors)
- * [BlockParser](extensions/api.html#blockparser)
- * [Working with the ElementTree](extensions/api.html#working_with_et)
- * [Integrating your code into Markdown](extensions/api.html#integrating_into_markdown)
- * [extendMarkdown](extensions/api.html#extendmarkdown)
- * [OrderedDict](extensions/api.html#ordereddict)
- * [registerExtension](extensions/api.html#registerextension)
- * [Configuration Settings](extensions/api.html#configsettings)
- * [makeExtension](extensions/api.html#makeextension)
-* [Test Suite](test_suite.html)
- * [Markdown Syntax Test](test_suite.html#markdown-syntax-tests)
- * [Syntax Test Configuration Settings](test_suite.html#syntax-test-config-settings)
- * [Unit Tests](test_suite.html#unit-tests)
-* [Change Log](change_log.html)
- * [Release Notes for v.2.6](release-2.6.html)
- * [Release Notes for v.2.5](release-2.5.html)
- * [Release Notes for v.2.4](release-2.4.html)
- * [Release Notes for v.2.3](release-2.3.html)
- * [Release Notes for v.2.2.1](release-2.1.1.html)
- * [Release Notes for v.2.2.0](release-2.1.0.html)
- * [Release Notes for v.2.0.2](release-2.0.2.html)
- * [Release Notes for v.2.0.1](release-2.0.1.html)
- * [Release Notes for v.2.0](release-2.0.html)
-* [Authors](authors.html)
+++ /dev/null
-title: Test Suite
-prev_title: Extension API
-prev_url: extensions/api.html
-next_title: Change Log
-next_url: change_log.html
-
-# Test Suite
-
-Python-Markdown comes with a test suite which uses the [Nose] testing
-framework and [YAML]. The test suite primarily serves to ensure that new bugs
-are not introduced as existing bugs are patched or new features are added. It
-also allows Python-Markdown to be tested with the tests from other
-implementations such as John Gruber's [Perl] implementation or Michel
-Fortin's [PHP] implementation.
-
-The test suite can be run by calling the `run_tests.py` command at the root of
-the distribution tarball or by calling the `nosetests` command directly. Either
-way, Nose will need to be installed on your system first (run `easy_install
-nose`). Any standard nosetests configuration options can be passed in on the command
-line (i.e.: verbosity level or use of a plugin like coverage).
-
-Additionally, a nicely formatted HTML report of all output is written to a
-temporary file in `test-output.html`. Open the file in a browser to view
-the report.
-
-A `tox.ini` file is also provided, so [tox] can be used to automatically create
-virtual environments, install all testing dependencies and run the tests on
-each supported Python version. See the wiki for instructions on
-[setting up a testing environment] to use tox.
-
-The test suite contains two kinds of tests: Markdown Syntax Tests and Unit
-Tests.
-
-## Markdown Syntax Tests
-
-The Syntax Tests are in the various directories contained within the 'tests'
-directory of the packaged tarball. Each test consists of a matching pair of text
-and HTML files. The text file contains a snippet of Markdown source text
-formatted for a specific syntax feature and the HTML file contains the expected
-HTML output of that snippet. When the test suite is run, each text file is run
-through Markdown and the output is compared with the HTML file as a separate
-Unit Test.
-
-In fact, this is the primary reason for using Nose, it gives us an easy way to
-treat each of these tests as a separate unit test which is reported on
-separately. Additionally, with the help of a couple custom Nose plugins which
-are included with the Markdown Test Suite, we are able to get back an easy to
-read diff of the actual output compared to expected output when a test fails.
-
-Here is some sample output with a test that is failing because of some
-insignificant white space differences:
-
- $ ./run-tests.py
- ..........................................................M...........
- ............................SSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSSS
- SSSSSSSSSS.................S..........................................
- .........
- ======================================================================
- MarkdownSyntaxError: TestSyntax: "misc/lists3"
- ----------------------------------------------------------------------
- MarkdownSyntaxError: Output from "/home/waylan/code/python-markdown/te
- sts/misc/lists3.txt" failed to match expected output.
-
- --- /home/waylan/code/python-markdown/tests/misc/lists3.html
- +++ actual_output.html
- @@ -1,5 +1,5 @@
- <ul>
- <li>blah blah blah
- -sdf asdf asdf asdf asdf
- -asda asdf asdfasd</li>
- + sdf asdf asdf asdf asdf
- + asda asdf asdfasd</li>
- </ul>
-
- ----------------------------------------------------------------------
- Ran 219 tests in 7.698s
-
- FAILED (MarkdownSyntaxError=1, SKIP=53)
-
-Note that 219 tests were run, one of which failed with a `MarkdownSyntaxError`.
-Only Markdown Syntax Tests should fail with a `MarkdownSyntaxError`. Nose then
-formats the error reports for `MarkdownSyntaxError`s so that they only include
-useful information. Namely the text file which failed and a unified diff showing
-the failure. Without the plugin, you would also get a useless traceback showing
-how the code stepped through the test framework, but nothing about how Markdown
-actually ran.
-
-If, on the other hand, a Syntax Test failed because some other exception gets
-raised by either Markdown or the test suite, then that would be reported as per
-a normal unit test failure with the appropriate traceback for debugging
-purposes.
-
-### Syntax Test Configuration Settings
-
-The other thing to note about the above example is that 53 tests were skipped.
-Those tests have been explicitly configured to be skipped as they are primarily
-tests from either PHP or Perl which are known to fail for various reasons. In
-fact, a number of different configuration settings can be set for any specific
-test.
-
-Each Syntax Test directory contains a `test.cfg` file in the [YAML] format. The
-file may contain a separate section for each text file named exactly as the file
-is named minus the file extension (i.e.; the section for a test in `foo.txt`
-would be `foo`). All settings are optional. Default settings for the entire
-directory can be set under the `DEFAULT` section (must be all caps). Any
-settings under a specific file section will override anything in the
-`DEFAULT` section for that specific test only.
-
-Below are the configuration options available and the defaults used when they
-are not explicitly set.
-
-* `normalize`: Switches white space normalization of the test output on or off.
- Defaults to `False` (off). Note: This requires that [PyTidyLib] be installed on
- the system. Otherwise the test will be skipped, regardless of any other
- settings.
-* `skip`: Switches skipping of the test on and off. Defaults to `False` (off).
-* `input_ext`: Extension of input file. Defaults to `.txt`. Useful for tests
- from other implementations.
-* `output_ext`: Extension of output file. Defaults to `.html`. Useful for tests
- from other implementations.
-* Any keyword argument accepted by the Markdown class. If not set, Markdown's
- defaults are used.
-
-## Unit Tests
-
-Unit Tests are used as regression tests for Python-Markdown's API.
-All Unit Tests shipped with Python-Markdown are standard Python Unit Tests and
-are all contained in `tests/test_apis.py` and `tests/test_extensions.py`.
-Standard discovery methods are used to find and run the tests. Therefore, when
-writing new tests, those standards and naming conventions should be followed.
-
-[Nose]: http://somethingaboutorange.com/mrl/projects/nose/
-[Perl]: http://daringfireball.net/projects/markdown/
-[PHP]: http://michelf.com/projects/php-markdown/
-[PyTidyLib]: http://countergram.com/open-source/pytidylib/
-[tox]: http://testrun.org/tox/latest/
-[setting up a testing environment]: https://github.com/waylan/Python-Markdown/wiki/Test-Environment-Setup
-[YAML]: http://yaml.org/
--- /dev/null
+title: Test Tools
+
+# Test Tools
+
+Python-Markdown provides some testing tools which simplify testing actual
+Markdown output again expected output. The tools are built on the Python
+standard library [`unittest`][unittest]. Therefore, no additional libraries are
+required. While Python-Markdown uses the tools for its own tests, they were
+designed and built so that third party extensions could use them as well.
+Therefore, the tools are importable from `markdown.test_tools`.
+
+The test tools include two different `unittest.TestCase` subclasses:
+`markdown.test_tools.TestCase` and `markdown.test_tools.LegacyTestCase`.
+
+## markdown.test_tools.TestCase
+
+The `markdown.test_tools.TestCase` class is a `unittest.TestCase` subclass with
+a few additional helpers to make testing Markdown output easier.
+
+Properties
+: `default_kwargs`: A `dict` of keywords to pass to Markdown for each
+test. The defaults can be overridden on individual tests.
+
+Methods
+: `assertMarkdownRenders`: accepts the source text, the expected output,
+ and any keywords to pass to Markdown. The `default_kwargs` defined on the
+ class are used except where overridden by keyword arguments. The output and
+ expected output are passed to `TestCase.assertMultiLineEqual`. An
+ `AssertionError` is raised with a diff if the actual output does not equal the
+ expected output.
+
+: `dedent`: Dedent triple-quoted strings.
+
+In all other respects, `markdown.test_tools.TestCase` behaves as
+`unittest.TestCase`. In fact, `assertMarkdownRenders` tests could be mixed with
+other `unittest` style tests within the same test class.
+
+An example Markdown test might look like this:
+
+```python
+from markdown.test_tools import TestCase
+
+class TestHr(TestCase):
+ def test_hr_before_paragraph(self):
+ self.assertMarkdownRenders(
+ # The Markdown source text used as input
+ self.dedent(
+ """
+ ***
+ An HR followed by a paragraph with no blank line.
+ """
+ ),
+ # The expected HTML output
+ self.dedent(
+ """
+ <hr>
+ <p>An HR followed by a paragraph with no blank line.</p>
+ """
+ ),
+ # Other keyword arguments to pass to `markdown.markdown`
+ output_format='html'
+ )
+```
+
+## markdown.test_tools.LegacyTestCase
+
+In the past Python-Markdown exclusively used file-based tests. Many of those
+tests still exist in Python-Markdown's test suite, including the test files from
+the [reference implementation][perl] (`markdown.pl`) and [PHP Markdown][PHP].
+Each test consists of a matching pair of text and HTML files. The text file
+contains a snippet of Markdown source text formatted for a specific syntax
+feature and the HTML file contains the expected HTML output of that snippet.
+When the test suite is run, each text file is run through Markdown and the
+output is compared with the HTML file as a separate unit test. When a test
+fails, the error report includes a diff of the expected output compared to the
+actual output to easily identify any problems.
+
+A separate `markdown.test_tools.LegacyTestCase` subclass must be created for
+each directory of test files. Various properties can be defined within the
+subclass to point to a directory of text-based test files and define various
+behaviors/defaults for those tests. The following properties are supported:
+
+* `location`: A path to the directory of test files. An absolute path is
+ preferred.
+* `exclude`: A list of tests to skip. Each test name should comprise of a
+ file name without an extension.
+* `normalize`: A boolean value indicating if the HTML should be normalized.
+ Default: `False`. Note: Normalization of HTML requires that [PyTidyLib] be
+ installed on the system. If PyTidyLib is not installed and `normalize` is set
+ to `True`, then the test will be skipped, regardless of any other settings.
+* `input_ext`: A string containing the file extension of input files.
+ Default: `.txt`.
+* `output_ext`: A string containing the file extension of expected output files.
+ Default: `html`.
+* `default_kwargs`: A `markdown.test_tools.Kwargs` instance which stores the
+ default set of keyword arguments for all test files in the directory.
+
+In addition, properties can be defined for each individual set of test files
+within the directory. The property should be given the name of the file without
+the file extension. Any spaces and dashes in the file name should be replaced
+with underscores. The value of the property should be a
+`markdown.test_tools.Kwargs` instance which contains the keyword arguments that
+should be passed to `markdown.markdown` for that test file. The keyword
+arguments will "update" the `default_kwargs`.
+
+When the class instance is created during a test run, it will walk the given
+directory and create a separate unit test for each set of test files using the
+naming scheme: `test_filename`. One unit test will be run for each set of input
+and output files.
+
+The definition of an example set of tests might look like this:
+
+```python
+from markdown.test_tools import LegacyTestCase, Kwargs
+import os
+
+# Get location of this file and use to find text file dirs.
+parent_test_dir = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestFoo(LegacyTestCase):
+ # Define location of text file directory. In this case, the directory is
+ # named "foo" and is in the same parent directory as this file.
+ location = os.path.join(parent_test_dir, 'foo')
+ # Define default keyword arguments. In this case, unless specified
+ # differently, all tests should use the output format "html".
+ default_kwargs = Kwargs(output_format='html')
+
+ # The "xhtml" test should override the output format and use "xhtml".
+ xhtml = Kwargs(output_format='xhtml')
+
+ # The "toc" test should use the "toc" extension with a custom permalink
+ # setting.
+ toc = Kwargs(
+ extensions=['markdown.extensions.toc'],
+ extension_configs={'markdown.extensions.toc': {'permalink': "[link]"}}
+ )
+```
+
+Note that in the above example, the text file directory may contain many more
+text-based test files than `xhtml` (`xhtml.txt` and `xhtml.html`) and `toc`
+(`toc.txt` and `toc.html`). As long as each set of files exists as a pair, a
+test will be created and run for each of them. Only the `xhtml` and `toc` tests
+needed to be specifically identified as they had specific, non-default settings
+which needed to be defined.
+
+## Running Python-Markdown's Tests
+
+As all of the tests for the `markdown` library are unit tests, standard
+`unittest` methods of calling tests can be used. For example, to run all of
+Python-Markdown's tests, from the root of the git repository, run the following
+command:
+
+```sh
+python -m unittest discover tests
+```
+
+That simple command will search everything in the `tests` directory and it's
+sub-directories and run all `unittest` tests that it finds, including
+`unittest.TestCase`, `markdown.test_tools.TestCase`, and
+`markdown.test_tools.LegacyTestCase` subclasses. Normal `unittest` discovery
+rules apply.
+
+Python-Markdown's git repository also includes a `tox.ini` file, so [tox] can be
+used to automate the creation of virtual environments, installation of all
+testing dependencies and running of the tests on each supported Python version.
+See the wiki for instructions on [setting up a testing environment] to use tox.
+
+[unittest]: https://docs.python.org/3/library/unittest.html
+[Perl]: http://daringfireball.net/projects/markdown/
+[PHP]: http://michelf.com/projects/php-markdown/
+[PyTidyLib]: http://countergram.com/open-source/pytidylib/
+[tox]: http://testrun.org/tox/latest/
+[setting up a testing environment]: https://github.com/Python-Markdown/markdown/wiki/Test-Environment-Setup
@echo ' build-win Build a Windows exe distribution'
@echo ' docs Build documentation'
@echo ' test Run all tests'
- @echo ' update-tests Generate html files for updated text files in tests'
@echo ' clean Clean up the source directories'
.PHONY : install
.PHONY : deploy
deploy:
- python setup.py register
- python setup.py sdist --manifest-only
- python setup.py sdist --formats zip,gztar upload
+ rm -rf dist
+ python setup.py bdist_wheel sdist --formats gztar
+ twine upload dist/*
.PHONY : build
build:
- python setup.py sdist --manifest-only
- python setup.py sdist --formats zip,gztar
+ python setup.py bdist_wheel sdist --formats gztar
.PHONY : build-win
build-win:
- python setup.py sdist --manifest-only
python setup.py bdist_wininst
.PHONY : docs
docs:
- python setup.py build_docs --force
- cd build/docs && zip -r ../docs.zip .
+ mkdocs build --clean
.PHONY : test
test:
- tox
-
-.PHONY : update-tests
-update-tests:
- python run-tests.py update
+ coverage run --source=markdown -m unittest discover tests
+ coverage report --show-missing
.PHONY : clean
clean:
rm -rf build
rm -rf dist
rm -rf tmp
+ rm -rf site
# git clean -dfx'
+# -*- coding: utf-8 -*-
"""
Python Markdown
-===============
-Python Markdown converts Markdown to HTML and can be used as a library or
-called from the command line.
+A Python implementation of John Gruber's Markdown.
-## Basic usage as a module:
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
- import markdown
- html = markdown.markdown(your_text_string)
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
-See <https://pythonhosted.org/Markdown/> for more
-information and instructions on how to extend the functionality of
-Python Markdown. Read that before you try modifying this file.
-
-## Authors and License
-
-Started by [Manfred Stienstra](http://www.dwerg.net/). Continued and
-maintained by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan
-Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com).
-
-Contact: markdown@freewisdom.org
-
-Copyright 2007-2013 The Python Markdown Project (v. 1.7 and later)
-Copyright 200? Django Software Foundation (OrderedDict implementation)
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
-License: BSD (see LICENSE for details).
+License: BSD (see LICENSE.md for details).
"""
from __future__ import absolute_import
from __future__ import unicode_literals
-from .__version__ import version, version_info # noqa
-import codecs
-import sys
-import logging
-import warnings
-import importlib
-from . import util
-from .preprocessors import build_preprocessors
-from .blockprocessors import build_block_parser
-from .treeprocessors import build_treeprocessors
-from .inlinepatterns import build_inlinepatterns
-from .postprocessors import build_postprocessors
-from .extensions import Extension
-from .serializers import to_html_string, to_xhtml_string
-
-__all__ = ['Markdown', 'markdown', 'markdownFromFile']
-
-
-logger = logging.getLogger('MARKDOWN')
-
-
-class Markdown(object):
- """Convert Markdown to HTML."""
-
- doc_tag = "div" # Element used to wrap document - later removed
-
- option_defaults = {
- 'html_replacement_text': '[HTML_REMOVED]',
- 'tab_length': 4,
- 'enable_attributes': True,
- 'smart_emphasis': True,
- 'lazy_ol': True,
- }
-
- output_formats = {
- 'html': to_html_string,
- 'html4': to_html_string,
- 'html5': to_html_string,
- 'xhtml': to_xhtml_string,
- 'xhtml1': to_xhtml_string,
- 'xhtml5': to_xhtml_string,
- }
-
- ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']',
- '(', ')', '>', '#', '+', '-', '.', '!']
-
- def __init__(self, *args, **kwargs):
- """
- Creates a new Markdown instance.
-
- 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.
- * output_format: Format of output. Supported formats are:
- * "xhtml1": Outputs XHTML 1.x. Default.
- * "xhtml5": Outputs XHTML style tags of HTML 5
- * "xhtml": Outputs latest supported version of XHTML
- (currently XHTML 1.1).
- * "html4": Outputs HTML 4
- * "html5": Outputs HTML style tags of HTML 5
- * "html": Outputs latest supported version of HTML
- (currently HTML 4).
- Note that it is suggested that the more specific formats ("xhtml1"
- and "html4") be used as "xhtml" or "html" may change in the future
- if it makes sense at that time.
- * safe_mode: Deprecated! Disallow raw html. One of "remove", "replace"
- or "escape".
- * html_replacement_text: Deprecated! Text used when safe_mode is set
- to "replace".
- * tab_length: Length of tabs in the source. Default: 4
- * enable_attributes: Enable the conversion of attributes. Default: True
- * smart_emphasis: Treat `_connected_words_` intelligently Default: True
- * lazy_ol: Ignore number of first item of ordered lists. Default: True
-
- """
-
- # For backward compatibility, loop through old positional args
- pos = ['extensions', 'extension_configs', 'safe_mode', 'output_format']
- for c, arg in enumerate(args):
- if pos[c] not in kwargs:
- kwargs[pos[c]] = arg
- if c+1 == len(pos): # pragma: no cover
- # ignore any additional args
- break
- if len(args):
- warnings.warn('Positional arguments are depreacted in Markdown'
- 'Use keyword arguments only.',
- DeprecationWarning)
-
- # Loop through kwargs and assign defaults
- for option, default in self.option_defaults.items():
- setattr(self, option, kwargs.get(option, default))
-
- self.safeMode = kwargs.get('safe_mode', False)
- if self.safeMode and 'enable_attributes' not in kwargs:
- # Disable attributes in safeMode when not explicitly set
- self.enable_attributes = False
-
- if 'safe_mode' in kwargs:
- warnings.warn('"safe_mode" is deprecated in Python-Markdown'
- 'Use an HTML sanitizer (like '
- 'Bleach http://bleach.readthedocs.org/) '
- 'if you are parsing untrusted markdown text. '
- 'See the 2.6 release notes for more info',
- DeprecationWarning)
-
- if 'html_replacement_text' in kwargs:
- warnings.warn('The "html_replacement_text" keyword is '
- 'deprecated along with "safe_mode".',
- DeprecationWarning)
-
- self.registeredExtensions = []
- self.docType = ""
- self.stripTopLevelTags = True
-
- self.build_parser()
-
- self.references = {}
- self.htmlStash = util.HtmlStash()
- self.registerExtensions(extensions=kwargs.get('extensions', []),
- configs=kwargs.get('extension_configs', {}))
- self.set_output_format(kwargs.get('output_format', 'xhtml1'))
- self.reset()
-
- def build_parser(self):
- """ Build the parser from the various parts. """
- self.preprocessors = build_preprocessors(self)
- self.parser = build_block_parser(self)
- self.inlinePatterns = build_inlinepatterns(self)
- self.treeprocessors = build_treeprocessors(self)
- self.postprocessors = build_postprocessors(self)
- return self
-
- def registerExtensions(self, extensions, configs):
- """
- Register extensions with this instance of Markdown.
-
- 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.
-
- """
- for ext in extensions:
- if isinstance(ext, util.string_type):
- ext = self.build_extension(ext, configs.get(ext, {}))
- if isinstance(ext, Extension):
- ext.extendMarkdown(self, globals())
- logger.debug(
- 'Successfully loaded extension "%s.%s".'
- % (ext.__class__.__module__, ext.__class__.__name__)
- )
- elif ext is not None:
- raise TypeError(
- 'Extension "%s.%s" must be of type: "markdown.Extension"'
- % (ext.__class__.__module__, ext.__class__.__name__))
-
- return self
-
- 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)"
-
- """
-
- 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 '
- 'deprecated. It is recommended that you '
- 'pass an instance of the extension class to '
- 'Markdown or use the "extension_configs" keyword. '
- 'The current behavior will raise an error in version 2.7. '
- 'See the Release Notes for Python-Markdown version '
- '2.6 for more info.', DeprecationWarning)
-
- # 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
-
- if class_name:
- # Load given class name from module.
- return getattr(module, class_name)(**configs)
- else:
- # Expect makeExtension() function to return a class.
- try:
- return module.makeExtension(**configs)
- except AttributeError as e:
- message = e.args[0]
- message = "Failed to initiate extension " \
- "'%s': %s" % (ext_name, message)
- e.args = (message,) + e.args[1:]
- raise
-
- def registerExtension(self, extension):
- """ This gets called by the extension """
- self.registeredExtensions.append(extension)
- return self
+from .core import Markdown, markdown, markdownFromFile
- def reset(self):
- """
- Resets all state variables so that we can start with a new text.
- """
- self.htmlStash.reset()
- self.references.clear()
+# For backward compatibility as some extensions expect it...
+from .extensions import Extension # noqa
- for extension in self.registeredExtensions:
- if hasattr(extension, 'reset'):
- extension.reset()
-
- return self
-
- def set_output_format(self, format):
- """ Set the output format for the class instance. """
- self.output_format = format.lower()
- try:
- self.serializer = self.output_formats[self.output_format]
- except KeyError as e:
- valid_formats = list(self.output_formats.keys())
- valid_formats.sort()
- message = 'Invalid Output Format: "%s". Use one of %s.' \
- % (self.output_format,
- '"' + '", "'.join(valid_formats) + '"')
- e.args = (message,) + e.args[1:]
- raise
- return self
-
- def convert(self, source):
- """
- Convert markdown to serialized XHTML or HTML.
-
- Keyword arguments:
-
- * source: Source text as a Unicode string.
-
- Markdown processing takes place in five steps:
-
- 1. A bunch of "preprocessors" munge the input text.
- 2. BlockParser() parses the high-level structural elements of the
- pre-processed text into an ElementTree.
- 3. A bunch of "treeprocessors" are run against the ElementTree. One
- such treeprocessor runs InlinePatterns against the ElementTree,
- detecting inline markup.
- 4. Some post-processors are run against the text after the ElementTree
- has been serialized into text.
- 5. The output is written to a string.
-
- """
-
- # Fixup the source text
- if not source.strip():
- return '' # a blank unicode string
-
- try:
- source = util.text_type(source)
- except UnicodeDecodeError as e:
- # Customise error message while maintaining original trackback
- e.reason += '. -- Note: Markdown only accepts unicode input!'
- raise
-
- # Split into lines and run the line preprocessors.
- self.lines = source.split("\n")
- for prep in self.preprocessors.values():
- self.lines = prep.run(self.lines)
-
- # Parse the high-level elements.
- root = self.parser.parseDocument(self.lines).getroot()
-
- # Run the tree-processors
- for treeprocessor in self.treeprocessors.values():
- newRoot = treeprocessor.run(root)
- if newRoot is not None:
- root = newRoot
-
- # Serialize _properly_. Strip top-level tags.
- output = self.serializer(root)
- if self.stripTopLevelTags:
- try:
- start = output.index(
- '<%s>' % self.doc_tag) + len(self.doc_tag) + 2
- end = output.rindex('</%s>' % self.doc_tag)
- output = output[start:end].strip()
- except ValueError: # pragma: no cover
- if output.strip().endswith('<%s />' % self.doc_tag):
- # We have an empty document
- output = ''
- else:
- # We have a serious problem
- raise ValueError('Markdown failed to strip top-level '
- 'tags. Document=%r' % output.strip())
-
- # Run the text post-processors
- for pp in self.postprocessors.values():
- output = pp.run(output)
-
- return output.strip()
-
- def convertFile(self, input=None, output=None, encoding=None):
- """Converts a markdown file and returns the HTML as a unicode string.
-
- Decodes the file using the provided encoding (defaults to utf-8),
- passes the file content to markdown, and outputs the html to either
- the provided stream or the file with provided name, using the same
- encoding as the source file. The 'xmlcharrefreplace' error handler is
- used when encoding the output.
-
- **Note:** This is the only place that decoding and encoding of unicode
- takes place in Python-Markdown. (All other code is unicode-in /
- unicode-out.)
-
- Keyword arguments:
-
- * input: File object or path. Reads from stdin if `None`.
- * output: File object or path. Writes to stdout if `None`.
- * encoding: Encoding of input and output files. Defaults to utf-8.
-
- """
-
- encoding = encoding or "utf-8"
-
- # Read the source
- if input:
- if isinstance(input, util.string_type):
- input_file = codecs.open(input, mode="r", encoding=encoding)
- else:
- input_file = codecs.getreader(encoding)(input)
- text = input_file.read()
- input_file.close()
- else:
- text = sys.stdin.read()
- if not isinstance(text, util.text_type):
- text = text.decode(encoding)
-
- text = text.lstrip('\ufeff') # remove the byte-order mark
-
- # Convert
- html = self.convert(text)
-
- # Write to file or stdout
- if output:
- if isinstance(output, util.string_type):
- output_file = codecs.open(output, "w",
- encoding=encoding,
- errors="xmlcharrefreplace")
- output_file.write(html)
- output_file.close()
- else:
- writer = codecs.getwriter(encoding)
- output_file = writer(output, errors="xmlcharrefreplace")
- output_file.write(html)
- # Don't close here. User may want to write more.
- else:
- # Encode manually and write bytes to stdout.
- html = html.encode(encoding, "xmlcharrefreplace")
- try:
- # Write bytes directly to buffer (Python 3).
- sys.stdout.buffer.write(html)
- except AttributeError:
- # Probably Python 2, which works with bytes by default.
- sys.stdout.write(html)
-
- return self
-
-
-"""
-EXPORTED FUNCTIONS
-=============================================================================
-
-Those are the two functions we really mean to export: markdown() and
-markdownFromFile().
-"""
-
-
-def markdown(text, *args, **kwargs):
- """Convert a markdown string to HTML and return HTML as a unicode string.
-
- This is a shortcut function for `Markdown` class to cover the most
- basic use case. It initializes an instance of Markdown, loads the
- necessary extensions and runs the parser on the given text.
-
- Keyword arguments:
-
- * text: Markdown formatted text as Unicode or ASCII string.
- * Any arguments accepted by the Markdown class.
-
- Returns: An HTML document as a string.
+__all__ = ['Markdown', 'markdown', 'markdownFromFile']
- """
- md = Markdown(*args, **kwargs)
- return md.convert(text)
+# version_info should conform to PEP 386
+# (major, minor, micro, alpha/beta/rc/final, #)
+# (1, 1, 2, 'alpha', 0) => "1.1.2.dev"
+# (1, 2, 0, 'beta', 2) => "1.2b2"
+__version_info__ = (3, 0, 0, 'final', 0)
-def markdownFromFile(*args, **kwargs):
- """Read markdown code from a file and write it to a file or a stream.
+def _get_version(): # pragma: no cover
+ " Returns a PEP 386-compliant version number from version_info. "
+ assert len(__version_info__) == 5
+ assert __version_info__[3] in ('alpha', 'beta', 'rc', 'final')
- This is a shortcut function which initializes an instance of Markdown,
- and calls the convertFile method rather than convert.
+ parts = 2 if __version_info__[2] == 0 else 3
+ main = '.'.join(map(str, __version_info__[:parts]))
- Keyword arguments:
+ sub = ''
+ if __version_info__[3] == 'alpha' and __version_info__[4] == 0:
+ # TODO: maybe append some sort of git info here??
+ sub = '.dev'
+ elif __version_info__[3] != 'final':
+ mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
+ sub = mapping[__version_info__[3]] + str(__version_info__[4])
- * input: a file name or readable object.
- * output: a file name or writable object.
- * encoding: Encoding of input and output.
- * Any arguments accepted by the Markdown class.
+ return str(main + sub)
- """
- # For backward compatibility loop through positional args
- pos = ['input', 'output', 'extensions', 'encoding']
- c = 0
- for arg in args:
- if pos[c] not in kwargs:
- kwargs[pos[c]] = arg
- c += 1
- if c == len(pos):
- break
- if len(args):
- warnings.warn('Positional arguments are depreacted in '
- 'Markdown and will raise an error in version 2.7. '
- 'Use keyword arguments only.',
- DeprecationWarning)
- md = Markdown(**kwargs)
- md.convertFile(kwargs.get('input', None),
- kwargs.get('output', None),
- kwargs.get('encoding', None))
+__version__ = _get_version()
+# -*- coding: utf-8 -*-
"""
-COMMAND-LINE SPECIFIC STUFF
-=============================================================================
+Python Markdown
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
"""
import sys
usage = """%prog [options] [INPUTFILE]
(STDIN is assumed if no INPUTFILE is given)"""
desc = "A Python implementation of John Gruber's Markdown. " \
- "https://pythonhosted.org/Markdown/"
- ver = "%%prog %s" % markdown.version
+ "https://Python-Markdown.github.io/"
+ ver = "%%prog %s" % markdown.__version__
parser = optparse.OptionParser(usage=usage, description=desc, version=ver)
parser.add_option("-f", "--file", dest="filename", default=None,
metavar="OUTPUT_FILE")
parser.add_option("-e", "--encoding", dest="encoding",
help="Encoding for input and output files.",)
- parser.add_option("-s", "--safe", dest="safe", default=False,
- metavar="SAFE_MODE",
- help="Deprecated! 'replace', 'remove' or 'escape' HTML "
- "tags in input")
parser.add_option("-o", "--output_format", dest="output_format",
- default='xhtml1', metavar="OUTPUT_FORMAT",
- help="'xhtml1' (default), 'html4' or 'html5'.")
+ default='xhtml', metavar="OUTPUT_FORMAT",
+ help="Use output format 'xhtml' (default) or 'html'.")
parser.add_option("-n", "--no_lazy_ol", dest="lazy_ol",
action='store_false', default=True,
help="Observe number of first item of ordered lists.")
'lazy_ol': options.lazy_ol
}
- if options.safe:
- # Avoid deprecation warning if user didn't set option
- opts['safe_mode'] = options.safe
-
return opts, options.verbose
+++ /dev/null
-#
-# markdown/__version__.py
-#
-# version_info should conform to PEP 386
-# (major, minor, micro, alpha/beta/rc/final, #)
-# (1, 1, 2, 'alpha', 0) => "1.1.2.dev"
-# (1, 2, 0, 'beta', 2) => "1.2b2"
-version_info = (2, 6, 0, 'final', 0)
-
-
-def _get_version():
- " Returns a PEP 386-compliant version number from version_info. "
- assert len(version_info) == 5
- assert version_info[3] in ('alpha', 'beta', 'rc', 'final')
-
- parts = 2 if version_info[2] == 0 else 3
- main = '.'.join(map(str, version_info[:parts]))
-
- sub = ''
- if version_info[3] == 'alpha' and version_info[4] == 0:
- # TODO: maybe append some sort of git info here??
- sub = '.dev'
- elif version_info[3] != 'final':
- mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
- sub = mapping[version_info[3]] + str(version_info[4])
-
- return str(main + sub)
-
-version = _get_version()
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
from __future__ import unicode_literals
from __future__ import absolute_import
from . import util
-from . import odict
class State(list):
looping through them and creating an ElementTree object.
"""
- def __init__(self, markdown):
- self.blockprocessors = odict.OrderedDict()
+ def __init__(self, md):
+ self.blockprocessors = util.Registry()
self.state = State()
- self.markdown = markdown
+ self.md = md
+
+ @property
+ @util.deprecated("Use 'md' instead.")
+ def markdown(self):
+ # TODO: remove this later
+ return self.md
def parseDocument(self, lines):
""" Parse a markdown document into an ElementTree.
"""
# Create a ElementTree from the lines
- self.root = util.etree.Element(self.markdown.doc_tag)
+ self.root = util.etree.Element(self.md.doc_tag)
self.parseChunk(self.root, '\n'.join(lines))
return util.etree.ElementTree(self.root)
"""
while blocks:
- for processor in self.blockprocessors.values():
+ for processor in self.blockprocessors:
if processor.test(parent, blocks[0]):
if processor.run(parent, blocks) is not False:
# run returns True or None
+# -*- coding: utf-8 -*-
"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+
CORE MARKDOWN BLOCKPARSER
===========================================================================
itself with inline elements such as **bold** or *italics*, but rather just
catches blocks, lists, quotes, etc.
-The BlockParser is made up of a bunch of BlockProssors, each handling a
+The BlockParser is made up of a bunch of BlockProcessors, each handling a
different type of block. Extensions may add/replace/remove BlockProcessors
as they need to alter how markdown blocks are parsed.
"""
logger = logging.getLogger('MARKDOWN')
-def build_block_parser(md_instance, **kwargs):
+def build_block_parser(md, **kwargs):
""" Build the default block parser used by Markdown. """
- parser = BlockParser(md_instance)
- parser.blockprocessors['empty'] = EmptyBlockProcessor(parser)
- parser.blockprocessors['indent'] = ListIndentProcessor(parser)
- parser.blockprocessors['code'] = CodeBlockProcessor(parser)
- parser.blockprocessors['hashheader'] = HashHeaderProcessor(parser)
- parser.blockprocessors['setextheader'] = SetextHeaderProcessor(parser)
- parser.blockprocessors['hr'] = HRProcessor(parser)
- parser.blockprocessors['olist'] = OListProcessor(parser)
- parser.blockprocessors['ulist'] = UListProcessor(parser)
- parser.blockprocessors['quote'] = BlockQuoteProcessor(parser)
- parser.blockprocessors['paragraph'] = ParagraphProcessor(parser)
+ parser = BlockParser(md)
+ parser.blockprocessors.register(EmptyBlockProcessor(parser), 'empty', 100)
+ parser.blockprocessors.register(ListIndentProcessor(parser), 'indent', 90)
+ parser.blockprocessors.register(CodeBlockProcessor(parser), 'code', 80)
+ parser.blockprocessors.register(HashHeaderProcessor(parser), 'hashheader', 70)
+ parser.blockprocessors.register(SetextHeaderProcessor(parser), 'setextheader', 60)
+ parser.blockprocessors.register(HRProcessor(parser), 'hr', 50)
+ parser.blockprocessors.register(OListProcessor(parser), 'olist', 40)
+ parser.blockprocessors.register(UListProcessor(parser), 'ulist', 30)
+ parser.blockprocessors.register(BlockQuoteProcessor(parser), 'quote', 20)
+ parser.blockprocessors.register(ParagraphProcessor(parser), 'paragraph', 10)
return parser
-class BlockProcessor:
+class BlockProcessor(object):
""" Base class for block processors.
Each subclass will provide the methods below to work with the source and
def __init__(self, parser):
self.parser = parser
- self.tab_length = parser.markdown.tab_length
+ self.tab_length = parser.md.tab_length
def lastChild(self, parent):
""" Return the last child of an etree element. """
LIST_TYPES = ['ul', 'ol']
def __init__(self, *args):
- BlockProcessor.__init__(self, *args)
+ super(ListIndentProcessor, self).__init__(*args)
self.INDENT_RE = re.compile(r'^(([ ]{%s})+)' % self.tab_length)
def test(self, parent, block):
code = sibling[0]
block, theRest = self.detab(block)
code.text = util.AtomicString(
- '%s\n%s\n' % (code.text, block.rstrip())
+ '%s\n%s\n' % (code.text, util.code_escape(block.rstrip()))
)
else:
# This is a new codeblock. Create the elements and insert text.
pre = util.etree.SubElement(parent, 'pre')
code = util.etree.SubElement(pre, 'code')
block, theRest = self.detab(block)
- code.text = util.AtomicString('%s\n' % block.rstrip())
+ code.text = util.AtomicString('%s\n' % util.code_escape(block.rstrip()))
if theRest:
# This block contained unindented line(s) after the first indented
# line. Insert these lines as the first block of the master blocks
before = block[:m.start()] # Lines before blockquote
# Pass lines before blockquote in recursively for parsing forst.
self.parser.parseBlocks(parent, [before])
- # Remove ``> `` from begining of each line.
+ # Remove ``> `` from beginning of each line.
block = '\n'.join(
[self.clean(line) for line in block[m.start():].split('\n')]
)
""" Process ordered list blocks. """
TAG = 'ol'
- # Detect an item (``1. item``). ``group(1)`` contains contents of item.
- RE = re.compile(r'^[ ]{0,3}\d+\.[ ]+(.*)')
- # Detect items on secondary lines. they can be of either list type.
- CHILD_RE = re.compile(r'^[ ]{0,3}((\d+\.)|[*+-])[ ]+(.*)')
- # Detect indented (nested) items of either type
- INDENT_RE = re.compile(r'^[ ]{4,7}((\d+\.)|[*+-])[ ]+.*')
# The integer (python string) with which the lists starts (default=1)
# Eg: If list is intialized as)
# 3. Item
# The ol tag will get starts="3" attribute
STARTSWITH = '1'
+ # Lazy ol - ignore startswith
+ LAZY_OL = True
# List of allowed sibling tags.
SIBLING_TAGS = ['ol', 'ul']
+ def __init__(self, parser):
+ super(OListProcessor, self).__init__(parser)
+ # Detect an item (``1. item``). ``group(1)`` contains contents of item.
+ self.RE = re.compile(r'^[ ]{0,%d}\d+\.[ ]+(.*)' % (self.tab_length - 1))
+ # Detect items on secondary lines. they can be of either list type.
+ self.CHILD_RE = re.compile(r'^[ ]{0,%d}((\d+\.)|[*+-])[ ]+(.*)' %
+ (self.tab_length - 1))
+ # Detect indented (nested) items of either type
+ self.INDENT_RE = re.compile(r'^[ ]{%d,%d}((\d+\.)|[*+-])[ ]+.*' %
+ (self.tab_length, self.tab_length * 2 - 1))
+
def test(self, parent, block):
return bool(self.RE.match(block))
# This is a new list so create parent with appropriate tag.
lst = util.etree.SubElement(parent, self.TAG)
# Check if a custom start integer is set
- if not self.parser.markdown.lazy_ol and self.STARTSWITH != '1':
+ if not self.LAZY_OL and self.STARTSWITH != '1':
lst.attrib['start'] = self.STARTSWITH
self.parser.state.set('list')
# Check first item for the start index
if not items and self.TAG == 'ol':
# Detect the integer value of first list item
- INTEGER_RE = re.compile('(\d+)')
+ INTEGER_RE = re.compile(r'(\d+)')
self.STARTSWITH = INTEGER_RE.match(m.group(1)).group()
# Append to the list
items.append(m.group(3))
""" Process unordered list blocks. """
TAG = 'ul'
- RE = re.compile(r'^[ ]{0,3}[*+-][ ]+(.*)')
+
+ def __init__(self, parser):
+ super(UListProcessor, self).__init__(parser)
+ # Detect an item (``1. item``). ``group(1)`` contains contents of item.
+ self.RE = re.compile(r'^[ ]{0,%d}[*+-][ ]+(.*)' % (self.tab_length - 1))
class HashHeaderProcessor(BlockProcessor):
def run(self, parent, blocks):
block = blocks.pop(0)
+ match = self.match
# Check for lines in block before hr.
- prelines = block[:self.match.start()].rstrip('\n')
+ prelines = block[:match.start()].rstrip('\n')
if prelines:
# Recursively parse lines before hr so they get parsed first.
self.parser.parseBlocks(parent, [prelines])
# create hr
util.etree.SubElement(parent, 'hr')
# check for lines in block after hr.
- postlines = block[self.match.end():].lstrip('\n')
+ postlines = block[match.end():].lstrip('\n')
if postlines:
# Add lines after hr to master blocks for later parsing.
blocks.insert(0, postlines)
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from __future__ import absolute_import
+from __future__ import unicode_literals
+import codecs
+import sys
+import logging
+import importlib
+import pkg_resources
+from . import util
+from .preprocessors import build_preprocessors
+from .blockprocessors import build_block_parser
+from .treeprocessors import build_treeprocessors
+from .inlinepatterns import build_inlinepatterns
+from .postprocessors import build_postprocessors
+from .extensions import Extension
+from .serializers import to_html_string, to_xhtml_string
+
+__all__ = ['Markdown', 'markdown', 'markdownFromFile']
+
+
+logger = logging.getLogger('MARKDOWN')
+
+
+class Markdown(object):
+ """Convert Markdown to HTML."""
+
+ doc_tag = "div" # Element used to wrap document - later removed
+
+ output_formats = {
+ 'html': to_html_string,
+ 'xhtml': to_xhtml_string,
+ }
+
+ block_level_elements = [
+ # Elements which are invalid to wrap in a `<p>` tag.
+ # See http://w3c.github.io/html/grouping-content.html#the-p-element
+ 'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl',
+ 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3',
+ 'h4', 'h5', 'h6', 'header', 'hr', 'main', 'menu', 'nav', 'ol', 'p', 'pre',
+ 'section', 'table', 'ul',
+ # Other elements which Markdown should not be mucking up the contents of.
+ 'canvas', 'dd', 'dt', 'group', 'iframe', 'li', 'math', 'noscript', 'output',
+ 'progress', 'script', 'style', 'tbody', 'td', 'th', 'thead', 'tr', 'video'
+ ]
+
+ def __init__(self, **kwargs):
+ """
+ Creates a new Markdown instance.
+
+ Keyword arguments:
+
+ * extensions: A list of 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:
+ * "xhtml": Outputs XHTML style tags. Default.
+ * "html": Outputs HTML style tags.
+ * tab_length: Length of tabs in the source. Default: 4
+
+ """
+
+ self.tab_length = kwargs.get('tab_length', 4)
+
+ self.ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']',
+ '(', ')', '>', '#', '+', '-', '.', '!']
+
+ self.registeredExtensions = []
+ self.docType = ""
+ self.stripTopLevelTags = True
+
+ self.build_parser()
+
+ self.references = {}
+ self.htmlStash = util.HtmlStash()
+ self.registerExtensions(extensions=kwargs.get('extensions', []),
+ configs=kwargs.get('extension_configs', {}))
+ self.set_output_format(kwargs.get('output_format', 'xhtml'))
+ self.reset()
+
+ def build_parser(self):
+ """ Build the parser from the various parts. """
+ self.preprocessors = build_preprocessors(self)
+ self.parser = build_block_parser(self)
+ self.inlinePatterns = build_inlinepatterns(self)
+ self.treeprocessors = build_treeprocessors(self)
+ self.postprocessors = build_postprocessors(self)
+ return self
+
+ def registerExtensions(self, extensions, configs):
+ """
+ Register extensions with this instance of Markdown.
+
+ Keyword arguments:
+
+ * extensions: A list of extensions, which can either
+ be strings or objects.
+ * configs: A dictionary mapping extension names to config options.
+
+ """
+ for ext in extensions:
+ if isinstance(ext, util.string_type):
+ ext = self.build_extension(ext, configs.get(ext, {}))
+ if isinstance(ext, Extension):
+ ext._extendMarkdown(self)
+ logger.debug(
+ 'Successfully loaded extension "%s.%s".'
+ % (ext.__class__.__module__, ext.__class__.__name__)
+ )
+ elif ext is not None:
+ raise TypeError(
+ 'Extension "%s.%s" must be of type: "%s.%s"' % (
+ ext.__class__.__module__, ext.__class__.__name__,
+ Extension.__module__, Extension.__name__
+ )
+ )
+ return self
+
+ def build_extension(self, ext_name, configs):
+ """
+ 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:
+ module = importlib.import_module(ext_name)
+ logger.debug(
+ 'Successfuly imported extension module "%s".' % ext_name
+ )
+ 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.
+ return getattr(module, class_name)(**configs)
+ else:
+ # Expect makeExtension() function to return a class.
+ try:
+ return module.makeExtension(**configs)
+ except AttributeError as e:
+ message = e.args[0]
+ message = "Failed to initiate extension " \
+ "'%s': %s" % (ext_name, message)
+ e.args = (message,) + e.args[1:]
+ raise
+
+ def registerExtension(self, extension):
+ """ This gets called by the extension """
+ self.registeredExtensions.append(extension)
+ return self
+
+ def reset(self):
+ """
+ Resets all state variables so that we can start with a new text.
+ """
+ self.htmlStash.reset()
+ self.references.clear()
+
+ for extension in self.registeredExtensions:
+ if hasattr(extension, 'reset'):
+ extension.reset()
+
+ return self
+
+ def set_output_format(self, format):
+ """ Set the output format for the class instance. """
+ self.output_format = format.lower().rstrip('145') # ignore num
+ try:
+ self.serializer = self.output_formats[self.output_format]
+ except KeyError as e:
+ valid_formats = list(self.output_formats.keys())
+ valid_formats.sort()
+ message = 'Invalid Output Format: "%s". Use one of %s.' \
+ % (self.output_format,
+ '"' + '", "'.join(valid_formats) + '"')
+ e.args = (message,) + e.args[1:]
+ raise
+ return self
+
+ def is_block_level(self, tag):
+ """Check if the tag is a block level HTML tag."""
+ if isinstance(tag, util.string_type):
+ return tag.lower().rstrip('/') in self.block_level_elements
+ # Some ElementTree tags are not strings, so return False.
+ return False
+
+ def convert(self, source):
+ """
+ Convert markdown to serialized XHTML or HTML.
+
+ Keyword arguments:
+
+ * source: Source text as a Unicode string.
+
+ Markdown processing takes place in five steps:
+
+ 1. A bunch of "preprocessors" munge the input text.
+ 2. BlockParser() parses the high-level structural elements of the
+ pre-processed text into an ElementTree.
+ 3. A bunch of "treeprocessors" are run against the ElementTree. One
+ such treeprocessor runs InlinePatterns against the ElementTree,
+ detecting inline markup.
+ 4. Some post-processors are run against the text after the ElementTree
+ has been serialized into text.
+ 5. The output is written to a string.
+
+ """
+
+ # Fixup the source text
+ if not source.strip():
+ return '' # a blank unicode string
+
+ try:
+ source = util.text_type(source)
+ except UnicodeDecodeError as e: # pragma: no cover
+ # Customise error message while maintaining original trackback
+ e.reason += '. -- Note: Markdown only accepts unicode input!'
+ raise
+
+ # Split into lines and run the line preprocessors.
+ self.lines = source.split("\n")
+ for prep in self.preprocessors:
+ self.lines = prep.run(self.lines)
+
+ # Parse the high-level elements.
+ root = self.parser.parseDocument(self.lines).getroot()
+
+ # Run the tree-processors
+ for treeprocessor in self.treeprocessors:
+ newRoot = treeprocessor.run(root)
+ if newRoot is not None:
+ root = newRoot
+
+ # Serialize _properly_. Strip top-level tags.
+ output = self.serializer(root)
+ if self.stripTopLevelTags:
+ try:
+ start = output.index(
+ '<%s>' % self.doc_tag) + len(self.doc_tag) + 2
+ end = output.rindex('</%s>' % self.doc_tag)
+ output = output[start:end].strip()
+ except ValueError: # pragma: no cover
+ if output.strip().endswith('<%s />' % self.doc_tag):
+ # We have an empty document
+ output = ''
+ else:
+ # We have a serious problem
+ raise ValueError('Markdown failed to strip top-level '
+ 'tags. Document=%r' % output.strip())
+
+ # Run the text post-processors
+ for pp in self.postprocessors:
+ output = pp.run(output)
+
+ return output.strip()
+
+ def convertFile(self, input=None, output=None, encoding=None):
+ """Converts a markdown file and returns the HTML as a unicode string.
+
+ Decodes the file using the provided encoding (defaults to utf-8),
+ passes the file content to markdown, and outputs the html to either
+ the provided stream or the file with provided name, using the same
+ encoding as the source file. The 'xmlcharrefreplace' error handler is
+ used when encoding the output.
+
+ **Note:** This is the only place that decoding and encoding of unicode
+ takes place in Python-Markdown. (All other code is unicode-in /
+ unicode-out.)
+
+ Keyword arguments:
+
+ * input: File object or path. Reads from stdin if `None`.
+ * output: File object or path. Writes to stdout if `None`.
+ * encoding: Encoding of input and output files. Defaults to utf-8.
+
+ """
+
+ encoding = encoding or "utf-8"
+
+ # Read the source
+ if input:
+ if isinstance(input, util.string_type):
+ input_file = codecs.open(input, mode="r", encoding=encoding)
+ else:
+ input_file = codecs.getreader(encoding)(input)
+ text = input_file.read()
+ input_file.close()
+ else:
+ text = sys.stdin.read()
+ if not isinstance(text, util.text_type): # pragma: no cover
+ text = text.decode(encoding)
+
+ text = text.lstrip('\ufeff') # remove the byte-order mark
+
+ # Convert
+ html = self.convert(text)
+
+ # Write to file or stdout
+ if output:
+ if isinstance(output, util.string_type):
+ output_file = codecs.open(output, "w",
+ encoding=encoding,
+ errors="xmlcharrefreplace")
+ output_file.write(html)
+ output_file.close()
+ else:
+ writer = codecs.getwriter(encoding)
+ output_file = writer(output, errors="xmlcharrefreplace")
+ output_file.write(html)
+ # Don't close here. User may want to write more.
+ else:
+ # Encode manually and write bytes to stdout.
+ html = html.encode(encoding, "xmlcharrefreplace")
+ try:
+ # Write bytes directly to buffer (Python 3).
+ sys.stdout.buffer.write(html)
+ except AttributeError: # pragma: no cover
+ # Probably Python 2, which works with bytes by default.
+ sys.stdout.write(html)
+
+ return self
+
+
+"""
+EXPORTED FUNCTIONS
+=============================================================================
+
+Those are the two functions we really mean to export: markdown() and
+markdownFromFile().
+"""
+
+
+def markdown(text, **kwargs):
+ """Convert a markdown string to HTML and return HTML as a unicode string.
+
+ This is a shortcut function for `Markdown` class to cover the most
+ basic use case. It initializes an instance of Markdown, loads the
+ necessary extensions and runs the parser on the given text.
+
+ Keyword arguments:
+
+ * text: Markdown formatted text as Unicode or ASCII string.
+ * Any arguments accepted by the Markdown class.
+
+ Returns: An HTML document as a string.
+
+ """
+ md = Markdown(**kwargs)
+ return md.convert(text)
+
+
+def markdownFromFile(**kwargs):
+ """Read markdown code from a file and write it to a file or a stream.
+
+ This is a shortcut function which initializes an instance of Markdown,
+ and calls the convertFile method rather than convert.
+
+ Keyword arguments:
+
+ * input: a file name or readable object.
+ * output: a file name or writable object.
+ * encoding: Encoding of input and output.
+ * Any arguments accepted by the Markdown class.
+
+ """
+ md = Markdown(**kwargs)
+ md.convertFile(kwargs.get('input', None),
+ kwargs.get('output', None),
+ kwargs.get('encoding', None))
+# -*- coding: utf-8 -*-
"""
-Extensions
------------------------------------------------------------------------------
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
"""
from __future__ import unicode_literals
-from ..util import parseBoolValue
import warnings
+from ..util import parseBoolValue
class Extension(object):
# if a default is not set here.
config = {}
- def __init__(self, *args, **kwargs):
+ def __init__(self, **kwargs):
""" Initiate Extension and set up configs. """
-
- # check for configs arg for backward compat.
- # (there only ever used to be one so we use arg[0])
- if len(args):
- if args[0] is not None:
- 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 are deprecated and will raise '
- 'an error in version 2.7. See the Release Notes for '
- 'Python-Markdown version 2.6 for more info.',
- DeprecationWarning)
- # check for configs kwarg for backward compat.
- if 'configs' in kwargs.keys():
- if kwargs['configs'] is not None:
- 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 is '
- 'deprecated and raise an error in '
- 'version 2.7. See the Release Notes for '
- 'Python-Markdown version 2.6 for more info.',
- DeprecationWarning)
- # finally, use kwargs
self.setConfigs(kwargs)
def getConfig(self, key, default=''):
for key, value in items:
self.setConfig(key, value)
- def extendMarkdown(self, md, md_globals):
+ def _extendMarkdown(self, *args):
+ """ Private wrapper around extendMarkdown. """
+ md = args[0]
+ try:
+ self.extendMarkdown(md)
+ except TypeError:
+ # Must be a 2.x extension. Pass in a dumby md_globals.
+ self.extendMarkdown(md, {})
+ warnings.warn(
+ "The 'md_globals' parameter of '{0}.{1}.extendMarkdown' is "
+ "deprecated.".format(self.__class__.__module__, self.__class__.__name__),
+ category=DeprecationWarning,
+ stacklevel=2
+ )
+
+ def extendMarkdown(self, md):
"""
Add the various proccesors and patterns to the Markdown Instance.
This extension adds abbreviation handling to Python-Markdown.
-See <https://pythonhosted.org/Markdown/extensions/abbreviations.html>
+See <https://Python-Markdown.github.io/extensions/abbreviations>
for documentation.
Oringinal code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/) and
from __future__ import unicode_literals
from . import Extension
from ..preprocessors import Preprocessor
-from ..inlinepatterns import Pattern
+from ..inlinepatterns import InlineProcessor
from ..util import etree, AtomicString
import re
class AbbrExtension(Extension):
""" Abbreviation Extension for Python-Markdown. """
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Insert AbbrPreprocessor before ReferencePreprocessor. """
- md.preprocessors.add('abbr', AbbrPreprocessor(md), '<reference')
+ md.preprocessors.register(AbbrPreprocessor(md), 'abbr', 12)
class AbbrPreprocessor(Preprocessor):
if m:
abbr = m.group('abbr').strip()
title = m.group('title').strip()
- self.markdown.inlinePatterns['abbr-%s' % abbr] = \
- AbbrPattern(self._generate_pattern(abbr), title)
+ self.md.inlinePatterns.register(
+ AbbrInlineProcessor(self._generate_pattern(abbr), title), 'abbr-%s' % abbr, 2
+ )
+ # Preserve the line to prevent raw HTML indexing issue.
+ # https://github.com/Python-Markdown/markdown/issues/584
+ new_text.append('')
else:
new_text.append(line)
return new_text
return r'(?P<abbr>\b%s\b)' % (r''.join(chars))
-class AbbrPattern(Pattern):
+class AbbrInlineProcessor(InlineProcessor):
""" Abbreviation inline pattern. """
def __init__(self, pattern, title):
- super(AbbrPattern, self).__init__(pattern)
+ super(AbbrInlineProcessor, self).__init__(pattern)
self.title = title
- def handleMatch(self, m):
+ def handleMatch(self, m, data):
abbr = etree.Element('abbr')
abbr.text = AtomicString(m.group('abbr'))
abbr.set('title', self.title)
- return abbr
+ return abbr, m.start(0), m.end(0)
-def makeExtension(*args, **kwargs):
- return AbbrExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return AbbrExtension(**kwargs)
[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions # noqa
-See <https://pythonhosted.org/Markdown/extensions/admonition.html>
+See <https://Python-Markdown.github.io/extensions/admonition>
for documentation.
Original code Copyright [Tiago Serafim](http://www.tiagoserafim.com/).
class AdmonitionExtension(Extension):
""" Admonition extension for Python-Markdown. """
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Add Admonition to Markdown instance. """
md.registerExtension(self)
- md.parser.blockprocessors.add('admonition',
- AdmonitionProcessor(md.parser),
- '_begin')
+ md.parser.blockprocessors.register(AdmonitionProcessor(md.parser), 'admonition', 105)
class AdmonitionProcessor(BlockProcessor):
CLASSNAME = 'admonition'
CLASSNAME_TITLE = 'admonition-title'
- RE = re.compile(r'(?:^|\n)!!!\ ?([\w\-]+)(?:\ "(.*?)")?')
+ RE = re.compile(r'(?:^|\n)!!! ?([\w\-]+(?: +[\w\-]+)*)(?: +"(.*?)")? *(?:\n|$)')
+ RE_SPACES = re.compile(' +')
def test(self, parent, block):
sibling = self.lastChild(parent)
m = self.RE.search(block)
if m:
- block = block[m.end() + 1:] # removes the first line
+ block = block[m.end():] # removes the first line
block, theRest = self.detab(block)
def get_class_and_title(self, match):
klass, title = match.group(1).lower(), match.group(2)
+ klass = self.RE_SPACES.sub(' ', klass)
if title is None:
# no title was provided, use the capitalized classname as title
# e.g.: `!!! note` will render
# `<p class="admonition-title">Note</p>`
- title = klass.capitalize()
+ title = klass.split(' ', 1)[0].capitalize()
elif title == '':
# an explicit blank title should not be rendered
# e.g.: `!!! warning ""` will *not* render `p` with a title
return klass, title
-def makeExtension(*args, **kwargs):
- return AdmonitionExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return AdmonitionExtension(**kwargs)
[maruku](http://maruku.rubyforge.org/proposal.html#attribute_lists)'s
feature of the same name.
-See <https://pythonhosted.org/Markdown/extensions/attr_list.html>
+See <https://Python-Markdown.github.io/extensions/attr_list>
for documentation.
Original code Copyright 2011 [Waylan Limberg](http://achinghead.com/).
from __future__ import unicode_literals
from . import Extension
from ..treeprocessors import Treeprocessor
-from ..util import isBlockLevel
import re
-try:
- Scanner = re.Scanner
-except AttributeError: # pragma: no cover
- # must be on Python 2.4
- from sre import Scanner
-
def _handle_double_quote(s, t):
- k, v = t.split('=')
+ k, v = t.split('=', 1)
return k, v.strip('"')
def _handle_single_quote(s, t):
- k, v = t.split('=')
+ k, v = t.split('=', 1)
return k, v.strip("'")
def _handle_key_value(s, t):
- return t.split('=')
+ return t.split('=', 1)
def _handle_word(s, t):
return 'id', t[1:]
return t, t
-_scanner = Scanner([
- (r'[^ ]+=".*?"', _handle_double_quote),
- (r"[^ ]+='.*?'", _handle_single_quote),
- (r'[^ ]+=[^ ]*', _handle_key_value),
- (r'[^ ]+', _handle_word),
+
+_scanner = re.Scanner([
+ (r'[^ =]+=".*?"', _handle_double_quote),
+ (r"[^ =]+='.*?'", _handle_single_quote),
+ (r'[^ =]+=[^ =]+', _handle_key_value),
+ (r'[^ =]+', _handle_word),
(r' ', None)
])
class AttrListTreeprocessor(Treeprocessor):
- BASE_RE = r'\{\:?([^\}]*)\}'
+ BASE_RE = r'\{\:?([^\}\n]*)\}'
HEADER_RE = re.compile(r'[ ]+%s[ ]*$' % BASE_RE)
BLOCK_RE = re.compile(r'\n[ ]*%s[ ]*$' % BASE_RE)
INLINE_RE = re.compile(r'^%s' % BASE_RE)
r'\:\-\.0-9\u00b7\u0300-\u036f\u203f-\u2040]+')
def run(self, doc):
- for elem in doc.getiterator():
- if isBlockLevel(elem.tag):
+ for elem in doc.iter():
+ if self.md.is_block_level(elem.tag):
# Block level: check for attrs on last line of text
RE = self.BLOCK_RE
if isheader(elem) or elem.tag == 'dt':
class AttrListExtension(Extension):
- def extendMarkdown(self, md, md_globals):
- md.treeprocessors.add(
- 'attr_list', AttrListTreeprocessor(md), '>prettify'
- )
+ def extendMarkdown(self, md):
+ md.treeprocessors.register(AttrListTreeprocessor(md), 'attr_list', 8)
-def makeExtension(*args, **kwargs):
- return AttrListExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return AttrListExtension(**kwargs)
Adds code/syntax highlighting to standard Python-Markdown code blocks.
-See <https://pythonhosted.org/Markdown/extensions/code_hilite.html>
+See <https://Python-Markdown.github.io/extensions/code_hilite>
for documentation.
Original code Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/).
c = re.compile(r'''
(?:(?:^::+)|(?P<shebang>^[#]!)) # Shebang or 2 or more colons
(?P<path>(?:/\w+)*[/ ])? # Zero or 1 path
- (?P<lang>[\w+-]*) # The language
+ (?P<lang>[\w#.+-]*) # The language
\s* # Arbitrary whitespace
# Optional highlight lines, single- or double-quote-delimited
(hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?
css_class=self.config['css_class'],
style=self.config['pygments_style'],
noclasses=self.config['noclasses'],
- tab_length=self.markdown.tab_length,
+ tab_length=self.md.tab_length,
use_pygments=self.config['use_pygments']
)
- placeholder = self.markdown.htmlStash.store(code.hilite(),
- safe=True)
+ placeholder = self.md.htmlStash.store(code.hilite())
# Clear codeblock in etree instance
block.clear()
# Change to p element which will later
class CodeHiliteExtension(Extension):
""" Add source code hilighting to markdown codeblocks. """
- def __init__(self, *args, **kwargs):
+ def __init__(self, **kwargs):
# define default configs
self.config = {
'linenums': [None,
'Default: True']
}
- super(CodeHiliteExtension, self).__init__(*args, **kwargs)
+ super(CodeHiliteExtension, self).__init__(**kwargs)
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Add HilitePostprocessor to Markdown instance. """
hiliter = HiliteTreeprocessor(md)
hiliter.config = self.getConfigs()
- md.treeprocessors.add("hilite", hiliter, "<inline")
+ md.treeprocessors.register(hiliter, 'hilite', 30)
md.registerExtension(self)
-def makeExtension(*args, **kwargs):
- return CodeHiliteExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return CodeHiliteExtension(**kwargs)
Adds parsing of Definition Lists to Python-Markdown.
-See <https://pythonhosted.org/Markdown/extensions/definition_lists.html>
+See <https://Python-Markdown.github.io/extensions/definition_lists>
for documentation.
Original code Copyright 2008 [Waylan Limberg](http://achinghead.com)
sibling = self.lastChild(parent)
if not terms and sibling is None:
# This is not a definition item. Most likely a paragraph that
- # starts with a colon at the begining of a document or list.
+ # starts with a colon at the beginning of a document or list.
blocks.insert(0, raw_block)
return False
if not terms and sibling.tag == 'p':
state = 'looselist'
terms = sibling.text.split('\n')
parent.remove(sibling)
- # Aquire new sibling
+ # Acquire new sibling
sibling = self.lastChild(parent)
else:
state = 'list'
class DefListExtension(Extension):
""" Add definition lists to Markdown. """
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Add an instance of DefListProcessor to BlockParser. """
- md.parser.blockprocessors.add('defindent',
- DefListIndentProcessor(md.parser),
- '>indent')
- md.parser.blockprocessors.add('deflist',
- DefListProcessor(md.parser),
- '>ulist')
+ md.parser.blockprocessors.register(DefListIndentProcessor(md.parser), 'defindent', 85)
+ md.parser.blockprocessors.register(DefListProcessor(md.parser), 'deflist', 25)
-def makeExtension(*args, **kwargs):
- return DefListExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return DefListExtension(**kwargs)
variable defined below, but be aware that such changes may be lost
when you upgrade to any future version of Python-Markdown.
-See <https://pythonhosted.org/Markdown/extensions/extra.html>
+See <https://Python-Markdown.github.io/extensions/extra>
for documentation.
Copyright The Python Markdown Project
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'
+ 'fenced_code',
+ 'footnotes',
+ 'attr_list',
+ 'def_list',
+ 'tables',
+ 'abbr'
]
class ExtraExtension(Extension):
""" Add various extensions to Markdown class."""
- def __init__(self, *args, **kwargs):
+ def __init__(self, **kwargs):
""" config is a dumb holder which gets passed to actual ext later. """
- self.config = kwargs.pop('configs', {})
- self.config.update(kwargs)
+ self.config = kwargs
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Register extension instances. """
md.registerExtensions(extensions, self.config)
- if not md.safeMode:
- # Turn on processing of markdown text within raw html
- md.preprocessors['html_block'].markdown_in_raw = True
- md.parser.blockprocessors.add('markdown_block',
- MarkdownInHtmlProcessor(md.parser),
- '_begin')
- md.parser.blockprocessors.tag_counter = -1
- md.parser.blockprocessors.contain_span_tags = re.compile(
- r'^(p|h[1-6]|li|dd|dt|td|th|legend|address)$', re.IGNORECASE)
+ # Turn on processing of markdown text within raw html
+ md.preprocessors['html_block'].markdown_in_raw = True
+ md.parser.blockprocessors.register(
+ MarkdownInHtmlProcessor(md.parser), 'markdown_block', 105
+ )
+ md.parser.blockprocessors.tag_counter = -1
+ md.parser.blockprocessors.contain_span_tags = re.compile(
+ r'^(p|h[1-6]|li|dd|dt|td|th|legend|address)$', re.IGNORECASE)
-def makeExtension(*args, **kwargs):
- return ExtraExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return ExtraExtension(**kwargs)
class MarkdownInHtmlProcessor(BlockProcessor):
block[nest_index[-1][1]:], True) # nest
def run(self, parent, blocks, tail=None, nest=False):
- self._tag_data = self.parser.markdown.htmlStash.tag_data
+ self._tag_data = self.parser.md.htmlStash.tag_data
self.parser.blockprocessors.tag_counter += 1
tag = self._tag_data[self.parser.blockprocessors.tag_counter]
This extension adds Fenced Code Blocks to Python-Markdown.
-See <https://pythonhosted.org/Markdown/extensions/fenced_code_blocks.html>
+See <https://Python-Markdown.github.io/extensions/fenced_code_blocks>
for documentation.
Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/).
class FencedCodeExtension(Extension):
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Add FencedBlockPreprocessor to the Markdown instance. """
md.registerExtension(self)
- md.preprocessors.add('fenced_code_block',
- FencedBlockPreprocessor(md),
- ">normalize_whitespace")
+ md.preprocessors.register(FencedBlockPreprocessor(md), 'fenced_code_block', 25)
class FencedBlockPreprocessor(Preprocessor):
FENCED_BLOCK_RE = re.compile(r'''
(?P<fence>^(?:~{3,}|`{3,}))[ ]* # Opening ``` or ~~~
-(\{?\.?(?P<lang>[a-zA-Z0-9_+-]*))?[ ]* # Optional {, and lang
+(\{?\.?(?P<lang>[\w#.+-]*))?[ ]* # Optional {, and lang
# Optional highlight lines, single- or double-quote-delimited
(hl_lines=(?P<quot>"|')(?P<hl_lines>.*?)(?P=quot))?[ ]*
}?[ ]*\n # Optional closing }
# Check for code hilite extension
if not self.checked_for_codehilite:
- for ext in self.markdown.registeredExtensions:
+ for ext in self.md.registeredExtensions:
if isinstance(ext, CodeHiliteExtension):
self.codehilite_conf = ext.config
break
guess_lang=self.codehilite_conf['guess_lang'][0],
css_class=self.codehilite_conf['css_class'][0],
style=self.codehilite_conf['pygments_style'][0],
+ use_pygments=self.codehilite_conf['use_pygments'][0],
lang=(m.group('lang') or None),
noclasses=self.codehilite_conf['noclasses'][0],
hl_lines=parse_hl_lines(m.group('hl_lines'))
code = self.CODE_WRAP % (lang,
self._escape(m.group('code')))
- placeholder = self.markdown.htmlStash.store(code, safe=True)
+ placeholder = self.md.htmlStash.store(code)
text = '%s\n%s\n%s' % (text[:m.start()],
placeholder,
text[m.end():])
return txt
-def makeExtension(*args, **kwargs):
- return FencedCodeExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return FencedCodeExtension(**kwargs)
Adds footnote handling to Python-Markdown.
-See <https://pythonhosted.org/Markdown/extensions/footnotes.html>
+See <https://Python-Markdown.github.io/extensions/footnotes>
for documentation.
Copyright The Python Markdown Project
from __future__ import unicode_literals
from . import Extension
from ..preprocessors import Preprocessor
-from ..inlinepatterns import Pattern
+from ..inlinepatterns import InlineProcessor
from ..treeprocessors import Treeprocessor
from ..postprocessors import Postprocessor
-from ..util import etree, text_type
-from ..odict import OrderedDict
+from .. import util
+from collections import OrderedDict
import re
+import copy
-FN_BACKLINK_TEXT = "zz1337820767766393qq"
-NBSP_PLACEHOLDER = "qq3936677670287331zz"
+FN_BACKLINK_TEXT = util.STX + "zz1337820767766393qq" + util.ETX
+NBSP_PLACEHOLDER = util.STX + "qq3936677670287331zz" + util.ETX
DEF_RE = re.compile(r'[ ]{0,3}\[\^([^\]]*)\]:\s*(.*)')
TABBED_RE = re.compile(r'((\t)|( ))(.*)')
+RE_REF_ID = re.compile(r'(fnref)(\d+)')
class FootnoteExtension(Extension):
""" Footnote Extension. """
- def __init__(self, *args, **kwargs):
+ def __init__(self, **kwargs):
""" Setup configs. """
self.config = {
"BACKLINK_TEXT":
["↩",
"The text string that links from the footnote "
- "to the reader's place."]
+ "to the reader's place."],
+ "BACKLINK_TITLE":
+ ["Jump back to footnote %d in the text",
+ "The text string used for the title HTML attribute "
+ "of the backlink. %d will be replaced by the "
+ "footnote number."]
}
- super(FootnoteExtension, self).__init__(*args, **kwargs)
+ super(FootnoteExtension, self).__init__(**kwargs)
# In multiple invocations, emit links that don't get tangled.
self.unique_prefix = 0
+ self.found_refs = {}
+ self.used_refs = set()
self.reset()
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Add pieces to Markdown. """
md.registerExtension(self)
self.parser = md.parser
self.md = md
# Insert a preprocessor before ReferencePreprocessor
- md.preprocessors.add(
- "footnote", FootnotePreprocessor(self), "<reference"
- )
+ md.preprocessors.register(FootnotePreprocessor(self), 'footnote', 15)
+
# Insert an inline pattern before ImageReferencePattern
FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah
- md.inlinePatterns.add(
- "footnote", FootnotePattern(FOOTNOTE_RE, self), "<reference"
- )
+ md.inlinePatterns.register(FootnoteInlineProcessor(FOOTNOTE_RE, self), 'footnote', 175)
# Insert a tree-processor that would actually add the footnote div
# This must be before all other treeprocessors (i.e., inline and
# codehilite) so they can run on the the contents of the div.
- md.treeprocessors.add(
- "footnote", FootnoteTreeprocessor(self), "_begin"
- )
- # Insert a postprocessor after amp_substitute oricessor
- md.postprocessors.add(
- "footnote", FootnotePostprocessor(self), ">amp_substitute"
- )
+ md.treeprocessors.register(FootnoteTreeprocessor(self), 'footnote', 50)
+
+ # Insert a tree-processor that will run after inline is done.
+ # In this tree-processor we want to check our duplicate footnote tracker
+ # And add additional backrefs to the footnote pointing back to the
+ # duplicated references.
+ md.treeprocessors.register(FootnotePostTreeprocessor(self), 'footnote-duplicate', 15)
+
+ # Insert a postprocessor after amp_substitute processor
+ md.postprocessors.register(FootnotePostprocessor(self), 'footnote', 25)
def reset(self):
""" Clear footnotes on reset, and prepare for distinct document. """
self.footnotes = OrderedDict()
self.unique_prefix += 1
+ self.found_refs = {}
+ self.used_refs = set()
+
+ def unique_ref(self, reference, found=False):
+ """ Get a unique reference if there are duplicates. """
+ if not found:
+ return reference
+
+ original_ref = reference
+ while reference in self.used_refs:
+ ref, rest = reference.split(self.get_separator(), 1)
+ m = RE_REF_ID.match(ref)
+ if m:
+ reference = '%s%d%s%s' % (m.group(1), int(m.group(2))+1, self.get_separator(), rest)
+ else:
+ reference = '%s%d%s%s' % (ref, 2, self.get_separator(), rest)
+
+ self.used_refs.add(reference)
+ if original_ref in self.found_refs:
+ self.found_refs[original_ref] += 1
+ else:
+ self.found_refs[original_ref] = 1
+ return reference
def findFootnotesPlaceholder(self, root):
""" Return ElementTree Element that contains Footnote placeholder. """
if child.tail:
if child.tail.find(self.getConfig("PLACE_MARKER")) > -1:
return child, element, False
- finder(child)
+ child_res = finder(child)
+ if child_res is not None:
+ return child_res
return None
res = finder(root)
else:
return 'fn%s%s' % (self.get_separator(), id)
- def makeFootnoteRefId(self, id):
+ def makeFootnoteRefId(self, id, found=False):
""" Return footnote back-link id. """
if self.getConfig("UNIQUE_IDS"):
- return 'fnref%s%d-%s' % (self.get_separator(),
- self.unique_prefix, id)
+ return self.unique_ref('fnref%s%d-%s' % (self.get_separator(), self.unique_prefix, id), found)
else:
- return 'fnref%s%s' % (self.get_separator(), id)
+ return self.unique_ref('fnref%s%s' % (self.get_separator(), id), found)
def makeFootnotesDiv(self, root):
""" Return div of footnotes as et Element. """
if not list(self.footnotes.keys()):
return None
- div = etree.Element("div")
+ div = util.etree.Element("div")
div.set('class', 'footnote')
- etree.SubElement(div, "hr")
- ol = etree.SubElement(div, "ol")
+ util.etree.SubElement(div, "hr")
+ ol = util.etree.SubElement(div, "ol")
+ surrogate_parent = util.etree.Element("div")
- for id in self.footnotes.keys():
- li = etree.SubElement(ol, "li")
+ for index, id in enumerate(self.footnotes.keys(), start=1):
+ li = util.etree.SubElement(ol, "li")
li.set("id", self.makeFootnoteId(id))
- self.parser.parseChunk(li, self.footnotes[id])
- backlink = etree.Element("a")
+ # Parse footnote with surrogate parent as li cannot be used.
+ # List block handlers have special logic to deal with li.
+ # When we are done parsing, we will copy everything over to li.
+ self.parser.parseChunk(surrogate_parent, self.footnotes[id])
+ for el in list(surrogate_parent):
+ li.append(el)
+ surrogate_parent.remove(el)
+ backlink = util.etree.Element("a")
backlink.set("href", "#" + self.makeFootnoteRefId(id))
if self.md.output_format not in ['html5', 'xhtml5']:
backlink.set("rev", "footnote") # Invalid in HTML5
backlink.set("class", "footnote-backref")
backlink.set(
"title",
- "Jump back to footnote %d in the text" %
- (self.footnotes.index(id)+1)
+ self.getConfig("BACKLINK_TITLE") % (index)
)
backlink.text = FN_BACKLINK_TEXT
- if li.getchildren():
+ if len(li):
node = li[-1]
if node.tag == "p":
node.text = node.text + NBSP_PLACEHOLDER
node.append(backlink)
else:
- p = etree.SubElement(li, "p")
+ p = util.etree.SubElement(li, "p")
p.append(backlink)
return div
fn, _i = self.detectTabbed(lines[i+1:])
fn.insert(0, m.group(2))
i += _i-1 # skip past footnote
- self.footnotes.setFootnote(m.group(1), "\n".join(fn))
+ footnote = "\n".join(fn)
+ self.footnotes.setFootnote(m.group(1), footnote.rstrip())
+ # Preserve a line for each block to prevent raw HTML indexing issue.
+ # https://github.com/Python-Markdown/markdown/issues/584
+ num_blocks = (len(footnote.split('\n\n')) * 2)
+ newlines.extend([''] * (num_blocks))
else:
newlines.append(lines[i])
if len(lines) > i+1:
if lines[j].strip():
next_line = lines[j]
break
+ else:
+ # Include extreaneous padding to prevent raw HTML
+ # parsing issue: https://github.com/Python-Markdown/markdown/issues/584
+ items.append("")
+ i += 1
else:
break # There is no more text; we are done.
return items, i
-class FootnotePattern(Pattern):
+class FootnoteInlineProcessor(InlineProcessor):
""" InlinePattern for footnote markers in a document's body text. """
def __init__(self, pattern, footnotes):
- super(FootnotePattern, self).__init__(pattern)
+ super(FootnoteInlineProcessor, self).__init__(pattern)
self.footnotes = footnotes
- def handleMatch(self, m):
- id = m.group(2)
+ def handleMatch(self, m, data):
+ id = m.group(1)
if id in self.footnotes.footnotes.keys():
- sup = etree.Element("sup")
- a = etree.SubElement(sup, "a")
- sup.set('id', self.footnotes.makeFootnoteRefId(id))
+ sup = util.etree.Element("sup")
+ a = util.etree.SubElement(sup, "a")
+ sup.set('id', self.footnotes.makeFootnoteRefId(id, found=True))
a.set('href', '#' + self.footnotes.makeFootnoteId(id))
if self.footnotes.md.output_format not in ['html5', 'xhtml5']:
a.set('rel', 'footnote') # invalid in HTML5
a.set('class', 'footnote-ref')
- a.text = text_type(self.footnotes.footnotes.index(id) + 1)
- return sup
+ a.text = util.text_type(list(self.footnotes.footnotes.keys()).index(id) + 1)
+ return sup, m.start(0), m.end(0)
else:
- return None
+ return None, None, None
+
+
+class FootnotePostTreeprocessor(Treeprocessor):
+ """ Amend footnote div with duplicates. """
+
+ def __init__(self, footnotes):
+ self.footnotes = footnotes
+
+ def add_duplicates(self, li, duplicates):
+ """ Adjust current li and add the duplicates: fnref2, fnref3, etc. """
+ for link in li.iter('a'):
+ # Find the link that needs to be duplicated.
+ if link.attrib.get('class', '') == 'footnote-backref':
+ ref, rest = link.attrib['href'].split(self.footnotes.get_separator(), 1)
+ # Duplicate link the number of times we need to
+ # and point the to the appropriate references.
+ links = []
+ for index in range(2, duplicates + 1):
+ sib_link = copy.deepcopy(link)
+ sib_link.attrib['href'] = '%s%d%s%s' % (ref, index, self.footnotes.get_separator(), rest)
+ links.append(sib_link)
+ self.offset += 1
+ # Add all the new duplicate links.
+ el = list(li)[-1]
+ for l in links:
+ el.append(l)
+ break
+
+ def get_num_duplicates(self, li):
+ """ Get the number of duplicate refs of the footnote. """
+ fn, rest = li.attrib.get('id', '').split(self.footnotes.get_separator(), 1)
+ link_id = '%sref%s%s' % (fn, self.footnotes.get_separator(), rest)
+ return self.footnotes.found_refs.get(link_id, 0)
+
+ def handle_duplicates(self, parent):
+ """ Find duplicate footnotes and format and add the duplicates. """
+ for li in list(parent):
+ # Check number of duplicates footnotes and insert
+ # additional links if needed.
+ count = self.get_num_duplicates(li)
+ if count > 1:
+ self.add_duplicates(li, count)
+
+ def run(self, root):
+ """ Crawl the footnote div and add missing duplicate footnotes. """
+ self.offset = 0
+ for div in root.iter('div'):
+ if div.attrib.get('class', '') == 'footnote':
+ # Footnotes shoul be under the first orderd list under
+ # the footnote div. So once we find it, quit.
+ for ol in div.iter('ol'):
+ self.handle_duplicates(ol)
+ break
class FootnoteTreeprocessor(Treeprocessor):
result = self.footnotes.findFootnotesPlaceholder(root)
if result:
child, parent, isText = result
- ind = parent.getchildren().index(child)
+ ind = list(parent).index(child)
if isText:
parent.remove(child)
parent.insert(ind, footnotesDiv)
return text.replace(NBSP_PLACEHOLDER, " ")
-def makeExtension(*args, **kwargs):
+def makeExtension(**kwargs): # pragma: no cover
""" Return an instance of the FootnoteExtension """
- return FootnoteExtension(*args, **kwargs)
+ return FootnoteExtension(**kwargs)
+++ /dev/null
-"""
-HeaderID Extension for Python-Markdown
-======================================
-
-Auto-generate id attributes for HTML headers.
-
-See <https://pythonhosted.org/Markdown/extensions/header_id.html>
-for documentation.
-
-Original code Copyright 2007-2011 [Waylan Limberg](http://achinghead.com/).
-
-All changes Copyright 2011-2014 The Python Markdown Project
-
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
-
-"""
-
-from __future__ import absolute_import
-from __future__ import unicode_literals
-from . import Extension
-from ..treeprocessors import Treeprocessor
-from ..util import parseBoolValue
-from .toc import slugify, unique, stashedHTML2text
-import warnings
-
-
-class HeaderIdTreeprocessor(Treeprocessor):
- """ Assign IDs to headers. """
-
- IDs = set()
-
- def run(self, doc):
- start_level, force_id = self._get_meta()
- slugify = self.config['slugify']
- sep = self.config['separator']
- for elem in doc:
- if elem.tag in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']:
- if force_id:
- if "id" in elem.attrib:
- id = elem.get('id')
- else:
- id = stashedHTML2text(''.join(elem.itertext()), self.md)
- id = slugify(id, sep)
- elem.set('id', unique(id, self.IDs))
- if start_level:
- level = int(elem.tag[-1]) + start_level
- if level > 6:
- level = 6
- elem.tag = 'h%d' % level
-
- def _get_meta(self):
- """ Return meta data suported by this ext as a tuple """
- level = int(self.config['level']) - 1
- force = parseBoolValue(self.config['forceid'])
- if hasattr(self.md, 'Meta'):
- if 'header_level' in self.md.Meta:
- level = int(self.md.Meta['header_level'][0]) - 1
- if 'header_forceid' in self.md.Meta:
- force = parseBoolValue(self.md.Meta['header_forceid'][0])
- return level, force
-
-
-class HeaderIdExtension(Extension):
- def __init__(self, *args, **kwargs):
- # set defaults
- self.config = {
- 'level': ['1', 'Base level for headers.'],
- 'forceid': ['True', 'Force all headers to have an id.'],
- 'separator': ['-', 'Word separator.'],
- 'slugify': [slugify, 'Callable to generate anchors']
- }
-
- super(HeaderIdExtension, self).__init__(*args, **kwargs)
-
- warnings.warn(
- 'The HeaderId Extension is pending deprecation. Use the TOC Extension instead.',
- PendingDeprecationWarning
- )
-
- def extendMarkdown(self, md, md_globals):
- md.registerExtension(self)
- self.processor = HeaderIdTreeprocessor()
- self.processor.md = md
- self.processor.config = self.getConfigs()
- if 'attr_list' in md.treeprocessors.keys():
- # insert after attr_list treeprocessor
- md.treeprocessors.add('headerid', self.processor, '>attr_list')
- else:
- # insert after 'prettify' treeprocessor.
- md.treeprocessors.add('headerid', self.processor, '>prettify')
-
- def reset(self):
- self.processor.IDs = set()
-
-
-def makeExtension(*args, **kwargs):
- return HeaderIdExtension(*args, **kwargs)
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+
+Legacy Attributes Extension
+===========================
+
+An extension to Python Markdown which implements legacy attributes.
+
+Prior to Python-Markdown version 3.0, the Markdown class had an `enable_attributes`
+keyword which was on by default and provided for attributes to be defined for elements
+using the format `{@key=value}`. This extension is provided as a replacement for
+backward compatability. New documents should be authored using attr_lists. However,
+numerious documents exist which have been using the old attribute format for many
+years. This extension can be used to continue to render those documents correctly.
+"""
+
+import re
+from markdown.treeprocessors import Treeprocessor, isString
+from markdown.extensions import Extension
+
+
+ATTR_RE = re.compile(r'\{@([^\}]*)=([^\}]*)}') # {@id=123}
+
+
+class LegacyAttrs(Treeprocessor):
+ def run(self, doc):
+ """Find and set values of attributes ({@key=value}). """
+ for el in doc.iter():
+ alt = el.get('alt', None)
+ if alt is not None:
+ el.set('alt', self.handleAttributes(el, alt))
+ if el.text and isString(el.text):
+ el.text = self.handleAttributes(el, el.text)
+ if el.tail and isString(el.tail):
+ el.tail = self.handleAttributes(el, el.tail)
+
+ def handleAttributes(self, el, txt):
+ """ Set attributes and return text without definitions. """
+ def attributeCallback(match):
+ el.set(match.group(1), match.group(2).replace('\n', ' '))
+ return ATTR_RE.sub(attributeCallback, txt)
+
+
+class LegacyAttrExtension(Extension):
+ def extendMarkdown(self, md):
+ md.treeprocessors.register(LegacyAttrs(md), 'legacyattrs', 15)
+
+
+def makeExtension(**kwargs): # pragma: no cover
+ return LegacyAttrExtension(**kwargs)
--- /dev/null
+'''
+Legacy Em Extension for Python-Markdown
+=======================================
+
+This extention provides legacy behavior for _connected_words_.
+
+Copyright 2015-2018 The Python Markdown Project
+
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
+
+'''
+
+from __future__ import absolute_import
+from __future__ import unicode_literals
+from . import Extension
+from ..inlinepatterns import SimpleTagInlineProcessor
+
+EMPHASIS_RE = r'(\*|_)(.+?)\1'
+STRONG_RE = r'(\*{2}|_{2})(.+?)\1'
+
+
+class LegacyEmExtension(Extension):
+ """ Add legacy_em extension to Markdown class."""
+
+ def extendMarkdown(self, md):
+ """ Modify inline patterns. """
+ md.inlinePatterns.register(SimpleTagInlineProcessor(STRONG_RE, 'strong'), 'strong', 40)
+ md.inlinePatterns.register(SimpleTagInlineProcessor(EMPHASIS_RE, 'em'), 'emphasis', 30)
+ md.inlinePatterns.deregister('strong2')
+ md.inlinePatterns.deregister('emphasis2')
This extension adds Meta Data handling to markdown.
-See <https://pythonhosted.org/Markdown/extensions/meta_data.html>
+See <https://Python-Markdown.github.io/extensions/meta_data>
for documentation.
Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com).
import re
import logging
-try: # pragma: no cover
- import yaml
- try:
- from yaml import CSafeLoader as SafeLoader
- except ImportError:
- from yaml import SafeLoader
-except ImportError:
- yaml = None
-
log = logging.getLogger('MARKDOWN')
# Global Vars
META_RE = re.compile(r'^[ ]{0,3}(?P<key>[A-Za-z0-9_-]+):\s*(?P<value>.*)')
META_MORE_RE = re.compile(r'^[ ]{4,}(?P<value>.*)')
-YAML_BEGIN_RE = re.compile(r'^-{3}(\s.*)?')
-YAML_END_RE = re.compile(r'^(-{3}|\.{3})(\s.*)?')
+BEGIN_RE = re.compile(r'^-{3}(\s.*)?')
+END_RE = re.compile(r'^(-{3}|\.{3})(\s.*)?')
class MetaExtension (Extension):
""" Meta-Data extension for Python-Markdown. """
- def __init__(self, *args, **kwargs):
- self.config = {
- 'yaml': [False, "Parse meta data specified as a "
- "'---' delimited YAML front matter"],
- }
- super(MetaExtension, self).__init__(*args, **kwargs)
-
- def extendMarkdown(self, md, md_globals):
+
+ def extendMarkdown(self, md):
""" Add MetaPreprocessor to Markdown instance. """
- md.preprocessors.add("meta",
- MetaPreprocessor(md, self.getConfigs()),
- ">normalize_whitespace")
+ md.registerExtension(self)
+ self.md = md
+ md.preprocessors.register(MetaPreprocessor(md), 'meta', 27)
+
+ def reset(self):
+ self.md.Meta = {}
class MetaPreprocessor(Preprocessor):
""" Get Meta-Data. """
- def __init__(self, md, config):
- self.config = config
- super(MetaPreprocessor, self).__init__(md)
-
def run(self, lines):
""" Parse Meta-Data and store in Markdown.Meta. """
meta = {}
key = None
- yaml_block = []
- have_yaml = False
- if lines and YAML_BEGIN_RE.match(lines[0]):
- have_yaml = True
+ if lines and BEGIN_RE.match(lines[0]):
lines.pop(0)
- if self.config['yaml'] and not yaml: # pragma: no cover
- log.warning('Document with YAML header, but PyYAML unavailable')
while lines:
line = lines.pop(0)
m1 = META_RE.match(line)
- if line.strip() == '' or have_yaml and YAML_END_RE.match(line):
+ if line.strip() == '' or END_RE.match(line):
break # blank line or end of YAML header - done
- elif have_yaml and self.config['yaml'] and yaml:
- yaml_block.append(line)
- elif m1:
+ if m1:
key = m1.group('key').lower().strip()
value = m1.group('value').strip()
try:
else:
lines.insert(0, line)
break # no meta data - done
- if yaml_block:
- meta = yaml.load('\n'.join(yaml_block), SafeLoader)
- self.markdown.Meta = meta
+ self.md.Meta = meta
return lines
-def makeExtension(*args, **kwargs):
- return MetaExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return MetaExtension(**kwargs)
A Python-Markdown extension to treat newlines as hard breaks; like
GitHub-flavored Markdown does.
-See <https://pythonhosted.org/Markdown/extensions/nl2br.html>
+See <https://Python-Markdown.github.io/extensions/nl2br>
for documentation.
Oringinal code Copyright 2011 [Brian Neal](http://deathofagremmie.com/)
from __future__ import absolute_import
from __future__ import unicode_literals
from . import Extension
-from ..inlinepatterns import SubstituteTagPattern
+from ..inlinepatterns import SubstituteTagInlineProcessor
BR_RE = r'\n'
class Nl2BrExtension(Extension):
- def extendMarkdown(self, md, md_globals):
- br_tag = SubstituteTagPattern(BR_RE, 'br')
- md.inlinePatterns.add('nl', br_tag, '_end')
+ def extendMarkdown(self, md):
+ br_tag = SubstituteTagInlineProcessor(BR_RE, 'br')
+ md.inlinePatterns.register(br_tag, 'nl', 5)
-def makeExtension(*args, **kwargs):
- return Nl2BrExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return Nl2BrExtension(**kwargs)
Modify the behavior of Lists in Python-Markdown to act in a sane manor.
-See <https://pythonhosted.org/Markdown/extensions/sane_lists.html>
+See <https://Python-Markdown.github.io/extensions/sane_lists>
for documentation.
Original code Copyright 2011 [Waylan Limberg](http://achinghead.com)
class SaneOListProcessor(OListProcessor):
- CHILD_RE = re.compile(r'^[ ]{0,3}((\d+\.))[ ]+(.*)')
SIBLING_TAGS = ['ol']
+ LAZY_OL = False
+
+ def __init__(self, parser):
+ super(SaneOListProcessor, self).__init__(parser)
+ self.CHILD_RE = re.compile(r'^[ ]{0,%d}((\d+\.))[ ]+(.*)' %
+ (self.tab_length - 1))
class SaneUListProcessor(UListProcessor):
- CHILD_RE = re.compile(r'^[ ]{0,3}(([*+-]))[ ]+(.*)')
SIBLING_TAGS = ['ul']
+ def __init__(self, parser):
+ super(SaneUListProcessor, self).__init__(parser)
+ self.CHILD_RE = re.compile(r'^[ ]{0,%d}(([*+-]))[ ]+(.*)' %
+ (self.tab_length - 1))
+
class SaneListExtension(Extension):
""" Add sane lists to Markdown. """
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Override existing Processors. """
- md.parser.blockprocessors['olist'] = SaneOListProcessor(md.parser)
- md.parser.blockprocessors['ulist'] = SaneUListProcessor(md.parser)
+ md.parser.blockprocessors.register(SaneOListProcessor(md.parser), 'olist', 40)
+ md.parser.blockprocessors.register(SaneUListProcessor(md.parser), 'ulist', 30)
-def makeExtension(*args, **kwargs):
- return SaneListExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return SaneListExtension(**kwargs)
+++ /dev/null
-'''
-Smart_Strong Extension for Python-Markdown
-==========================================
-
-This extention adds smarter handling of double underscores within words.
-
-See <https://pythonhosted.org/Markdown/extensions/smart_strong.html>
-for documentation.
-
-Original code Copyright 2011 [Waylan Limberg](http://achinghead.com)
-
-All changes Copyright 2011-2014 The Python Markdown Project
-
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
-
-'''
-
-from __future__ import absolute_import
-from __future__ import unicode_literals
-from . import Extension
-from ..inlinepatterns import SimpleTagPattern
-
-SMART_STRONG_RE = r'(?<!\w)(_{2})(?!_)(.+?)(?<!_)\2(?!\w)'
-STRONG_RE = r'(\*{2})(.+?)\2'
-
-
-class SmartEmphasisExtension(Extension):
- """ Add smart_emphasis extension to Markdown class."""
-
- def extendMarkdown(self, md, md_globals):
- """ Modify inline patterns. """
- md.inlinePatterns['strong'] = SimpleTagPattern(STRONG_RE, 'strong')
- md.inlinePatterns.add(
- 'strong2',
- SimpleTagPattern(SMART_STRONG_RE, 'strong'),
- '>emphasis2'
- )
-
-
-def makeExtension(*args, **kwargs):
- return SmartEmphasisExtension(*args, **kwargs)
Adds conversion of ASCII dashes, quotes and ellipses to their HTML
entity equivalents.
-See <https://pythonhosted.org/Markdown/extensions/smarty.html>
+See <https://Python-Markdown.github.io/extensions/smarty>
for documentation.
Author: 2013, Dmitry Shachnev <mitya57@gmail.com>
from __future__ import unicode_literals
from . import Extension
-from ..inlinepatterns import HtmlPattern
-from ..odict import OrderedDict
+from ..inlinepatterns import HtmlInlineProcessor, HTML_RE
from ..treeprocessors import InlineProcessor
+from ..util import Registry, deprecated
# Constants for quote education.
punctClass = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]"""
endOfWordClass = r"[\s.,;:!?)]"
-closeClass = "[^\ \t\r\n\[\{\(\-\u0002\u0003]"
+closeClass = r"[^\ \t\r\n\[\{\(\-\u0002\u0003]"
openingQuotesBase = (
- '(\s' # a whitespace char
- '| ' # or a non-breaking space entity
- '|--' # or dashes
- '|–|—' # or unicode
- '|&[mn]dash;' # or named dash entities
- '|–|—' # or decimal entities
- ')'
+ r'(\s' # a whitespace char
+ r'| ' # or a non-breaking space entity
+ r'|--' # or dashes
+ r'|–|—' # or unicode
+ r'|&[mn]dash;' # or named dash entities
+ r'|–|—' # or decimal entities
+ r')'
)
substitutions = {
doubleQuoteSetsRe = r""""'(?=\w)"""
singleQuoteSetsRe = r"""'"(?=\w)"""
+# Special case for decade abbreviations (the '80s):
+decadeAbbrRe = r"(?<!\w)'(?=\d{2}s)"
+
# Get most opening double quotes:
openingDoubleQuotesRegex = r'%s"(?=\w)' % openingQuotesBase
closingSingleQuotesRegex2 = r"(?<=%s)'(\s|s\b)" % closeClass
# All remaining quotes should be opening ones
-remainingSingleQuotesRegex = "'"
-remainingDoubleQuotesRegex = '"'
+remainingSingleQuotesRegex = r"'"
+remainingDoubleQuotesRegex = r'"'
+
+HTML_STRICT_RE = HTML_RE + r'(?!\>)'
-class SubstituteTextPattern(HtmlPattern):
- def __init__(self, pattern, replace, markdown_instance):
+class SubstituteTextPattern(HtmlInlineProcessor):
+ def __init__(self, pattern, replace, md):
""" Replaces matches with some text. """
- HtmlPattern.__init__(self, pattern)
+ HtmlInlineProcessor.__init__(self, pattern)
self.replace = replace
- self.markdown = markdown_instance
+ self.md = md
+
+ @property
+ @deprecated("Use 'md' instead.")
+ def markdown(self):
+ # TODO: remove this later
+ return self.md
- def handleMatch(self, m):
+ def handleMatch(self, m, data):
result = ''
for part in self.replace:
if isinstance(part, int):
result += m.group(part)
else:
- result += self.markdown.htmlStash.store(part, safe=True)
- return result
+ result += self.md.htmlStash.store(part)
+ return result, m.start(0), m.end(0)
class SmartyExtension(Extension):
- def __init__(self, *args, **kwargs):
+ def __init__(self, **kwargs):
self.config = {
'smart_quotes': [True, 'Educate quotes'],
'smart_angled_quotes': [False, 'Educate angled quotes'],
'smart_ellipses': [True, 'Educate ellipses'],
'substitutions': [{}, 'Overwrite default substitutions'],
}
- super(SmartyExtension, self).__init__(*args, **kwargs)
+ super(SmartyExtension, self).__init__(**kwargs)
self.substitutions = dict(substitutions)
self.substitutions.update(self.getConfig('substitutions', default={}))
- def _addPatterns(self, md, patterns, serie):
+ def _addPatterns(self, md, patterns, serie, priority):
for ind, pattern in enumerate(patterns):
pattern += (md,)
pattern = SubstituteTextPattern(*pattern)
- after = ('>smarty-%s-%d' % (serie, ind - 1) if ind else '_begin')
name = 'smarty-%s-%d' % (serie, ind)
- self.inlinePatterns.add(name, pattern, after)
+ self.inlinePatterns.register(pattern, name, priority-ind)
def educateDashes(self, md):
emDashesPattern = SubstituteTextPattern(
enDashesPattern = SubstituteTextPattern(
r'(?<!-)--(?!-)', (self.substitutions['ndash'],), md
)
- self.inlinePatterns.add('smarty-em-dashes', emDashesPattern, '_begin')
- self.inlinePatterns.add(
- 'smarty-en-dashes', enDashesPattern, '>smarty-em-dashes'
- )
+ self.inlinePatterns.register(emDashesPattern, 'smarty-em-dashes', 50)
+ self.inlinePatterns.register(enDashesPattern, 'smarty-en-dashes', 45)
def educateEllipses(self, md):
ellipsesPattern = SubstituteTextPattern(
r'(?<!\.)\.{3}(?!\.)', (self.substitutions['ellipsis'],), md
)
- self.inlinePatterns.add('smarty-ellipses', ellipsesPattern, '_begin')
+ self.inlinePatterns.register(ellipsesPattern, 'smarty-ellipses', 10)
def educateAngledQuotes(self, md):
leftAngledQuotePattern = SubstituteTextPattern(
rightAngledQuotePattern = SubstituteTextPattern(
r'\>\>', (self.substitutions['right-angle-quote'],), md
)
- self.inlinePatterns.add(
- 'smarty-left-angle-quotes', leftAngledQuotePattern, '_begin'
- )
- self.inlinePatterns.add(
- 'smarty-right-angle-quotes',
- rightAngledQuotePattern,
- '>smarty-left-angle-quotes'
- )
+ self.inlinePatterns.register(leftAngledQuotePattern, 'smarty-left-angle-quotes', 40)
+ self.inlinePatterns.register(rightAngledQuotePattern, 'smarty-right-angle-quotes', 35)
def educateQuotes(self, md):
lsquo = self.substitutions['left-single-quote']
(doubleQuoteStartRe, (rdquo,)),
(doubleQuoteSetsRe, (ldquo + lsquo,)),
(singleQuoteSetsRe, (lsquo + ldquo,)),
- (openingSingleQuotesRegex, (2, lsquo)),
+ (decadeAbbrRe, (rsquo,)),
+ (openingSingleQuotesRegex, (1, lsquo)),
(closingSingleQuotesRegex, (rsquo,)),
- (closingSingleQuotesRegex2, (rsquo, 2)),
+ (closingSingleQuotesRegex2, (rsquo, 1)),
(remainingSingleQuotesRegex, (lsquo,)),
- (openingDoubleQuotesRegex, (2, ldquo)),
+ (openingDoubleQuotesRegex, (1, ldquo)),
(closingDoubleQuotesRegex, (rdquo,)),
(closingDoubleQuotesRegex2, (rdquo,)),
(remainingDoubleQuotesRegex, (ldquo,))
)
- self._addPatterns(md, patterns, 'quotes')
+ self._addPatterns(md, patterns, 'quotes', 30)
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
configs = self.getConfigs()
- self.inlinePatterns = OrderedDict()
+ self.inlinePatterns = Registry()
if configs['smart_ellipses']:
self.educateEllipses(md)
if configs['smart_quotes']:
self.educateQuotes(md)
if configs['smart_angled_quotes']:
self.educateAngledQuotes(md)
+ # Override HTML_RE from inlinepatterns.py so that it does not
+ # process tags with duplicate closing quotes.
+ md.inlinePatterns.register(HtmlInlineProcessor(HTML_STRICT_RE, md), 'html', 90)
if configs['smart_dashes']:
self.educateDashes(md)
inlineProcessor = InlineProcessor(md)
inlineProcessor.inlinePatterns = self.inlinePatterns
- md.treeprocessors.add('smarty', inlineProcessor, '_end')
+ md.treeprocessors.register(inlineProcessor, 'smarty', 2)
md.ESCAPED_CHARS.extend(['"', "'"])
-def makeExtension(*args, **kwargs):
- return SmartyExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return SmartyExtension(**kwargs)
Added parsing of tables to Python-Markdown.
-See <https://pythonhosted.org/Markdown/extensions/tables.html>
+See <https://Python-Markdown.github.io/extensions/tables>
for documentation.
Original code Copyright 2009 [Waylan Limberg](http://achinghead.com)
from . import Extension
from ..blockprocessors import BlockProcessor
from ..util import etree
+import re
+PIPE_NONE = 0
+PIPE_LEFT = 1
+PIPE_RIGHT = 2
class TableProcessor(BlockProcessor):
""" Process Tables. """
+ RE_CODE_PIPES = re.compile(r'(?:(\\\\)|(\\`+)|(`+)|(\\\|)|(\|))')
+ RE_END_BORDER = re.compile(r'(?<!\\)(?:\\\\)*\|$')
+
+ def __init__(self, parser):
+ self.border = False
+ self.separator = ''
+ super(TableProcessor, self).__init__(parser)
+
def test(self, parent, block):
- rows = block.split('\n')
- return (len(rows) > 2 and '|' in rows[0] and
- '|' in rows[1] and '-' in rows[1] and
- rows[1].strip()[0] in ['|', ':', '-'])
+ """
+ Ensure first two rows (column header and separator row) are valid table rows.
+
+ Keep border check and separator row do avoid repeating the work.
+ """
+ is_table = False
+ rows = [row.strip(' ') for row in block.split('\n')]
+ if len(rows) > 1:
+ header0 = rows[0]
+ self.border = PIPE_NONE
+ if header0.startswith('|'):
+ self.border |= PIPE_LEFT
+ if self.RE_END_BORDER.search(header0) is not None:
+ self.border |= PIPE_RIGHT
+ row = self._split_row(header0)
+ row0_len = len(row)
+ is_table = row0_len > 1
+
+ # Each row in a single column table needs at least one pipe.
+ if not is_table and row0_len == 1 and self.border:
+ for index in range(1, len(rows)):
+ is_table = rows[index].startswith('|')
+ if not is_table:
+ is_table = self.RE_END_BORDER.search(rows[index]) is not None
+ if not is_table:
+ break
+
+ if is_table:
+ row = self._split_row(rows[1])
+ is_table = (len(row) == row0_len) and set(''.join(row)) <= set('|:- ')
+ if is_table:
+ self.separator = row
+
+ return is_table
def run(self, parent, blocks):
""" Parse a table block and build table. """
block = blocks.pop(0).split('\n')
- header = block[0].strip()
- seperator = block[1].strip()
- rows = block[2:]
- # Get format type (bordered by pipes or not)
- border = False
- if header.startswith('|'):
- border = True
+ header = block[0].strip(' ')
+ rows = [] if len(block) < 3 else block[2:]
+
# Get alignment of columns
align = []
- for c in self._split_row(seperator, border):
+ for c in self.separator:
+ c = c.strip(' ')
if c.startswith(':') and c.endswith(':'):
align.append('center')
elif c.startswith(':'):
align.append('right')
else:
align.append(None)
+
# Build table
table = etree.SubElement(parent, 'table')
thead = etree.SubElement(table, 'thead')
- self._build_row(header, thead, align, border)
+ self._build_row(header, thead, align)
tbody = etree.SubElement(table, 'tbody')
- for row in rows:
- self._build_row(row.strip(), tbody, align, border)
+ if len(rows) == 0:
+ # Handle empty table
+ self._build_empty_row(tbody, align)
+ else:
+ for row in rows:
+ self._build_row(row.strip(' '), tbody, align)
+
+ def _build_empty_row(self, parent, align):
+ """Build an empty row."""
+ tr = etree.SubElement(parent, 'tr')
+ count = len(align)
+ while count:
+ etree.SubElement(tr, 'td')
+ count -= 1
- def _build_row(self, row, parent, align, border):
+ def _build_row(self, row, parent, align):
""" Given a row of text, build table cells. """
tr = etree.SubElement(parent, 'tr')
tag = 'td'
if parent.tag == 'thead':
tag = 'th'
- cells = self._split_row(row, border)
+ cells = self._split_row(row)
# We use align here rather than cells to ensure every row
# contains the same number of columns.
for i, a in enumerate(align):
c = etree.SubElement(tr, tag)
try:
- c.text = cells[i].strip()
+ c.text = cells[i].strip(' ')
except IndexError: # pragma: no cover
c.text = ""
if a:
c.set('align', a)
- def _split_row(self, row, border):
+ def _split_row(self, row):
""" split a row of text into list of cells. """
- if border:
+ if self.border:
if row.startswith('|'):
row = row[1:]
- if row.endswith('|'):
- row = row[:-1]
- return row.split('|')
+ row = self.RE_END_BORDER.sub('', row)
+ return self._split(row)
+
+ def _split(self, row):
+ """ split a row of text with some code into a list of cells. """
+ elements = []
+ pipes = []
+ tics = []
+ tic_points = []
+ tic_region = []
+ good_pipes = []
+
+ # Parse row
+ # Throw out \\, and \|
+ for m in self.RE_CODE_PIPES.finditer(row):
+ # Store ` data (len, start_pos, end_pos)
+ if m.group(2):
+ # \`+
+ # Store length of each tic group: subtract \
+ tics.append(len(m.group(2)) - 1)
+ # Store start of group, end of group, and escape length
+ tic_points.append((m.start(2), m.end(2) - 1, 1))
+ elif m.group(3):
+ # `+
+ # Store length of each tic group
+ tics.append(len(m.group(3)))
+ # Store start of group, end of group, and escape length
+ tic_points.append((m.start(3), m.end(3) - 1, 0))
+ # Store pipe location
+ elif m.group(5):
+ pipes.append(m.start(5))
+
+ # Pair up tics according to size if possible
+ # Subtract the escape length *only* from the opening.
+ # Walk through tic list and see if tic has a close.
+ # Store the tic region (start of region, end of region).
+ pos = 0
+ tic_len = len(tics)
+ while pos < tic_len:
+ try:
+ tic_size = tics[pos] - tic_points[pos][2]
+ if tic_size == 0:
+ raise ValueError
+ index = tics[pos + 1:].index(tic_size) + 1
+ tic_region.append((tic_points[pos][0], tic_points[pos + index][1]))
+ pos += index + 1
+ except ValueError:
+ pos += 1
+
+ # Resolve pipes. Check if they are within a tic pair region.
+ # Walk through pipes comparing them to each region.
+ # - If pipe position is less that a region, it isn't in a region
+ # - If it is within a region, we don't want it, so throw it out
+ # - If we didn't throw it out, it must be a table pipe
+ for pipe in pipes:
+ throw_out = False
+ for region in tic_region:
+ if pipe < region[0]:
+ # Pipe is not in a region
+ break
+ elif region[0] <= pipe <= region[1]:
+ # Pipe is within a code region. Throw it out.
+ throw_out = True
+ break
+ if not throw_out:
+ good_pipes.append(pipe)
+
+ # Split row according to table delimeters.
+ pos = 0
+ for pipe in good_pipes:
+ elements.append(row[pos:pipe])
+ pos = pipe + 1
+ elements.append(row[pos:])
+ return elements
class TableExtension(Extension):
""" Add tables to Markdown. """
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
""" Add an instance of TableProcessor to BlockParser. """
- md.parser.blockprocessors.add('table',
- TableProcessor(md.parser),
- '<hashheader')
+ if '|' not in md.ESCAPED_CHARS:
+ md.ESCAPED_CHARS.append('|')
+ md.parser.blockprocessors.register(TableProcessor(md.parser), 'table', 75)
-def makeExtension(*args, **kwargs):
- return TableExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return TableExtension(**kwargs)
Table of Contents Extension for Python-Markdown
===============================================
-See <https://pythonhosted.org/Markdown/extensions/toc.html>
+See <https://Python-Markdown.github.io/extensions/toc>
for documentation.
Oringinal code Copyright 2008 [Jack Miller](http://codezen.org)
from __future__ import unicode_literals
from . import Extension
from ..treeprocessors import Treeprocessor
-from ..util import etree, parseBoolValue, AMP_SUBSTITUTE, HTML_PLACEHOLDER_RE
+from ..util import etree, parseBoolValue, AMP_SUBSTITUTE, HTML_PLACEHOLDER_RE, string_type
import re
import unicodedata
def slugify(value, separator):
""" Slugify a string, to make it URL friendly. """
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore')
- value = re.sub('[^\w\s-]', '', value.decode('ascii')).strip().lower()
- return re.sub('[%s\s]+' % separator, separator, value)
+ value = re.sub(r'[^\w\s-]', '', value.decode('ascii')).strip().lower()
+ return re.sub(r'[%s\s]+' % separator, separator, value)
IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
def _html_sub(m):
""" Substitute raw html with plain text. """
try:
- raw, safe = md.htmlStash.rawHtmlBlocks[int(m.group(1))]
+ raw = md.htmlStash.rawHtmlBlocks[int(m.group(1))]
except (IndexError, TypeError): # pragma: no cover
return m.group(0)
- if md.safeMode and not safe: # pragma: no cover
- return ''
# Strip out tags and entities - leaveing text
return re.sub(r'(<[^>]+>)|(&[\#a-zA-Z0-9]+;)', '', raw)
self.use_permalinks = parseBoolValue(config["permalink"], False)
if self.use_permalinks is None:
self.use_permalinks = config["permalink"]
-
self.header_rgx = re.compile("[Hh][123456]")
+ self.toc_depth = config["toc_depth"]
+
+ def iterparent(self, node):
+ ''' Iterator wrapper to get allowed parent and child all at once. '''
- def iterparent(self, root):
- ''' Iterator wrapper to get parent and child all at once. '''
- for parent in root.iter():
- for child in parent:
- yield parent, child
+ # We do not allow the marker inside a header as that
+ # would causes an enless loop of placing a new TOC
+ # inside previously generated TOC.
+ for child in node:
+ if not self.header_rgx.match(child.tag) and child.tag not in ['pre', 'code']:
+ yield node, child
+ for p, c in self.iterparent(child):
+ yield p, c
def replace_marker(self, root, elem):
''' Replace marker with elem. '''
# To keep the output from screwing up the
# validation by putting a <div> inside of a <p>
# we actually replace the <p> in its entirety.
- # We do not allow the marker inside a header as that
- # would causes an enless loop of placing a new TOC
- # inside previously generated TOC.
- if c.text and c.text.strip() == self.marker and \
- not self.header_rgx.match(c.tag) and c.tag not in ['pre', 'code']:
+ if c.text and c.text.strip() == self.marker:
for i in range(len(p)):
if p[i] == c:
p[i] = elem
c.text = ""
for elem in c:
anchor.append(elem)
- c.remove(elem)
+ while len(c):
+ c.remove(c[0])
c.append(anchor)
def add_permalink(self, c, elem_id):
return ul
build_etree_ul(toc_list, div)
- prettify = self.markdown.treeprocessors.get('prettify')
- if prettify:
- prettify.run(div)
+
+ if 'prettify' in self.md.treeprocessors:
+ self.md.treeprocessors['prettify'].run(div)
+
return div
def run(self, doc):
toc_tokens = []
for el in doc.iter():
- if self.header_rgx.match(el.tag):
+ if isinstance(el.tag, string_type) and self.header_rgx.match(el.tag):
self.set_level(el)
+ if int(el.tag[-1]) > int(self.toc_depth):
+ continue
text = ''.join(el.itertext()).strip()
# Do not override pre-existing ids
if "id" not in el.attrib:
- innertext = stashedHTML2text(text, self.markdown)
+ innertext = stashedHTML2text(text, self.md)
el.attrib["id"] = unique(self.slugify(innertext, self.sep), used_ids)
toc_tokens.append({
'level': int(el.tag[-1]),
'id': el.attrib["id"],
- 'name': text
+ 'name': el.attrib.get('data-toc-label', text)
})
+ # Remove the data-toc-label attribute as it is no longer needed
+ if 'data-toc-label' in el.attrib:
+ del el.attrib['data-toc-label']
+
if self.use_anchors:
self.add_anchor(el, el.attrib["id"])
if self.use_permalinks:
self.add_permalink(el, el.attrib["id"])
- div = self.build_toc_div(nest_toc_tokens(toc_tokens))
+ toc_tokens = nest_toc_tokens(toc_tokens)
+ div = self.build_toc_div(toc_tokens)
if self.marker:
self.replace_marker(doc, div)
# serialize and attach to markdown instance.
- toc = self.markdown.serializer(div)
- for pp in self.markdown.postprocessors.values():
+ toc = self.md.serializer(div)
+ for pp in self.md.postprocessors:
toc = pp.run(toc)
- self.markdown.toc = toc
+ self.md.toc_tokens = toc_tokens
+ self.md.toc = toc
class TocExtension(Extension):
TreeProcessorClass = TocTreeprocessor
- def __init__(self, *args, **kwargs):
+ def __init__(self, **kwargs):
self.config = {
"marker": ['[TOC]',
'Text to find and replace with Table of Contents - '
"slugify": [slugify,
"Function to generate anchors based on header text - "
"Defaults to the headerid ext's slugify function."],
- 'separator': ['-', 'Word separator. Defaults to "-".']
+ 'separator': ['-', 'Word separator. Defaults to "-".'],
+ "toc_depth": [6,
+ "Define up to which section level n (<h1>..<hn>) to "
+ "include in the TOC"]
}
- super(TocExtension, self).__init__(*args, **kwargs)
+ super(TocExtension, self).__init__(**kwargs)
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
md.registerExtension(self)
self.md = md
self.reset()
# by the header id extension) if both are used. Same goes for
# attr_list extension. This must come last because we don't want
# to redefine ids after toc is created. But we do want toc prettified.
- md.treeprocessors.add("toc", tocext, "_end")
+ md.treeprocessors.register(tocext, 'toc', 5)
def reset(self):
self.md.toc = ''
+ self.md.toc_tokens = []
-def makeExtension(*args, **kwargs):
- return TocExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return TocExtension(**kwargs)
Converts [[WikiLinks]] to relative links.
-See <https://pythonhosted.org/Markdown/extensions/wikilinks.html>
+See <https://Python-Markdown.github.io/extensions/wikilinks>
for documentation.
Original code Copyright [Waylan Limberg](http://achinghead.com/).
from __future__ import absolute_import
from __future__ import unicode_literals
from . import Extension
-from ..inlinepatterns import Pattern
+from ..inlinepatterns import InlineProcessor
from ..util import etree
import re
class WikiLinkExtension(Extension):
- def __init__(self, *args, **kwargs):
+ def __init__(self, **kwargs):
self.config = {
'base_url': ['/', 'String to append to beginning or URL.'],
'end_url': ['/', 'String to append to end of URL.'],
'build_url': [build_url, 'Callable formats URL from label.'],
}
- super(WikiLinkExtension, self).__init__(*args, **kwargs)
+ super(WikiLinkExtension, self).__init__(**kwargs)
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
self.md = md
# append to end of inline patterns
WIKILINK_RE = r'\[\[([\w0-9_ -]+)\]\]'
- wikilinkPattern = WikiLinks(WIKILINK_RE, self.getConfigs())
+ wikilinkPattern = WikiLinksInlineProcessor(WIKILINK_RE, self.getConfigs())
wikilinkPattern.md = md
- md.inlinePatterns.add('wikilink', wikilinkPattern, "<not_strong")
+ md.inlinePatterns.register(wikilinkPattern, 'wikilink', 75)
-class WikiLinks(Pattern):
+class WikiLinksInlineProcessor(InlineProcessor):
def __init__(self, pattern, config):
- super(WikiLinks, self).__init__(pattern)
+ super(WikiLinksInlineProcessor, self).__init__(pattern)
self.config = config
- def handleMatch(self, m):
- if m.group(2).strip():
+ def handleMatch(self, m, data):
+ if m.group(1).strip():
base_url, end_url, html_class = self._getMeta()
- label = m.group(2).strip()
+ label = m.group(1).strip()
url = self.config['build_url'](label, base_url, end_url)
a = etree.Element('a')
a.text = label
a.set('class', html_class)
else:
a = ''
- return a
+ return a, m.start(0), m.end(0)
def _getMeta(self):
""" Return meta data or config data. """
return base_url, end_url, html_class
-def makeExtension(*args, **kwargs):
- return WikiLinkExtension(*args, **kwargs)
+def makeExtension(**kwargs): # pragma: no cover
+ return WikiLinkExtension(**kwargs)
+# -*- coding: utf-8 -*-
"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+
INLINE PATTERNS
=============================================================================
from __future__ import absolute_import
from __future__ import unicode_literals
from . import util
-from . import odict
import re
-try: # pragma: no cover
- from urllib.parse import urlparse, urlunparse
-except ImportError: # pragma: no cover
- from urlparse import urlparse, urlunparse
try: # pragma: no cover
from html import entities
except ImportError: # pragma: no cover
import htmlentitydefs as entities
-def build_inlinepatterns(md_instance, **kwargs):
+def build_inlinepatterns(md, **kwargs):
""" Build the default set of inline patterns for Markdown. """
- inlinePatterns = odict.OrderedDict()
- inlinePatterns["backtick"] = BacktickPattern(BACKTICK_RE)
- inlinePatterns["escape"] = EscapePattern(ESCAPE_RE, md_instance)
- inlinePatterns["reference"] = ReferencePattern(REFERENCE_RE, md_instance)
- inlinePatterns["link"] = LinkPattern(LINK_RE, md_instance)
- inlinePatterns["image_link"] = ImagePattern(IMAGE_LINK_RE, md_instance)
- inlinePatterns["image_reference"] = ImageReferencePattern(
- IMAGE_REFERENCE_RE, md_instance
+ inlinePatterns = util.Registry()
+ inlinePatterns.register(BacktickInlineProcessor(BACKTICK_RE), 'backtick', 190)
+ inlinePatterns.register(EscapeInlineProcessor(ESCAPE_RE, md), 'escape', 180)
+ inlinePatterns.register(ReferenceInlineProcessor(REFERENCE_RE, md), 'reference', 170)
+ inlinePatterns.register(LinkInlineProcessor(LINK_RE, md), 'link', 160)
+ inlinePatterns.register(ImageInlineProcessor(IMAGE_LINK_RE, md), 'image_link', 150)
+ inlinePatterns.register(
+ ImageReferenceInlineProcessor(IMAGE_REFERENCE_RE, md), 'image_reference', 140
)
- inlinePatterns["short_reference"] = ReferencePattern(
- SHORT_REF_RE, md_instance
+ inlinePatterns.register(
+ ShortReferenceInlineProcessor(REFERENCE_RE, md), 'short_reference', 130
)
- inlinePatterns["autolink"] = AutolinkPattern(AUTOLINK_RE, md_instance)
- inlinePatterns["automail"] = AutomailPattern(AUTOMAIL_RE, md_instance)
- inlinePatterns["linebreak"] = SubstituteTagPattern(LINE_BREAK_RE, 'br')
- if md_instance.safeMode != 'escape':
- inlinePatterns["html"] = HtmlPattern(HTML_RE, md_instance)
- inlinePatterns["entity"] = HtmlPattern(ENTITY_RE, md_instance)
- inlinePatterns["not_strong"] = SimpleTextPattern(NOT_STRONG_RE)
- inlinePatterns["em_strong"] = DoubleTagPattern(EM_STRONG_RE, 'strong,em')
- inlinePatterns["strong_em"] = DoubleTagPattern(STRONG_EM_RE, 'em,strong')
- inlinePatterns["strong"] = SimpleTagPattern(STRONG_RE, 'strong')
- inlinePatterns["emphasis"] = SimpleTagPattern(EMPHASIS_RE, 'em')
- if md_instance.smart_emphasis:
- inlinePatterns["emphasis2"] = SimpleTagPattern(SMART_EMPHASIS_RE, 'em')
- else:
- inlinePatterns["emphasis2"] = SimpleTagPattern(EMPHASIS_2_RE, 'em')
+ inlinePatterns.register(AutolinkInlineProcessor(AUTOLINK_RE, md), 'autolink', 120)
+ inlinePatterns.register(AutomailInlineProcessor(AUTOMAIL_RE, md), 'automail', 110)
+ inlinePatterns.register(SubstituteTagInlineProcessor(LINE_BREAK_RE, 'br'), 'linebreak', 100)
+ inlinePatterns.register(HtmlInlineProcessor(HTML_RE, md), 'html', 90)
+ inlinePatterns.register(HtmlInlineProcessor(ENTITY_RE, md), 'entity', 80)
+ inlinePatterns.register(SimpleTextInlineProcessor(NOT_STRONG_RE), 'not_strong', 70)
+ inlinePatterns.register(DoubleTagInlineProcessor(EM_STRONG_RE, 'strong,em'), 'em_strong', 60)
+ inlinePatterns.register(DoubleTagInlineProcessor(STRONG_EM_RE, 'em,strong'), 'strong_em', 50)
+ inlinePatterns.register(SimpleTagInlineProcessor(STRONG_RE, 'strong'), 'strong', 40)
+ inlinePatterns.register(SimpleTagInlineProcessor(EMPHASIS_RE, 'em'), 'emphasis', 30)
+ inlinePatterns.register(SimpleTagInlineProcessor(SMART_STRONG_RE, 'strong'), 'strong2', 20)
+ inlinePatterns.register(SimpleTagInlineProcessor(SMART_EMPHASIS_RE, 'em'), 'emphasis2', 10)
return inlinePatterns
+
"""
The actual regular expressions for patterns
-----------------------------------------------------------------------------
"""
-NOBRACKET = r'[^\]\[]*'
-BRK = (
- r'\[(' +
- (NOBRACKET + r'(\[')*6 +
- (NOBRACKET + r'\])*')*6 +
- NOBRACKET + r')\]'
-)
NOIMG = r'(?<!\!)'
# `e=f()` or ``e=f("`")``
-BACKTICK_RE = r'(?<!\\)(`+)(.+?)(?<!`)\2(?!`)'
+BACKTICK_RE = r'(?:(?<!\\)((?:\\{2})+)(?=`+)|(?<!\\)(`+)(.+?)(?<!`)\2(?!`))'
# \<
ESCAPE_RE = r'\\(.)'
# *emphasis*
-EMPHASIS_RE = r'(\*)([^\*]+)\2'
+EMPHASIS_RE = r'(\*)([^\*]+)\1'
# **strong**
-STRONG_RE = r'(\*{2}|_{2})(.+?)\2'
-
-# ***strongem*** or ***em*strong**
-EM_STRONG_RE = r'(\*|_)\2{2}(.+?)\2(.*?)\2{2}'
+STRONG_RE = r'(\*{2})(.+?)\1'
-# ***strong**em*
-STRONG_EM_RE = r'(\*|_)\2{2}(.+?)\2{2}(.*?)\2'
+# __smart__strong__
+SMART_STRONG_RE = r'(?<!\w)(_{2})(?!_)(.+?)(?<!_)\1(?!\w)'
# _smart_emphasis_
-SMART_EMPHASIS_RE = r'(?<!\w)(_)(?!_)(.+?)(?<!_)\2(?!\w)'
+SMART_EMPHASIS_RE = r'(?<!\w)(_)(?!_)(.+?)(?<!_)\1(?!\w)'
-# _emphasis_
-EMPHASIS_2_RE = r'(_)(.+?)\2'
+# ***strongem*** or ***em*strong**
+EM_STRONG_RE = r'(\*|_)\1{2}(.+?)\1(.*?)\1{2}'
+
+# ***strong**em*
+STRONG_EM_RE = r'(\*|_)\1{2}(.+?)\1{2}(.*?)\1'
# [text](url) or [text](<url>) or [text](url "title")
-LINK_RE = NOIMG + BRK + \
- r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)'''
+LINK_RE = NOIMG + r'\['
#  or 
-IMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^")]+"[^"]*"|[^\)]*))\)'
+IMAGE_LINK_RE = r'\!\['
# [Google][3]
-REFERENCE_RE = NOIMG + BRK + r'\s?\[([^\]]*)\]'
-
-# [Google]
-SHORT_REF_RE = NOIMG + r'\[([^\]]+)\]'
+REFERENCE_RE = LINK_RE
# ![alt text][2]
-IMAGE_REFERENCE_RE = r'\!' + BRK + '\s?\[([^\]]*)\]'
+IMAGE_REFERENCE_RE = IMAGE_LINK_RE
# stand-alone * or _
NOT_STRONG_RE = r'((^| )(\*|_)( |$))'
HTML_RE = r'(\<([a-zA-Z/][^\>]*?|\!--.*?--)\>)'
# &
-ENTITY_RE = r'(&[\#a-zA-Z0-9]*;)'
+ENTITY_RE = r'(&(?:\#[0-9]+|[a-zA-Z0-9]+);)'
# two spaces at end of line
LINE_BREAK_RE = r' \n'
return string
-ATTR_RE = re.compile("\{@([^\}]*)=([^\}]*)}") # {@id=123}
-
-
-def handleAttributes(text, parent):
- """Set values of an element based on attribute definitions ({@id=123})."""
- def attributeCallback(match):
- parent.set(match.group(1), match.group(2).replace('\n', ' '))
- return ATTR_RE.sub(attributeCallback, text)
-
-
"""
The pattern classes
-----------------------------------------------------------------------------
"""
-class Pattern(object):
+class Pattern(object): # pragma: no cover
"""Base class that inline patterns subclass. """
- def __init__(self, pattern, markdown_instance=None):
+ ANCESTOR_EXCLUDES = tuple()
+
+ def __init__(self, pattern, md=None):
"""
Create an instant of an inline pattern.
"""
self.pattern = pattern
- self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern,
+ self.compiled_re = re.compile(r"^(.*?)%s(.*)$" % pattern,
re.DOTALL | re.UNICODE)
- # Api for Markdown to pass safe_mode into instance
- self.safe_mode = False
- if markdown_instance:
- self.markdown = markdown_instance
+ self.md = md
+
+ @property
+ @util.deprecated("Use 'md' instead.")
+ def markdown(self):
+ # TODO: remove this later
+ return self.md
def getCompiledRegExp(self):
""" Return a compiled regular expression. """
def unescape(self, text):
""" Return unescaped text given text with an inline placeholder. """
try:
- stash = self.markdown.treeprocessors['inline'].stashed_nodes
+ stash = self.md.treeprocessors['inline'].stashed_nodes
except KeyError: # pragma: no cover
return text
- def itertext(el): # pragma: no cover
- ' Reimplement Element.itertext for older python versions '
- tag = el.tag
- if not isinstance(tag, util.string_type) and tag is not None:
- return
- if el.text:
- yield el.text
- for e in el:
- for s in itertext(e):
- yield s
- if e.tail:
- yield e.tail
-
def get_stash(m):
id = m.group(1)
if id in stash:
return value
else:
# An etree Element - return text content only
- return ''.join(itertext(value))
+ return ''.join(value.itertext())
return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
-class SimpleTextPattern(Pattern):
+class InlineProcessor(Pattern):
+ """
+ Base class that inline patterns subclass.
+
+ This is the newer style inline processor that uses a more
+ efficient and flexible search approach.
+ """
+
+ def __init__(self, pattern, md=None):
+ """
+ Create an instant of an inline pattern.
+
+ Keyword arguments:
+
+ * pattern: A regular expression that matches a pattern
+
+ """
+ self.pattern = pattern
+ self.compiled_re = re.compile(pattern, re.DOTALL | re.UNICODE)
+
+ # Api for Markdown to pass safe_mode into instance
+ self.safe_mode = False
+ self.md = md
+
+ def handleMatch(self, m, data):
+ """Return a ElementTree element from the given match and the
+ start and end index of the matched text.
+
+ If `start` and/or `end` are returned as `None`, it will be
+ assumed that the processor did not find a valid region of text.
+
+ Subclasses should override this method.
+
+ Keyword arguments:
+
+ * m: A re match object containing a match of the pattern.
+ * data: The buffer current under analysis
+
+ Returns:
+
+ * el: The ElementTree element, text or None.
+ * start: The start of the region that has been matched or None.
+ * end: The end of the region that has been matched or None.
+
+ """
+ pass # pragma: no cover
+
+
+class SimpleTextPattern(Pattern): # pragma: no cover
""" Return a simple text of group(2) of a Pattern. """
def handleMatch(self, m):
return m.group(2)
-class EscapePattern(Pattern):
+class SimpleTextInlineProcessor(InlineProcessor):
+ """ Return a simple text of group(1) of a Pattern. """
+ def handleMatch(self, m, data):
+ return m.group(1), m.start(0), m.end(0)
+
+
+class EscapeInlineProcessor(InlineProcessor):
""" Return an escaped character. """
- def handleMatch(self, m):
- char = m.group(2)
- if char in self.markdown.ESCAPED_CHARS:
- return '%s%s%s' % (util.STX, ord(char), util.ETX)
+ def handleMatch(self, m, data):
+ char = m.group(1)
+ if char in self.md.ESCAPED_CHARS:
+ return '%s%s%s' % (util.STX, ord(char), util.ETX), m.start(0), m.end(0)
else:
- return None
+ return None, m.start(0), m.end(0)
-class SimpleTagPattern(Pattern):
+class SimpleTagPattern(Pattern): # pragma: no cover
"""
Return element of type `tag` with a text attribute of group(3)
of a Pattern.
return el
-class SubstituteTagPattern(SimpleTagPattern):
+class SimpleTagInlineProcessor(InlineProcessor):
+ """
+ Return element of type `tag` with a text attribute of group(2)
+ of a Pattern.
+
+ """
+ def __init__(self, pattern, tag):
+ InlineProcessor.__init__(self, pattern)
+ self.tag = tag
+
+ def handleMatch(self, m, data):
+ el = util.etree.Element(self.tag)
+ el.text = m.group(2)
+ return el, m.start(0), m.end(0)
+
+
+class SubstituteTagPattern(SimpleTagPattern): # pragma: no cover
""" Return an element of type `tag` with no children. """
def handleMatch(self, m):
return util.etree.Element(self.tag)
-class BacktickPattern(Pattern):
+class SubstituteTagInlineProcessor(SimpleTagInlineProcessor):
+ """ Return an element of type `tag` with no children. """
+ def handleMatch(self, m, data):
+ return util.etree.Element(self.tag), m.start(0), m.end(0)
+
+
+class BacktickInlineProcessor(InlineProcessor):
""" Return a `<code>` element containing the matching text. """
def __init__(self, pattern):
- Pattern.__init__(self, pattern)
- self.tag = "code"
-
- def handleMatch(self, m):
- el = util.etree.Element(self.tag)
- el.text = util.AtomicString(m.group(3).strip())
- return el
+ InlineProcessor.__init__(self, pattern)
+ self.ESCAPED_BSLASH = '%s%s%s' % (util.STX, ord('\\'), util.ETX)
+ self.tag = 'code'
+
+ def handleMatch(self, m, data):
+ if m.group(3):
+ el = util.etree.Element(self.tag)
+ el.text = util.AtomicString(util.code_escape(m.group(3).strip()))
+ return el, m.start(0), m.end(0)
+ else:
+ return m.group(1).replace('\\\\', self.ESCAPED_BSLASH), m.start(0), m.end(0)
-class DoubleTagPattern(SimpleTagPattern):
+class DoubleTagPattern(SimpleTagPattern): # pragma: no cover
"""Return a ElementTree element nested in tag2 nested in tag1.
Useful for strong emphasis etc.
return el1
-class HtmlPattern(Pattern):
+class DoubleTagInlineProcessor(SimpleTagInlineProcessor):
+ """Return a ElementTree element nested in tag2 nested in tag1.
+
+ Useful for strong emphasis etc.
+
+ """
+ def handleMatch(self, m, data):
+ tag1, tag2 = self.tag.split(",")
+ el1 = util.etree.Element(tag1)
+ el2 = util.etree.SubElement(el1, tag2)
+ el2.text = m.group(2)
+ if len(m.groups()) == 3:
+ el2.tail = m.group(3)
+ return el1, m.start(0), m.end(0)
+
+
+class HtmlInlineProcessor(InlineProcessor):
""" Store raw inline html and return a placeholder. """
- def handleMatch(self, m):
- rawhtml = self.unescape(m.group(2))
- place_holder = self.markdown.htmlStash.store(rawhtml)
- return place_holder
+ def handleMatch(self, m, data):
+ rawhtml = self.unescape(m.group(1))
+ place_holder = self.md.htmlStash.store(rawhtml)
+ return place_holder, m.start(0), m.end(0)
def unescape(self, text):
""" Return unescaped text given text with an inline placeholder. """
try:
- stash = self.markdown.treeprocessors['inline'].stashed_nodes
+ stash = self.md.treeprocessors['inline'].stashed_nodes
except KeyError: # pragma: no cover
return text
value = stash.get(id)
if value is not None:
try:
- return self.markdown.serializer(value)
- except:
- return '\%s' % value
+ return self.md.serializer(value)
+ except Exception:
+ return r'\%s' % value
return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
-class LinkPattern(Pattern):
+class LinkInlineProcessor(InlineProcessor):
""" Return a link element from the given match. """
- def handleMatch(self, m):
- el = util.etree.Element("a")
- el.text = m.group(2)
- title = m.group(13)
- href = m.group(9)
+ RE_LINK = re.compile(r'''\(\s*(?:(<.*?>)\s*(?:(['"])(.*?)\2\s*)?\))?''', re.DOTALL | re.UNICODE)
+ RE_TITLE_CLEAN = re.compile(r'\s')
- if href:
- if href[0] == "<":
- href = href[1:-1]
- el.set("href", self.sanitize_url(self.unescape(href.strip())))
- else:
- el.set("href", "")
+ def handleMatch(self, m, data):
+ text, index, handled = self.getText(data, m.end(0))
- if title:
- title = dequote(self.unescape(title))
- el.set("title", title)
- return el
+ if not handled:
+ return None, None, None
- def sanitize_url(self, url):
- """
- Sanitize a url against xss attacks in "safe_mode".
-
- Rather than specifically blacklisting `javascript:alert("XSS")` and all
- its aliases (see <http://ha.ckers.org/xss.html>), we whitelist known
- safe url formats. Most urls contain a network location, however some
- are known not to (i.e.: mailto links). Script urls do not contain a
- location. Additionally, for `javascript:...`, the scheme would be
- "javascript" but some aliases will appear to `urlparse()` to have no
- scheme. On top of that relative links (i.e.: "foo/bar.html") have no
- scheme. Therefore we must check "path", "parameters", "query" and
- "fragment" for any literal colons. We don't check "scheme" for colons
- because it *should* never have any and "netloc" must allow the form:
- `username:password@host:port`.
+ href, title, index, handled = self.getLink(data, index)
+ if not handled:
+ return None, None, None
- """
- if not self.markdown.safeMode:
- # Return immediately bipassing parsing.
- return url
+ el = util.etree.Element("a")
+ el.text = text
- try:
- scheme, netloc, path, params, query, fragment = url = urlparse(url)
- except ValueError: # pragma: no cover
- # Bad url - so bad it couldn't be parsed.
- return ''
+ el.set("href", href)
- locless_schemes = ['', 'mailto', 'news']
- allowed_schemes = locless_schemes + ['http', 'https', 'ftp', 'ftps']
- if scheme not in allowed_schemes:
- # Not a known (allowed) scheme. Not safe.
- return ''
+ if title is not None:
+ el.set("title", title)
- if netloc == '' and scheme not in locless_schemes: # pragma: no cover
- # This should not happen. Treat as suspect.
- return ''
+ return el, m.start(0), index
+
+ def getLink(self, data, index):
+ """Parse data between `()` of `[Text]()` allowing recursive `()`. """
+
+ href = ''
+ title = None
+ handled = False
+
+ m = self.RE_LINK.match(data, pos=index)
+ if m and m.group(1):
+ # Matches [Text](<link> "title")
+ href = m.group(1)[1:-1].strip()
+ if m.group(3):
+ title = m.group(3)
+ index = m.end(0)
+ handled = True
+ elif m:
+ # Track bracket nesting and index in string
+ bracket_count = 1
+ backtrack_count = 1
+ start_index = m.end()
+ index = start_index
+ last_bracket = -1
+
+ # Primary (first found) quote tracking.
+ quote = None
+ start_quote = -1
+ exit_quote = -1
+ ignore_matches = False
+
+ # Secondary (second found) quote tracking.
+ alt_quote = None
+ start_alt_quote = -1
+ exit_alt_quote = -1
+
+ # Track last character
+ last = ''
+
+ for pos in util.iterrange(index, len(data)):
+ c = data[pos]
+ if c == '(':
+ # Count nested (
+ # Don't increment the bracket count if we are sure we're in a title.
+ if not ignore_matches:
+ bracket_count += 1
+ elif backtrack_count > 0:
+ backtrack_count -= 1
+ elif c == ')':
+ # Match nested ) to (
+ # Don't decrement if we are sure we are in a title that is unclosed.
+ if ((exit_quote != -1 and quote == last) or (exit_alt_quote != -1 and alt_quote == last)):
+ bracket_count = 0
+ elif not ignore_matches:
+ bracket_count -= 1
+ elif backtrack_count > 0:
+ backtrack_count -= 1
+ # We've found our backup end location if the title doesn't reslove.
+ if backtrack_count == 0:
+ last_bracket = index + 1
+
+ elif c in ("'", '"'):
+ # Quote has started
+ if not quote:
+ # We'll assume we are now in a title.
+ # Brackets are quoted, so no need to match them (except for the final one).
+ ignore_matches = True
+ backtrack_count = bracket_count
+ bracket_count = 1
+ start_quote = index + 1
+ quote = c
+ # Secondary quote (in case the first doesn't resolve): [text](link'"title")
+ elif c != quote and not alt_quote:
+ start_alt_quote = index + 1
+ alt_quote = c
+ # Update primary quote match
+ elif c == quote:
+ exit_quote = index + 1
+ # Update secondary quote match
+ elif alt_quote and c == alt_quote:
+ exit_alt_quote = index + 1
+
+ index += 1
+
+ # Link is closed, so let's break out of the loop
+ if bracket_count == 0:
+ # Get the title if we closed a title string right before link closed
+ if exit_quote >= 0 and quote == last:
+ href = data[start_index:start_quote - 1]
+ title = ''.join(data[start_quote:exit_quote - 1])
+ elif exit_alt_quote >= 0 and alt_quote == last:
+ href = data[start_index:start_alt_quote - 1]
+ title = ''.join(data[start_alt_quote:exit_alt_quote - 1])
+ else:
+ href = data[start_index:index - 1]
+ break
+
+ if c != ' ':
+ last = c
+
+ # We have a scenario: [test](link"notitle)
+ # When we enter a string, we stop tracking bracket resolution in the main counter,
+ # but we do keep a backup counter up until we discover where we might resolve all brackets
+ # if the title string fails to resolve.
+ if bracket_count != 0 and backtrack_count == 0:
+ href = data[start_index:last_bracket - 1]
+ index = last_bracket
+ bracket_count = 0
+
+ handled = bracket_count == 0
+
+ if title is not None:
+ title = self.RE_TITLE_CLEAN.sub(' ', dequote(self.unescape(title.strip())))
+
+ href = self.unescape(href).strip()
+
+ return href, title, index, handled
+
+ def getText(self, data, index):
+ """Parse the content between `[]` of the start of an image or link
+ resolving nested square brackets.
- for part in url[2:]:
- if ":" in part:
- # A colon in "path", "parameters", "query"
- # or "fragment" is suspect.
- return ''
+ """
+ bracket_count = 1
+ text = []
+ for pos in util.iterrange(index, len(data)):
+ c = data[pos]
+ if c == ']':
+ bracket_count -= 1
+ elif c == '[':
+ bracket_count += 1
+ index += 1
+ if bracket_count == 0:
+ break
+ text.append(c)
+ return ''.join(text), index, bracket_count == 0
+
+
+class ImageInlineProcessor(LinkInlineProcessor):
+ """ Return a img element from the given match. """
- # Url passes all tests. Return url as-is.
- return urlunparse(url)
+ def handleMatch(self, m, data):
+ text, index, handled = self.getText(data, m.end(0))
+ if not handled:
+ return None, None, None
+ src, title, index, handled = self.getLink(data, index)
+ if not handled:
+ return None, None, None
-class ImagePattern(LinkPattern):
- """ Return a img element from the given match. """
- def handleMatch(self, m):
el = util.etree.Element("img")
- src_parts = m.group(9).split()
- if src_parts:
- src = src_parts[0]
- if src[0] == "<" and src[-1] == ">":
- src = src[1:-1]
- el.set('src', self.sanitize_url(self.unescape(src)))
- else:
- el.set('src', "")
- if len(src_parts) > 1:
- el.set('title', dequote(self.unescape(" ".join(src_parts[1:]))))
- if self.markdown.enable_attributes:
- truealt = handleAttributes(m.group(2), el)
- else:
- truealt = m.group(2)
+ el.set("src", src)
- el.set('alt', self.unescape(truealt))
- return el
+ if title is not None:
+ el.set("title", title)
+ el.set('alt', self.unescape(text))
+ return el, m.start(0), index
-class ReferencePattern(LinkPattern):
- """ Match to a stored reference and return link element. """
+class ReferenceInlineProcessor(LinkInlineProcessor):
+ """ Match to a stored reference and return link element. """
NEWLINE_CLEANUP_RE = re.compile(r'[ ]?\n', re.MULTILINE)
- def handleMatch(self, m):
- try:
- id = m.group(9).lower()
- except IndexError:
- id = None
- if not id:
- # if we got something like "[Google][]" or "[Goggle]"
- # we'll use "google" as the id
- id = m.group(2).lower()
+ RE_LINK = re.compile(r'\s?\[([^\]]*)\]', re.DOTALL | re.UNICODE)
+
+ def handleMatch(self, m, data):
+ text, index, handled = self.getText(data, m.end(0))
+ if not handled:
+ return None, None, None
+
+ id, end, handled = self.evalId(data, index, text)
+ if not handled:
+ return None, None, None
# Clean up linebreaks in id
id = self.NEWLINE_CLEANUP_RE.sub(' ', id)
- if id not in self.markdown.references: # ignore undefined refs
- return None
- href, title = self.markdown.references[id]
+ if id not in self.md.references: # ignore undefined refs
+ return None, m.start(0), end
+
+ href, title = self.md.references[id]
+
+ return self.makeTag(href, title, text), m.start(0), end
+
+ def evalId(self, data, index, text):
+ """
+ Evaluate the id portion of [ref][id].
- text = m.group(2)
- return self.makeTag(href, title, text)
+ If [ref][] use [ref].
+ """
+ m = self.RE_LINK.match(data, pos=index)
+ if not m:
+ return None, index, False
+ else:
+ id = m.group(1).lower()
+ end = m.end(0)
+ if not id:
+ id = text.lower()
+ return id, end, True
def makeTag(self, href, title, text):
el = util.etree.Element('a')
- el.set('href', self.sanitize_url(href))
+ el.set('href', href)
if title:
el.set('title', title)
return el
-class ImageReferencePattern(ReferencePattern):
+class ShortReferenceInlineProcessor(ReferenceInlineProcessor):
+ """Shorte form of reference: [google]. """
+ def evalId(self, data, index, text):
+ """Evaluate the id from of [ref] """
+
+ return text.lower(), index, True
+
+
+class ImageReferenceInlineProcessor(ReferenceInlineProcessor):
""" Match to a stored reference and return img element. """
def makeTag(self, href, title, text):
el = util.etree.Element("img")
- el.set("src", self.sanitize_url(href))
+ el.set("src", href)
if title:
el.set("title", title)
-
- if self.markdown.enable_attributes:
- text = handleAttributes(text, el)
-
el.set("alt", self.unescape(text))
return el
-class AutolinkPattern(Pattern):
+class AutolinkInlineProcessor(InlineProcessor):
""" Return a link Element given an autolink (`<http://example/com>`). """
- def handleMatch(self, m):
+ def handleMatch(self, m, data):
el = util.etree.Element("a")
- el.set('href', self.unescape(m.group(2)))
- el.text = util.AtomicString(m.group(2))
- return el
+ el.set('href', self.unescape(m.group(1)))
+ el.text = util.AtomicString(m.group(1))
+ return el, m.start(0), m.end(0)
-class AutomailPattern(Pattern):
+class AutomailInlineProcessor(InlineProcessor):
"""
Return a mailto link Element given an automail link (`<foo@example.com>`).
"""
- def handleMatch(self, m):
+ def handleMatch(self, m, data):
el = util.etree.Element('a')
- email = self.unescape(m.group(2))
+ email = self.unescape(m.group(1))
if email.startswith("mailto:"):
email = email[len("mailto:"):]
mailto = "".join([util.AMP_SUBSTITUTE + '#%d;' %
ord(letter) for letter in mailto])
el.set('href', mailto)
- return el
+ return el, m.start(0), m.end(0)
+++ /dev/null
-from __future__ import unicode_literals
-from __future__ import absolute_import
-from . import util
-from copy import deepcopy
-
-
-class OrderedDict(dict):
- """
- A dictionary that keeps its keys in the order in which they're inserted.
-
- Copied from Django's SortedDict with some modifications.
-
- """
- def __new__(cls, *args, **kwargs):
- instance = super(OrderedDict, cls).__new__(cls, *args, **kwargs)
- instance.keyOrder = []
- return instance
-
- def __init__(self, data=None):
- if data is None or isinstance(data, dict):
- data = data or []
- super(OrderedDict, self).__init__(data)
- self.keyOrder = list(data) if data else []
- else:
- super(OrderedDict, self).__init__()
- super_set = super(OrderedDict, self).__setitem__
- for key, value in data:
- # Take the ordering from first key
- if key not in self:
- self.keyOrder.append(key)
- # But override with last value in data (dict() does this)
- super_set(key, value)
-
- def __deepcopy__(self, memo):
- return self.__class__([(key, deepcopy(value, memo))
- for key, value in self.items()])
-
- def __copy__(self):
- # The Python's default copy implementation will alter the state
- # of self. The reason for this seems complex but is likely related to
- # subclassing dict.
- return self.copy()
-
- def __setitem__(self, key, value):
- if key not in self:
- self.keyOrder.append(key)
- super(OrderedDict, self).__setitem__(key, value)
-
- def __delitem__(self, key):
- super(OrderedDict, self).__delitem__(key)
- self.keyOrder.remove(key)
-
- def __iter__(self):
- return iter(self.keyOrder)
-
- def __reversed__(self):
- return reversed(self.keyOrder)
-
- def pop(self, k, *args):
- result = super(OrderedDict, self).pop(k, *args)
- try:
- self.keyOrder.remove(k)
- except ValueError:
- # Key wasn't in the dictionary in the first place. No problem.
- pass
- return result
-
- def popitem(self):
- result = super(OrderedDict, self).popitem()
- self.keyOrder.remove(result[0])
- return result
-
- def _iteritems(self):
- for key in self.keyOrder:
- yield key, self[key]
-
- def _iterkeys(self):
- for key in self.keyOrder:
- yield key
-
- def _itervalues(self):
- for key in self.keyOrder:
- yield self[key]
-
- if util.PY3: # pragma: no cover
- items = _iteritems
- keys = _iterkeys
- values = _itervalues
- else: # pragma: no cover
- iteritems = _iteritems
- iterkeys = _iterkeys
- itervalues = _itervalues
-
- def items(self):
- return [(k, self[k]) for k in self.keyOrder]
-
- def keys(self):
- return self.keyOrder[:]
-
- def values(self):
- return [self[k] for k in self.keyOrder]
-
- def update(self, dict_):
- for k in dict_:
- self[k] = dict_[k]
-
- def setdefault(self, key, default):
- if key not in self:
- self.keyOrder.append(key)
- return super(OrderedDict, self).setdefault(key, default)
-
- def value_for_index(self, index):
- """Returns the value of the item at the given zero-based index."""
- return self[self.keyOrder[index]]
-
- def insert(self, index, key, value):
- """Inserts the key, value pair before the item with the given index."""
- if key in self.keyOrder:
- n = self.keyOrder.index(key)
- del self.keyOrder[n]
- if n < index:
- index -= 1
- self.keyOrder.insert(index, key)
- super(OrderedDict, self).__setitem__(key, value)
-
- def copy(self):
- """Returns a copy of this object."""
- # This way of initializing the copy means it works for subclasses, too.
- return self.__class__(self)
-
- def __repr__(self):
- """
- Replaces the normal dict.__repr__ with a version that returns the keys
- in their Ordered order.
- """
- return '{%s}' % ', '.join(
- ['%r: %r' % (k, v) for k, v in self._iteritems()]
- )
-
- def clear(self):
- super(OrderedDict, self).clear()
- self.keyOrder = []
-
- def index(self, key):
- """ Return the index of a given key. """
- try:
- return self.keyOrder.index(key)
- except ValueError:
- raise ValueError("Element '%s' was not found in OrderedDict" % key)
-
- def index_for_location(self, location):
- """ Return index or None for a given location. """
- if location == '_begin':
- i = 0
- elif location == '_end':
- i = None
- elif location.startswith('<') or location.startswith('>'):
- i = self.index(location[1:])
- if location.startswith('>'):
- if i >= len(self):
- # last item
- i = None
- else:
- i += 1
- else:
- raise ValueError('Not a valid location: "%s". Location key '
- 'must start with a ">" or "<".' % location)
- return i
-
- def add(self, key, value, location):
- """ Insert by key location. """
- i = self.index_for_location(location)
- if i is not None:
- self.insert(i, key, value)
- else:
- self.__setitem__(key, value)
-
- def link(self, key, location):
- """ Change location of an existing item. """
- n = self.keyOrder.index(key)
- del self.keyOrder[n]
- try:
- i = self.index_for_location(location)
- if i is not None:
- self.keyOrder.insert(i, key)
- else:
- self.keyOrder.append(key)
- except Exception as e:
- # restore to prevent data loss and reraise
- self.keyOrder.insert(n, key)
- raise e
+# -*- coding: utf-8 -*-
"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+
POST-PROCESSORS
=============================================================================
from __future__ import absolute_import
from __future__ import unicode_literals
+from collections import OrderedDict
from . import util
-from . import odict
import re
-def build_postprocessors(md_instance, **kwargs):
+def build_postprocessors(md, **kwargs):
""" Build the default postprocessors for Markdown. """
- postprocessors = odict.OrderedDict()
- postprocessors["raw_html"] = RawHtmlPostprocessor(md_instance)
- postprocessors["amp_substitute"] = AndSubstitutePostprocessor()
- postprocessors["unescape"] = UnescapePostprocessor()
+ postprocessors = util.Registry()
+ postprocessors.register(RawHtmlPostprocessor(md), 'raw_html', 30)
+ postprocessors.register(AndSubstitutePostprocessor(), 'amp_substitute', 20)
+ postprocessors.register(UnescapePostprocessor(), 'unescape', 10)
return postprocessors
""" Restore raw html to the document. """
def run(self, text):
- """ Iterate over html stash and restore "safe" html. """
- for i in range(self.markdown.htmlStash.html_counter):
- html, safe = self.markdown.htmlStash.rawHtmlBlocks[i]
- if self.markdown.safeMode and not safe:
- if str(self.markdown.safeMode).lower() == 'escape':
- html = self.escape(html)
- elif str(self.markdown.safeMode).lower() == 'remove':
- html = ''
- else:
- html = self.markdown.html_replacement_text
- if (self.isblocklevel(html) and
- (safe or not self.markdown.safeMode)):
- text = text.replace(
- "<p>%s</p>" %
- (self.markdown.htmlStash.get_placeholder(i)),
+ """ Iterate over html stash and restore html. """
+ replacements = OrderedDict()
+ for i in range(self.md.htmlStash.html_counter):
+ html = self.md.htmlStash.rawHtmlBlocks[i]
+ if self.isblocklevel(html):
+ replacements["<p>%s</p>" %
+ (self.md.htmlStash.get_placeholder(i))] = \
html + "\n"
- )
- text = text.replace(
- self.markdown.htmlStash.get_placeholder(i), html
- )
- return text
+ replacements[self.md.htmlStash.get_placeholder(i)] = html
- def escape(self, html):
- """ Basic html escaping """
- html = html.replace('&', '&')
- html = html.replace('<', '<')
- html = html.replace('>', '>')
- return html.replace('"', '"')
+ if replacements:
+ pattern = re.compile("|".join(re.escape(k) for k in replacements))
+ text = pattern.sub(lambda m: replacements[m.group(0)], text)
+
+ return text
def isblocklevel(self, html):
m = re.match(r'^\<\/?([^ >]+)', html)
if m.group(1)[0] in ('!', '?', '@', '%'):
# Comment, php etc...
return True
- return util.isBlockLevel(m.group(1))
+ return self.md.is_block_level(m.group(1))
return False
class UnescapePostprocessor(Postprocessor):
""" Restore escaped chars """
- RE = re.compile('%s(\d+)%s' % (util.STX, util.ETX))
+ RE = re.compile(r'%s(\d+)%s' % (util.STX, util.ETX))
def unescape(self, m):
return util.int2str(int(m.group(1)))
+# -*- coding: utf-8 -*-
"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+
PRE-PROCESSORS
=============================================================================
from __future__ import absolute_import
from __future__ import unicode_literals
from . import util
-from . import odict
import re
-def build_preprocessors(md_instance, **kwargs):
+def build_preprocessors(md, **kwargs):
""" Build the default set of preprocessors used by Markdown. """
- preprocessors = odict.OrderedDict()
- preprocessors['normalize_whitespace'] = NormalizeWhitespace(md_instance)
- if md_instance.safeMode != 'escape':
- preprocessors["html_block"] = HtmlBlockPreprocessor(md_instance)
- preprocessors["reference"] = ReferencePreprocessor(md_instance)
+ preprocessors = util.Registry()
+ preprocessors.register(NormalizeWhitespace(md), 'normalize_whitespace', 30)
+ preprocessors.register(HtmlBlockPreprocessor(md), 'html_block', 20)
+ preprocessors.register(ReferencePreprocessor(md), 'reference', 10)
return preprocessors
class NormalizeWhitespace(Preprocessor):
- """ Normalize whitespace for consistant parsing. """
+ """ Normalize whitespace for consistent parsing. """
def run(self, lines):
source = '\n'.join(lines)
source = source.replace(util.STX, "").replace(util.ETX, "")
source = source.replace("\r\n", "\n").replace("\r", "\n") + "\n\n"
- source = source.expandtabs(self.markdown.tab_length)
+ source = source.expandtabs(self.md.tab_length)
source = re.sub(r'(?<=\n) +\n', '\n', source)
return source.split('\n')
self._stringindex_to_listindex(data_index, items[i:]) + i
if 'markdown' in attrs.keys():
items[i] = items[i][left_index:] # remove opening tag
- placeholder = self.markdown.htmlStash.store_tag(
+ placeholder = self.md.htmlStash.store_tag(
left_tag, attrs, i + 1, right_listindex + 1)
items.insert(i, placeholder)
if len(items) - right_listindex <= 1: # last nest, no tail
else: # raw html
if len(items) - right_listindex <= 1: # last element
right_listindex -= 1
- offset = 1 if i == right_listindex else 0
- placeholder = self.markdown.htmlStash.store('\n\n'.join(
- items[i:right_listindex + offset]))
- del items[i:right_listindex + offset]
+ if right_listindex <= i:
+ right_listindex = i + 1
+ placeholder = self.md.htmlStash.store('\n\n'.join(
+ items[i:right_listindex]))
+ del items[i:right_listindex]
items.insert(i, placeholder)
return items
block)
# keep checking conditions below and maybe just append
- if data_index < len(block) and (util.isBlockLevel(left_tag) or left_tag == '--'):
+ if data_index < len(block) and (self.md.is_block_level(left_tag) or left_tag == '--'):
text.insert(0, block[data_index:])
block = block[:data_index]
- if not (util.isBlockLevel(left_tag) or block[1] in ["!", "?", "@", "%"]):
+ if not (self.md.is_block_level(left_tag) or block[1] in ["!", "?", "@", "%"]):
new_blocks.append(block)
continue
and self._equal_tags(left_tag, right_tag):
if self.markdown_in_raw and 'markdown' in attrs.keys():
block = block[left_index:-len(right_tag) - 2]
- new_blocks.append(self.markdown.htmlStash.
+ new_blocks.append(self.md.htmlStash.
store_tag(left_tag, attrs, 0, 2))
new_blocks.extend([block])
else:
new_blocks.append(
- self.markdown.htmlStash.store(block.strip()))
+ self.md.htmlStash.store(block.strip()))
continue
else:
# if is block level tag and is not complete
if (not self._equal_tags(left_tag, right_tag)) and \
- (util.isBlockLevel(left_tag) or left_tag == "--"):
+ (self.md.is_block_level(left_tag) or left_tag == "--"):
items.append(block.strip())
in_tag = True
else:
new_blocks.append(
- self.markdown.htmlStash.store(block.strip())
+ self.md.htmlStash.store(block.strip())
)
continue
else:
items.append(block)
- right_tag, data_index = self._get_right_tag(left_tag, 0, block)
+ # Need to evaluate all items so we can calculate relative to the left index.
+ right_tag, data_index = self._get_right_tag(left_tag, left_index, ''.join(items))
+ # Adjust data_index: relative to items -> relative to last block
+ prev_block_length = 0
+ for item in items[:-1]:
+ prev_block_length += len(item)
+ data_index -= prev_block_length
if self._equal_tags(left_tag, right_tag):
# if find closing tag
right_index = len(items) + 3
else:
right_index = len(items) + 2
- new_blocks.append(self.markdown.htmlStash.store_tag(
+ new_blocks.append(self.md.htmlStash.store_tag(
left_tag, attrs, 0, right_index))
- placeholderslen = len(self.markdown.htmlStash.tag_data)
+ placeholderslen = len(self.md.htmlStash.tag_data)
new_blocks.extend(
self._nested_markdown_in_html(items))
- nests = len(self.markdown.htmlStash.tag_data) - \
+ nests = len(self.md.htmlStash.tag_data) - \
placeholderslen
- self.markdown.htmlStash.tag_data[-1 - nests][
+ self.md.htmlStash.tag_data[-1 - nests][
'right_index'] += nests - 2
else:
new_blocks.append(
- self.markdown.htmlStash.store('\n\n'.join(items)))
+ self.md.htmlStash.store('\n\n'.join(items)))
items = []
if items:
else:
right_index = len(items) + 2
new_blocks.append(
- self.markdown.htmlStash.store_tag(
+ self.md.htmlStash.store_tag(
left_tag, attrs, 0, right_index))
- placeholderslen = len(self.markdown.htmlStash.tag_data)
+ placeholderslen = len(self.md.htmlStash.tag_data)
new_blocks.extend(self._nested_markdown_in_html(items))
- nests = len(self.markdown.htmlStash.tag_data) - placeholderslen
- self.markdown.htmlStash.tag_data[-1 - nests][
+ nests = len(self.md.htmlStash.tag_data) - placeholderslen
+ self.md.htmlStash.tag_data[-1 - nests][
'right_index'] += nests - 2
else:
new_blocks.append(
- self.markdown.htmlStash.store('\n\n'.join(items)))
+ self.md.htmlStash.store('\n\n'.join(items)))
new_blocks.append('\n')
new_text = "\n\n".join(new_blocks)
if tm:
lines.pop(0)
t = tm.group(2) or tm.group(3) or tm.group(4)
- self.markdown.references[id] = (link, t)
+ self.md.references[id] = (link, t)
+ # Preserve the line to prevent raw HTML indexing issue.
+ # https://github.com/Python-Markdown/markdown/issues/584
+ new_text.append('')
else:
new_text.append(line)
from __future__ import absolute_import
from __future__ import unicode_literals
+from xml.etree.ElementTree import ProcessingInstruction
from . import util
+import re
ElementTree = util.etree.ElementTree
QName = util.etree.QName
if hasattr(util.etree, 'test_comment'): # pragma: no cover
Comment = util.etree.test_comment
else: # pragma: no cover
Comment = util.etree.Comment
-PI = util.etree.PI
-ProcessingInstruction = util.etree.ProcessingInstruction
__all__ = ['to_html_string', 'to_xhtml_string']
HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr",
- "img", "input", "isindex", "link", "meta" "param")
+ "img", "input", "isindex", "link", "meta", "param")
+RE_AMP = re.compile(r'&(?!(?:\#[0-9]+|[0-9a-z]+);)', re.I)
try:
HTML_EMPTY = set(HTML_EMPTY)
except NameError: # pragma: no cover
pass
-_namespace_map = {
- # "well-known" namespace prefixes
- "http://www.w3.org/XML/1998/namespace": "xml",
- "http://www.w3.org/1999/xhtml": "html",
- "http://www.w3.org/1999/02/22-rdf-syntax-ns#": "rdf",
- "http://schemas.xmlsoap.org/wsdl/": "wsdl",
- # xml schema
- "http://www.w3.org/2001/XMLSchema": "xs",
- "http://www.w3.org/2001/XMLSchema-instance": "xsi",
- # dublic core
- "http://purl.org/dc/elements/1.1/": "dc",
-}
-
def _raise_serialization_error(text): # pragma: no cover
raise TypeError(
)
-def _encode(text, encoding):
- try:
- return text.encode(encoding, "xmlcharrefreplace")
- except (TypeError, AttributeError): # pragma: no cover
- _raise_serialization_error(text)
-
-
def _escape_cdata(text):
# escape character data
try:
# shorter than 500 character, or so. assume that's, by far,
# the most common case in most applications.
if "&" in text:
- text = text.replace("&", "&")
+ # Only replace & when not part of an entity
+ text = RE_AMP.sub('&', text)
if "<" in text:
text = text.replace("<", "<")
if ">" in text:
# escape attribute value
try:
if "&" in text:
- text = text.replace("&", "&")
+ # Only replace & when not part of an entity
+ text = RE_AMP.sub('&', text)
if "<" in text:
text = text.replace("<", "<")
if ">" in text:
# escape attribute value
try:
if "&" in text:
- text = text.replace("&", "&")
+ # Only replace & when not part of an entity
+ text = RE_AMP.sub('&', text)
if "<" in text:
text = text.replace("<", "<")
if ">" in text:
_raise_serialization_error(text)
-def _serialize_html(write, elem, qnames, namespaces, format):
+def _serialize_html(write, elem, format):
tag = elem.tag
text = elem.text
if tag is Comment:
write("<!--%s-->" % _escape_cdata(text))
elif tag is ProcessingInstruction:
write("<?%s?>" % _escape_cdata(text))
+ elif tag is None:
+ if text:
+ write(_escape_cdata(text))
+ for e in elem:
+ _serialize_html(write, e, format)
else:
- tag = qnames[tag]
- if tag is None:
+ namespace_uri = None
+ if isinstance(tag, QName):
+ # QNAME objects store their data as a string: `{uri}tag`
+ if tag.text[:1] == "{":
+ namespace_uri, tag = tag.text[1:].split("}", 1)
+ else:
+ raise ValueError('QName objects must define a tag.')
+ write("<" + tag)
+ items = elem.items()
+ if items:
+ items = sorted(items) # lexical order
+ for k, v in items:
+ if isinstance(k, QName):
+ # Assume a text only QName
+ k = k.text
+ if isinstance(v, QName):
+ # Assume a text only QName
+ v = v.text
+ else:
+ v = _escape_attrib_html(v)
+ if k == v and format == 'html':
+ # handle boolean attributes
+ write(" %s" % v)
+ else:
+ write(' %s="%s"' % (k, v))
+ if namespace_uri:
+ write(' xmlns="%s"' % (_escape_attrib(namespace_uri)))
+ if format == "xhtml" and tag.lower() in HTML_EMPTY:
+ write(" />")
+ else:
+ write(">")
if text:
- write(_escape_cdata(text))
+ if tag.lower() in ["script", "style"]:
+ write(text)
+ else:
+ write(_escape_cdata(text))
for e in elem:
- _serialize_html(write, e, qnames, None, format)
- else:
- write("<" + tag)
- items = elem.items()
- if items or namespaces:
- items = sorted(items) # lexical order
- for k, v in items:
- if isinstance(k, QName):
- k = k.text
- if isinstance(v, QName):
- v = qnames[v.text]
- else:
- v = _escape_attrib_html(v)
- if qnames[k] == v and format == 'html':
- # handle boolean attributes
- write(" %s" % v)
- else:
- write(" %s=\"%s\"" % (qnames[k], v))
- if namespaces:
- items = namespaces.items()
- items.sort(key=lambda x: x[1]) # sort on prefix
- for v, k in items:
- if k:
- k = ":" + k
- write(" xmlns%s=\"%s\"" % (k, _escape_attrib(v)))
- if format == "xhtml" and tag.lower() in HTML_EMPTY:
- write(" />")
- else:
- write(">")
- if text:
- if tag.lower() in ["script", "style"]:
- write(text)
- else:
- write(_escape_cdata(text))
- for e in elem:
- _serialize_html(write, e, qnames, None, format)
- if tag.lower() not in HTML_EMPTY:
- write("</" + tag + ">")
+ _serialize_html(write, e, format)
+ if tag.lower() not in HTML_EMPTY:
+ write("</" + tag + ">")
if elem.tail:
write(_escape_cdata(elem.tail))
-def _write_html(root,
- encoding=None,
- default_namespace=None,
- format="html"):
+def _write_html(root, format="html"):
assert root is not None
data = []
write = data.append
- qnames, namespaces = _namespaces(root, default_namespace)
- _serialize_html(write, root, qnames, namespaces, format)
- if encoding is None:
- return "".join(data)
- else:
- return _encode("".join(data))
+ _serialize_html(write, root, format)
+ return "".join(data)
# --------------------------------------------------------------------
-# serialization support
-
-def _namespaces(elem, default_namespace=None):
- # identify namespaces used in this tree
-
- # maps qnames to *encoded* prefix:local names
- qnames = {None: None}
-
- # maps uri:s to prefixes
- namespaces = {}
- if default_namespace:
- namespaces[default_namespace] = ""
-
- def add_qname(qname):
- # calculate serialized qname representation
- try:
- if qname[:1] == "{":
- uri, tag = qname[1:].split("}", 1)
- prefix = namespaces.get(uri)
- if prefix is None:
- prefix = _namespace_map.get(uri)
- if prefix is None:
- prefix = "ns%d" % len(namespaces)
- if prefix != "xml":
- namespaces[uri] = prefix
- if prefix:
- qnames[qname] = "%s:%s" % (prefix, tag)
- else:
- qnames[qname] = tag # default element
- else:
- if default_namespace:
- raise ValueError(
- "cannot use non-qualified names with "
- "default_namespace option"
- )
- qnames[qname] = qname
- except TypeError: # pragma: no cover
- _raise_serialization_error(qname)
-
- # populate qname and namespaces table
- try:
- iterate = elem.iter
- except AttributeError:
- iterate = elem.getiterator # cET compatibility
- for elem in iterate():
- tag = elem.tag
- if isinstance(tag, QName) and tag.text not in qnames:
- add_qname(tag.text)
- elif isinstance(tag, util.string_type):
- if tag not in qnames:
- add_qname(tag)
- elif tag is not None and tag is not Comment and tag is not PI:
- _raise_serialization_error(tag)
- for key, value in elem.items():
- if isinstance(key, QName):
- key = key.text
- if key not in qnames:
- add_qname(key)
- if isinstance(value, QName) and value.text not in qnames:
- add_qname(value.text)
- text = elem.text
- if isinstance(text, QName) and text.text not in qnames:
- add_qname(text.text)
- return qnames, namespaces
-
+# public functions
def to_html_string(element):
return _write_html(ElementTree(element).getroot(), format="html")
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from __future__ import absolute_import
+import os
+import io
+import unittest
+import textwrap
+from . import markdown
+
+try:
+ import tidylib
+except ImportError:
+ tidylib = None
+
+__all__ = ['TestCase', 'LegacyTestCase', 'Kwargs']
+
+
+class TestCase(unittest.TestCase):
+ """
+ A unittest.TestCase subclass with helpers for testing Markdown output.
+
+ Define `default_kwargs` as a dict of keywords to pass to Markdown for each
+ test. The defaults can be overridden on individual tests.
+
+ The `assertMarkdownRenders` method accepts the source text, the expected
+ output, and any keywords to pass to Markdown. The `default_kwargs` are used
+ except where overridden by `kwargs`. The ouput and expected ouput are passed
+ to `TestCase.assertMultiLineEqual`. An AssertionError is raised with a diff
+ if the actual output does not equal the expected output.
+
+ The `dedent` method is available to dedent triple-quoted strings if
+ necessary.
+
+ In all other respects, behaves as unittest.TestCase.
+ """
+
+ default_kwargs = {}
+
+ def assertMarkdownRenders(self, source, expected, **kwargs):
+ """
+ Test that source Markdown text renders to expected output with given keywords.
+ """
+
+ kws = self.default_kwargs.copy()
+ kws.update(kwargs)
+ output = markdown(source, **kws)
+ self.assertMultiLineEqual(output, expected)
+
+ def dedent(self, text):
+ """
+ Dedent text.
+ """
+
+ # TODO: If/when actual output ends with a newline, then use:
+ # return textwrap.dedent(text.strip('/n'))
+ return textwrap.dedent(text).strip()
+
+
+#########################
+# Legacy Test Framework #
+#########################
+
+
+class Kwargs(dict):
+ """ A dict like class for holding keyword arguments. """
+ pass
+
+
+def _normalize_whitespace(text):
+ """ Normalize whitespace for a string of html using tidylib. """
+ output, errors = tidylib.tidy_fragment(text, options={
+ 'drop_empty_paras': 0,
+ 'fix_backslash': 0,
+ 'fix_bad_comments': 0,
+ 'fix_uri': 0,
+ 'join_styles': 0,
+ 'lower_literals': 0,
+ 'merge_divs': 0,
+ 'output_xhtml': 1,
+ 'quote_ampersand': 0,
+ 'newline': 'LF'
+ })
+ return output
+
+
+class LegacyTestMeta(type):
+ def __new__(cls, name, bases, dct):
+
+ def generate_test(infile, outfile, normalize, kwargs):
+ def test(self):
+ with io.open(infile, encoding="utf-8") as f:
+ input = f.read()
+ with io.open(outfile, encoding="utf-8") as f:
+ # Normalize line endings
+ # (on Windows, git may have altered line endings).
+ expected = f.read().replace("\r\n", "\n")
+ output = markdown(input, **kwargs)
+ if tidylib and normalize:
+ expected = _normalize_whitespace(expected)
+ output = _normalize_whitespace(output)
+ elif normalize:
+ self.skipTest('Tidylib not available.')
+ self.assertMultiLineEqual(output, expected)
+ return test
+
+ location = dct.get('location', '')
+ exclude = dct.get('exclude', [])
+ normalize = dct.get('normalize', False)
+ input_ext = dct.get('input_ext', '.txt')
+ output_ext = dct.get('output_ext', '.html')
+ kwargs = dct.get('default_kwargs', Kwargs())
+
+ if os.path.isdir(location):
+ for file in os.listdir(location):
+ infile = os.path.join(location, file)
+ if os.path.isfile(infile):
+ tname, ext = os.path.splitext(file)
+ if ext == input_ext:
+ outfile = os.path.join(location, tname + output_ext)
+ tname = tname.replace(' ', '_').replace('-', '_')
+ kws = kwargs.copy()
+ if tname in dct:
+ kws.update(dct[tname])
+ test_name = 'test_%s' % tname
+ if tname not in exclude:
+ dct[test_name] = generate_test(infile, outfile, normalize, kws)
+ else:
+ dct[test_name] = unittest.skip('Excluded')(lambda: None)
+
+ return type.__new__(cls, name, bases, dct)
+
+
+# Define LegacyTestCase class with metaclass in Py2 & Py3 compatible way.
+# See https://stackoverflow.com/a/38668373/866026
+# TODO: If/when py2 support is dropped change to:
+# class LegacyTestCase(unittest.Testcase, metaclass=LegacyTestMeta)
+
+
+class LegacyTestCase(LegacyTestMeta('LegacyTestCase', (unittest.TestCase,), {'__slots__': ()})):
+ """
+ A `unittest.TestCase` subclass for running Markdown's legacy file-based tests.
+
+ A subclass should define various properties which point to a directory of
+ text-based test files and define various behaviors/defaults for those tests.
+ The following properties are supported:
+
+ location: A path to the directory fo test files. An absolute path is preferred.
+ exclude: A list of tests to exclude. Each test name should comprise the filename
+ without an extension.
+ normalize: A boolean value indicating if the HTML should be normalized.
+ Default: `False`.
+ input_ext: A string containing the file extension of input files. Default: `.txt`.
+ ouput_ext: A string containing the file extension of expected output files.
+ Default: `html`.
+ default_kwargs: A `Kwargs` instance which stores the default set of keyword
+ arguments for all test files in the directory.
+
+ In addition, properties can be defined for each individual set of test files within
+ the directory. The property should be given the name of the file wihtout the file
+ extension. Any spaces and dashes in the filename should be replaced with
+ underscores. The value of the property should be a `Kwargs` instance which
+ contains the keyword arguments that should be passed to `Markdown` for that
+ test file. The keyword arguments will "update" the `default_kwargs`.
+
+ When the class instance is created, it will walk the given directory and create
+ a separate unitttest for each set of test files using the naming scheme:
+ `test_filename`. One unittest will be run for each set of input and output files.
+ """
+ pass
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
from __future__ import unicode_literals
from __future__ import absolute_import
from . import util
-from . import odict
from . import inlinepatterns
-def build_treeprocessors(md_instance, **kwargs):
+def build_treeprocessors(md, **kwargs):
""" Build the default treeprocessors for Markdown. """
- treeprocessors = odict.OrderedDict()
- treeprocessors["inline"] = InlineProcessor(md_instance)
- treeprocessors["prettify"] = PrettifyTreeprocessor(md_instance)
+ treeprocessors = util.Registry()
+ treeprocessors.register(InlineProcessor(md), 'inline', 20)
+ treeprocessors.register(PrettifyTreeprocessor(md), 'prettify', 10)
return treeprocessors
self.__placeholder_length = 4 + len(self.__placeholder_prefix) \
+ len(self.__placeholder_suffix)
self.__placeholder_re = util.INLINE_PLACEHOLDER_RE
- self.markdown = md
+ self.md = md
self.inlinePatterns = md.inlinePatterns
+ self.ancestors = []
+
+ @property
+ @util.deprecated("Use 'md' instead.")
+ def markdown(self):
+ # TODO: remove this later
+ return self.md
def __makePlaceholder(self, type):
""" Generate a placeholder """
startIndex = 0
while patternIndex < len(self.inlinePatterns):
data, matched, startIndex = self.__applyPattern(
- self.inlinePatterns.value_for_index(patternIndex),
- data, patternIndex, startIndex)
+ self.inlinePatterns[patternIndex], data, patternIndex, startIndex
+ )
if not matched:
patternIndex += 1
return data
childResult.reverse()
for newChild in childResult:
- node.insert(pos, newChild)
+ node.insert(pos, newChild[0])
def __processPlaceholders(self, data, parent, isText=True):
"""
def linkText(text):
if text:
if result:
- if result[-1].tail:
- result[-1].tail += text
+ if result[-1][0].tail:
+ result[-1][0].tail += text
else:
- result[-1].tail = text
+ result[-1][0].tail = text
elif not isText:
if parent.tail:
parent.tail += text
continue
strartIndex = phEndIndex
- result.append(node)
+ result.append((node, self.ancestors[:]))
else: # wrong placeholder
end = index + len(self.__placeholder_prefix)
Returns: String with placeholders instead of ElementTree elements.
"""
- match = pattern.getCompiledRegExp().match(data[startIndex:])
- leftData = data[:startIndex]
+ new_style = isinstance(pattern, inlinepatterns.InlineProcessor)
+
+ for exclude in pattern.ANCESTOR_EXCLUDES:
+ if exclude.lower() in self.ancestors:
+ return data, False, 0
+
+ if new_style:
+ match = None
+ # Since handleMatch may reject our first match,
+ # we iterate over the buffer looking for matches
+ # until we can't find any more.
+ for match in pattern.getCompiledRegExp().finditer(data, startIndex):
+ node, start, end = pattern.handleMatch(match, data)
+ if start is None or end is None:
+ startIndex += match.end(0)
+ match = None
+ continue
+ break
+ else: # pragma: no cover
+ match = pattern.getCompiledRegExp().match(data[startIndex:])
+ leftData = data[:startIndex]
if not match:
return data, False, 0
- node = pattern.handleMatch(match)
+ if not new_style: # pragma: no cover
+ node = pattern.handleMatch(match)
+ start = match.start(0)
+ end = match.end(0)
if node is None:
- return data, True, len(leftData)+match.span(len(match.groups()))[0]
+ return data, True, end
if not isString(node):
if not isinstance(node.text, util.AtomicString):
for child in [node] + list(node):
if not isString(node):
if child.text:
+ self.ancestors.append(child.tag.lower())
child.text = self.__handleInline(
child.text, patternIndex + 1
)
+ self.ancestors.pop()
if child.tail:
child.tail = self.__handleInline(
child.tail, patternIndex
placeholder = self.__stashNode(node, pattern.type())
- return "%s%s%s%s" % (leftData,
- match.group(1),
- placeholder, match.groups()[-1]), True, 0
-
- def run(self, tree):
+ if new_style:
+ return "%s%s%s" % (data[:start],
+ placeholder, data[end:]), True, 0
+ else: # pragma: no cover
+ return "%s%s%s%s" % (leftData,
+ match.group(1),
+ placeholder, match.groups()[-1]), True, 0
+
+ def __build_ancestors(self, parent, parents):
+ """Build the ancestor list."""
+ ancestors = []
+ while parent is not None:
+ if parent is not None:
+ ancestors.append(parent.tag.lower())
+ parent = self.parent_map.get(parent)
+ ancestors.reverse()
+ parents.extend(ancestors)
+
+ def run(self, tree, ancestors=None):
"""Apply inline patterns to a parsed Markdown tree.
Iterate over ElementTree, find elements with inline tag, apply inline
Arguments:
* tree: ElementTree object, representing Markdown tree.
+ * ancestors: List of parent tag names that precede the tree node (if needed).
Returns: ElementTree object with applied inline patterns.
"""
self.stashed_nodes = {}
- stack = [tree]
+ # Ensure a valid parent list, but copy passed in lists
+ # to ensure we don't have the user accidentally change it on us.
+ tree_parents = [] if ancestors is None else ancestors[:]
+
+ self.parent_map = dict((c, p) for p in tree.iter() for c in p)
+ stack = [(tree, tree_parents)]
while stack:
- currElement = stack.pop()
+ currElement, parents = stack.pop()
+
+ self.ancestors = parents
+ self.__build_ancestors(currElement, self.ancestors)
+
insertQueue = []
for child in currElement:
if child.text and not isinstance(
child.text, util.AtomicString
):
+ self.ancestors.append(child.tag.lower())
text = child.text
child.text = None
lst = self.__processPlaceholders(
self.__handleInline(text), child
)
+ for l in lst:
+ self.parent_map[l[0]] = child
stack += lst
insertQueue.append((child, lst))
+ self.ancestors.pop()
if child.tail:
tail = self.__handleInline(child.tail)
dumby = util.etree.Element('d')
pos = list(currElement).index(child) + 1
tailResult.reverse()
for newChild in tailResult:
- currElement.insert(pos, newChild)
+ self.parent_map[newChild[0]] = currElement
+ currElement.insert(pos, newChild[0])
if len(child):
- stack.append(child)
+ self.parent_map[child] = currElement
+ stack.append((child, self.ancestors[:]))
for element, lst in insertQueue:
- if self.markdown.enable_attributes:
- if element.text and isString(element.text):
- element.text = inlinepatterns.handleAttributes(
- element.text, element
- )
- i = 0
- for newChild in lst:
- if self.markdown.enable_attributes:
- # Processing attributes
- if newChild.tail and isString(newChild.tail):
- newChild.tail = inlinepatterns.handleAttributes(
- newChild.tail, element
- )
- if newChild.text and isString(newChild.text):
- newChild.text = inlinepatterns.handleAttributes(
- newChild.text, newChild
- )
+ for i, obj in enumerate(lst):
+ newChild = obj[0]
element.insert(i, newChild)
- i += 1
return tree
""" Recursively add linebreaks to ElementTree children. """
i = "\n"
- if util.isBlockLevel(elem.tag) and elem.tag not in ['code', 'pre']:
+ if self.md.is_block_level(elem.tag) and elem.tag not in ['code', 'pre']:
if (not elem.text or not elem.text.strip()) \
- and len(elem) and util.isBlockLevel(elem[0].tag):
+ and len(elem) and self.md.is_block_level(elem[0].tag):
elem.text = i
for e in elem:
- if util.isBlockLevel(e.tag):
+ if self.md.is_block_level(e.tag):
self._prettifyETree(e)
if not elem.tail or not elem.tail.strip():
elem.tail = i
""" Add linebreaks to ElementTree root object. """
self._prettifyETree(root)
- # Do <br />'s seperately as they are often in the middle of
+ # Do <br />'s separately as they are often in the middle of
# inline content and missed by _prettifyETree.
- brs = root.getiterator('br')
+ brs = root.iter('br')
for br in brs:
if not br.tail or not br.tail.strip():
br.tail = '\n'
else:
br.tail = '\n%s' % br.tail
# Clean up extra empty lines at end of code blocks.
- pres = root.getiterator('pre')
+ pres = root.iter('pre')
for pre in pres:
if len(pre) and pre[0].tag == 'code':
pre[0].text = util.AtomicString(pre[0].text.rstrip() + '\n')
# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
from __future__ import unicode_literals
import re
import sys
+from collections import namedtuple
+from functools import wraps
+import warnings
"""
string_type = str
text_type = str
int2str = chr
+ iterrange = range
else: # pragma: no cover
string_type = basestring # noqa
text_type = unicode # noqa
int2str = unichr # noqa
+ iterrange = xrange # noqa
"""
"""
-BLOCK_LEVEL_ELEMENTS = re.compile(
- "^(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul"
- "|script|noscript|form|fieldset|iframe|math"
- "|hr|hr/|style|li|dt|dd|thead|tbody"
- "|tr|th|td|section|footer|header|group|figure"
- "|figcaption|aside|article|canvas|output"
- "|progress|video|nav)$",
- re.IGNORECASE
-)
+BLOCK_LEVEL_ELEMENTS = [
+ # Elements which are invalid to wrap in a `<p>` tag.
+ # See http://w3c.github.io/html/grouping-content.html#the-p-element
+ 'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl',
+ 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3',
+ 'h4', 'h5', 'h6', 'header', 'hr', 'main', 'menu', 'nav', 'ol', 'p', 'pre',
+ 'section', 'table', 'ul',
+ # Other elements which Markdown should not be mucking up the contents of.
+ 'canvas', 'dd', 'dt', 'group', 'iframe', 'li', 'math', 'noscript', 'output',
+ 'progress', 'script', 'style', 'tbody', 'td', 'th', 'thead', 'tr', 'video'
+]
+
# Placeholders
STX = '\u0002' # Use STX ("Start of text") for start-of-placeholder
ETX = '\u0003' # Use ETX ("End of text") for end-of-placeholder
"""
+def deprecated(message):
+ """
+ Raise a DeprecationWarning when wrapped function/method is called.
+
+ Borrowed from https://stackoverflow.com/a/48632082/866026
+ """
+ def deprecated_decorator(func):
+ @wraps(func)
+ def deprecated_func(*args, **kwargs):
+ warnings.warn(
+ "'{}' is deprecated. {}".format(func.__name__, message),
+ category=DeprecationWarning,
+ stacklevel=2
+ )
+ return func(*args, **kwargs)
+ return deprecated_func
+ return deprecated_decorator
+
+
+@deprecated("Use 'Markdown.is_block_level' instead.")
def isBlockLevel(tag):
"""Check if the tag is a block level HTML tag."""
if isinstance(tag, string_type):
- return BLOCK_LEVEL_ELEMENTS.match(tag)
+ return tag.lower().rstrip('/') in BLOCK_LEVEL_ELEMENTS
# Some ElementTree tags are not strings, so return False.
return False
raise ValueError('Cannot parse bool value: %r' % value)
+def code_escape(text):
+ """Escape code."""
+ if "&" in text:
+ text = text.replace("&", "&")
+ if "<" in text:
+ text = text.replace("<", "<")
+ if ">" in text:
+ text = text.replace(">", ">")
+ return text
+
+
"""
MISC AUXILIARY CLASSES
=============================================================================
class Processor(object):
- def __init__(self, markdown_instance=None):
- if markdown_instance:
- self.markdown = markdown_instance
+ def __init__(self, md=None):
+ self.md = md
+
+ @property
+ @deprecated("Use 'md' instead.")
+ def markdown(self):
+ # TODO: remove this later
+ return self.md
class HtmlStash(object):
self.tag_counter = 0
self.tag_data = [] # list of dictionaries in the order tags appear
- def store(self, html, safe=False):
+ def store(self, html):
"""
Saves an HTML segment for later reinsertion. Returns a
placeholder string that needs to be inserted into the
Keyword arguments:
* html: an html segment
- * safe: label an html segment as safe for safemode
Returns : a placeholder string
"""
- self.rawHtmlBlocks.append((html, safe))
+ self.rawHtmlBlocks.append(html)
placeholder = self.get_placeholder(self.html_counter)
self.html_counter += 1
return placeholder
placeholder = TAG_PLACEHOLDER % str(self.tag_counter)
self.tag_counter += 1 # equal to the tag's index in self.tag_data
return placeholder
+
+
+# Used internally by `Registry` for each item in its sorted list.
+# Provides an easier to read API when editing the code later.
+# For example, `item.name` is more clear than `item[0]`.
+_PriorityItem = namedtuple('PriorityItem', ['name', 'priority'])
+
+
+class Registry(object):
+ """
+ A priority sorted registry.
+
+ A `Registry` instance provides two public methods to alter the data of the
+ registry: `register` and `deregister`. Use `register` to add items and
+ `deregister` to remove items. See each method for specifics.
+
+ When registering an item, a "name" and a "priority" must be provided. All
+ items are automatically sorted by "priority" from highest to lowest. The
+ "name" is used to remove ("deregister") and get items.
+
+ A `Registry` instance it like a list (which maintains order) when reading
+ data. You may iterate over the items, get an item and get a count (length)
+ of all items. You may also check that the registry contains an item.
+
+ When getting an item you may use either the index of the item or the
+ string-based "name". For example:
+
+ registry = Registry()
+ registry.register(SomeItem(), 'itemname', 20)
+ # Get the item by index
+ item = registry[0]
+ # Get the item by name
+ item = registry['itemname']
+
+ When checking that the registry contains an item, you may use either the
+ string-based "name", or a reference to the actual item. For example:
+
+ someitem = SomeItem()
+ registry.register(someitem, 'itemname', 20)
+ # Contains the name
+ assert 'itemname' in registry
+ # Contains the item instance
+ assert someitem in registry
+
+ The method `get_index_for_name` is also available to obtain the index of
+ an item using that item's assigned "name".
+ """
+
+ def __init__(self):
+ self._data = {}
+ self._priority = []
+ self._is_sorted = False
+
+ def __contains__(self, item):
+ if isinstance(item, string_type):
+ # Check if an item exists by this name.
+ return item in self._data.keys()
+ # Check if this instance exists.
+ return item in self._data.values()
+
+ def __iter__(self):
+ self._sort()
+ return iter([self._data[k] for k, p in self._priority])
+
+ def __getitem__(self, key):
+ self._sort()
+ if isinstance(key, slice):
+ data = Registry()
+ for k, p in self._priority[key]:
+ data.register(self._data[k], k, p)
+ return data
+ if isinstance(key, int):
+ return self._data[self._priority[key].name]
+ return self._data[key]
+
+ def __len__(self):
+ return len(self._priority)
+
+ def __repr__(self):
+ return '<{0}({1})>'.format(self.__class__.__name__, list(self))
+
+ def get_index_for_name(self, name):
+ """
+ Return the index of the given name.
+ """
+ if name in self:
+ self._sort()
+ return self._priority.index(
+ [x for x in self._priority if x.name == name][0]
+ )
+ raise ValueError('No item named "{0}" exists.'.format(name))
+
+ def register(self, item, name, priority):
+ """
+ Add an item to the registry with the given name and priority.
+
+ Parameters:
+
+ * `item`: The item being registered.
+ * `name`: A string used to reference the item.
+ * `priority`: An integer or float used to sort against all items.
+
+ If an item is registered with a "name" which already exists, the
+ existing item is replaced with the new item. Tread carefully as the
+ old item is lost with no way to recover it. The new item will be
+ sorted according to its priority and will **not** retain the position
+ of the old item.
+ """
+ if name in self:
+ # Remove existing item of same name first
+ self.deregister(name)
+ self._is_sorted = False
+ self._data[name] = item
+ self._priority.append(_PriorityItem(name, priority))
+
+ def deregister(self, name, strict=True):
+ """
+ Remove an item from the registry.
+
+ Set `strict=False` to fail silently.
+ """
+ try:
+ index = self.get_index_for_name(name)
+ del self._priority[index]
+ del self._data[name]
+ except ValueError:
+ if strict:
+ raise
+
+ def _sort(self):
+ """
+ Sort the registry by priority from highest to lowest.
+
+ This method is called internally and should never be explicitly called.
+ """
+ if not self._is_sorted:
+ self._priority.sort(key=lambda item: item.priority, reverse=True)
+ self._is_sorted = True
+
+ # Deprecated Methods which provide a smooth transition from OrderedDict
+
+ def __setitem__(self, key, value):
+ """ Register item with priorty 5 less than lowest existing priority. """
+ if isinstance(key, string_type):
+ warnings.warn(
+ 'Using setitem to register a processor or pattern is deprecated. '
+ 'Use the `register` method instead.', DeprecationWarning
+ )
+ if key in self:
+ # Key already exists, replace without altering priority
+ self._data[key] = value
+ return
+ if len(self) == 0:
+ # This is the first item. Set priority to 50.
+ priority = 50
+ else:
+ self._sort()
+ priority = self._priority[-1].priority - 5
+ self.register(value, key, priority)
+ else:
+ raise TypeError
+
+ def __delitem__(self, key):
+ """ Deregister an item by name. """
+ if key in self:
+ self.deregister(key)
+ warnings.warn(
+ 'Using del to remove a processor or pattern is deprecated. '
+ 'Use the `deregister` method instead.', DeprecationWarning
+ )
+ else:
+ raise TypeError
+
+ def add(self, key, value, location):
+ """ Register a key by location. """
+ if len(self) == 0:
+ # This is the first item. Set priority to 50.
+ priority = 50
+ elif location == '_begin':
+ self._sort()
+ # Set priority 5 greater than highest existing priority
+ priority = self._priority[0].priority + 5
+ elif location == '_end':
+ self._sort()
+ # Set priority 5 less than lowest existing priority
+ priority = self._priority[-1].priority - 5
+ elif location.startswith('<') or location.startswith('>'):
+ # Set priority halfway between existing priorities.
+ i = self.get_index_for_name(location[1:])
+ if location.startswith('<'):
+ after = self._priority[i].priority
+ if i > 0:
+ before = self._priority[i-1].priority
+ else:
+ # Location is first item`
+ before = after + 10
+ else:
+ # location.startswith('>')
+ before = self._priority[i].priority
+ if i < len(self) - 1:
+ after = self._priority[i+1].priority
+ else:
+ # location is last item
+ after = before - 10
+ priority = before - ((before - after) / 2)
+ else:
+ raise ValueError('Not a valid location: "%s". Location key '
+ 'must start with a ">" or "<".' % location)
+ self.register(value, key, priority)
+ warnings.warn(
+ 'Using the add method to register a processor or pattern is deprecated. '
+ 'Use the `register` method instead.', DeprecationWarning
+ )
--- /dev/null
+site_name: Python-Markdown
+site_url: https://Python-Markdown.github.io/
+repo_url: https://github.com/Python-Markdown/markdown
+site_author: "The Python-Markdown Project"
+copyright: "Copyright © 2010-2017"
+
+use_directory_urls: true
+
+theme:
+ name: nature
+ icon: py.png
+ release: 3.0.dev
+ issue_tracker: https://github.com/Python-Markdown/markdown/issues
+
+nav:
+ - Python-Markdown: index.md
+ - Installation: install.md
+ - Library Reference: reference.md
+ - Command Line: cli.md
+ - Extensions: extensions/index.md
+ - Officially Supported Extensions:
+ - Abbreviations: extensions/abbreviations.md
+ - Admonition: extensions/admonition.md
+ - Attribute Lists: extensions/attr_list.md
+ - CodeHilite: extensions/code_hilite.md
+ - Definition Lists: extensions/definition_lists.md
+ - Extra: extensions/extra.md
+ - Fenced Code Blocks: extensions/fenced_code_blocks.md
+ - Footnotes: extensions/footnotes.md
+ - Legacy Attributes: extensions/legacy_attr.md
+ - Legacy Emphasis: extensions/legacy_em.md
+ - Meta-Data: extensions/meta_data.md
+ - New Line to Break: extensions/nl2br.md
+ - Sane Lists: extensions/sane_lists.md
+ - SmartyPants: extensions/smarty.md
+ - Table of Contents: extensions/toc.md
+ - Tables: extensions/tables.md
+ - WikiLinks: extensions/wikilinks.md
+ - Extension API: extensions/api.md
+ - Test Tools: test_tools.md
+ - Change Log: change_log/index.md
+ - Release Notes for v.3.0: change_log/release-3.0.md
+ - Release Notes for v.2.6: change_log/release-2.6.md
+ - Release Notes for v.2.5: change_log/release-2.5.md
+ - Release Notes for v.2.4: change_log/release-2.4.md
+ - Release Notes for v.2.3: change_log/release-2.3.md
+ - Release Notes for v.2.2: change_log/release-2.2.md
+ - Release Notes for v.2.1: change_log/release-2.1.md
+ - Release Notes for v.2.0: change_log/release-2.0.md
+ - Authors: authors.md
+
+markdown_extensions:
+ - extra
+ - admonition
+ - smarty
+ - codehilite
+ - toc:
+ permalink: true
+++ /dev/null
-#!/usr/bin/env python
-
-import tests
-import os
-import sys
-
-if len(sys.argv) > 1 and sys.argv[1] == "update":
- if len(sys.argv) > 2:
- config = tests.get_config(os.path.dirname(sys.argv[2]))
- root, ext = os.path.splitext(sys.argv[2])
- if ext == config.get(
- config.get_section(os.path.basename(root)), 'input_ext'
- ):
- tests.generate(root, config)
- else:
- print(
- sys.argv[2],
- 'does not have a valid file extension. Check config.'
- )
- else:
- tests.generate_all()
-else:
- tests.run()
-[nosetests]
+[bdist_wheel]
+universal = 1
+
+[metadata]
+license_file = LICENSE.md
+
+[egg_info]
+tag_build =
+tag_date = 0
+
#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
-from __future__ import with_statement
-import sys
-import os
-from distutils.core import setup
-from distutils.command.install_scripts import install_scripts
-from distutils.command.build import build
-from distutils.core import Command
-from distutils.util import change_root, newer
-import codecs
-import imp
+A Python implementation of John Gruber's Markdown.
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
-def get_version():
- " Get version & version_info without importing markdown.__init__ "
- path = os.path.join(os.path.dirname(__file__), 'markdown')
- fp, pathname, desc = imp.find_module('__version__', [path])
- try:
- v = imp.load_module('__version__', fp, pathname, desc)
- return v.version, v.version_info
- finally:
- fp.close()
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+
+from setuptools import setup
+from markdown import __version__, __version_info__
-version, version_info = get_version()
# Get development Status for classifiers
dev_status_map = {
'rc': '4 - Beta',
'final': '5 - Production/Stable'
}
-if version_info[3] == 'alpha' and version_info[4] == 0:
+if __version_info__[3] == 'alpha' and __version_info__[4] == 0:
DEVSTATUS = '2 - Pre-Alpha'
else:
- DEVSTATUS = dev_status_map[version_info[3]]
+ DEVSTATUS = dev_status_map[__version_info__[3]]
# The command line script name. Currently set to "markdown_py" so as not to
-# conflict with the perl implimentation (which uses "markdown"). We can't use
-# "markdown.py" as the default config on some systems will cause the script to
-# try to import itself rather than the library which will raise an error.
+# conflict with the perl implimentation (which uses "markdown").
SCRIPT_NAME = 'markdown_py'
-class md_install_scripts(install_scripts):
-
- """ Customized install_scripts. Create markdown_py.bat for win32. """
-
- def run(self):
- install_scripts.run(self)
-
- if sys.platform == 'win32':
- try:
- script_dir = os.path.join(sys.prefix, 'Scripts')
- script_path = os.path.join(script_dir, SCRIPT_NAME)
- bat_str = '@"%s" "%s" %%*' % (sys.executable, script_path)
- bat_path = os.path.join(
- self.install_dir, '%s.bat' % SCRIPT_NAME
- )
- f = open(bat_path, 'w')
- f.write(bat_str)
- f.close()
- print('Created: %s' % bat_path)
- except Exception:
- _, err, _ = sys.exc_info() # for both 2.x & 3.x compatability
- print('ERROR: Unable to create %s: %s' % (bat_path, err))
-
-
-class build_docs(Command):
-
- """ Build markdown documentation into html."""
-
- description = '"build" documentation (convert markdown text to html)'
-
- user_options = [
- ('build-base=', 'd', 'directory to "build" to'),
- ('force', 'f', 'forcibly build everything (ignore file timestamps)'),
- ]
-
- boolean_options = ['force']
-
- def initialize_options(self):
- self.build_base = None
- self.force = None
- self.docs = None
- self.sitemap = ''
-
- def finalize_options(self):
- self.set_undefined_options(
- 'build',
- ('build_base', 'build_base'),
- ('force', 'force')
- )
- self.docs = self._get_docs()
-
- def _get_docs(self):
- for root, dirs, files in os.walk('docs'):
- for file in files:
- if not file.startswith('_'):
- path = os.path.join(root, file)
- yield path
-
- def _get_context(self, src, path):
- """ Build and return context to pass to template. """
- # set defaults
- c = {
- 'title': '',
- 'prev_url': '',
- 'prev_title': '',
- 'next_url': '',
- 'next_title': '',
- 'crumb': '',
- 'version': version,
- }
- c['body'] = self.md.convert(src)
- c['toc'] = self.md.toc
- for k, v in self.md.Meta.items():
- c[k] = ' '.join(v)
- self.md.reset()
- # Manipulate path
- path = path[len(os.path.join(self.build_base, 'docs/')):]
- dir, file = os.path.split(path)
- name, ext = os.path.splitext(file)
- parts = [x for x in dir.split(os.sep) if x]
- c['source'] = '%s.txt' % name
- c['base'] = '../' * len(parts)
- # Build page title
- if name.lower() != 'index' or parts:
- c['page_title'] = '%s — Python Markdown' % c['title']
- else:
- c['page_title'] = 'Python Markdown'
- # Build crumb trail
- crumbs = []
- ctemp = '<li><a href="%s">%s</a> »</li>'
- for n, part in enumerate(parts):
- href = ('../' * n) + 'index.html'
- label = part.replace('_', ' ').capitalize()
- crumbs.append(ctemp % (href, label))
- if c['title'] and name.lower() != 'index':
- crumbs.append(ctemp % (file, c['title']))
- c['crumb'] = '\n'.join(crumbs)
- return c
-
- def run(self):
- # Before importing markdown, tweak sys.path to import from the
- # build directory (2to3 might have run on the library).
- bld_cmd = self.get_finalized_command("build")
- sys.path.insert(0, bld_cmd.build_lib)
- try:
- import markdown
- except ImportError:
- print('skipping build_docs: Markdown "import" failed!')
- else:
- with codecs.open('docs/_template.html', encoding='utf-8') as f:
- template = f.read()
- self.md = markdown.Markdown(
- extensions=[
- 'extra',
- 'toc(permalink=true)',
- 'meta',
- 'admonition',
- 'smarty'
- ]
- )
- for infile in self.docs:
- outfile, ext = os.path.splitext(infile)
- if ext == '.txt':
- # Copy src to .txt file
- srcfile = outfile + '.txt'
- srcfile = change_root(self.build_base, srcfile)
- self.mkpath(os.path.split(srcfile)[0])
- self.copy_file(infile, srcfile)
- # Render html file
- outfile += '.html'
- outfile = change_root(self.build_base, outfile)
- self.mkpath(os.path.split(outfile)[0])
- if self.force or newer(infile, outfile):
- if self.verbose:
- print('Converting %s -> %s' % (infile, outfile))
- if not self.dry_run:
- with codecs.open(infile, encoding='utf-8') as f:
- src = f.read()
- out = template % self._get_context(src, outfile)
- doc = open(outfile, 'wb')
- doc.write(out.encode('utf-8'))
- doc.close()
- else:
- outfile = change_root(self.build_base, infile)
- self.mkpath(os.path.split(outfile)[0])
- self.copy_file(infile, outfile)
-
-
-class md_build(build):
-
- """ Run "build_docs" command from "build" command. """
-
- user_options = build.user_options + [
- ('no-build-docs', None, 'do not build documentation'),
- ]
-
- boolean_options = build.boolean_options + ['build-docs']
-
- def initialize_options(self):
- build.initialize_options(self)
- self.no_build_docs = False
-
- def has_docs(self):
- return not self.no_build_docs
-
- sub_commands = build.sub_commands + [('build_docs', has_docs)]
-
long_description = '''
This is a Python implementation of John Gruber's Markdown_.
It is almost completely compliant with the reference implementation,
supported by the `Available Extensions`_.
.. _Markdown: http://daringfireball.net/projects/markdown/
-.. _Features: https://pythonhosted.org/Markdown/index.html#Features
-.. _`Available Extensions`: https://pythonhosted.org/Markdown/extensions/index.html
+.. _Features: https://Python-Markdown.github.io#features
+.. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
Support
=======
`mailing list`_ and report bugs on the `bug tracker`_.
.. _`mailing list`: http://lists.sourceforge.net/lists/listinfo/python-markdown-discuss
-.. _`bug tracker`: http://github.com/waylan/Python-Markdown/issues
+.. _`bug tracker`: http://github.com/Python-Markdown/markdown/issues
'''
+
setup(
name='Markdown',
- version=version,
- url='https://pythonhosted.org/Markdown/',
- download_url='http://pypi.python.org/packages/source/M/Markdown/Markdown-%s.tar.gz' % version,
+ version=__version__,
+ url='https://Python-Markdown.github.io/',
+ download_url='http://pypi.python.org/packages/source/M/Markdown/Markdown-%s-py2.py3-none-any.whl' % __version__,
description='Python implementation of Markdown.',
long_description=long_description,
author='Manfred Stienstra, Yuri takhteyev and Waylan limberg',
- author_email='waylan.limberg [at] icloud.com',
+ author_email='waylan.limberg@icloud.com',
maintainer='Waylan Limberg',
- maintainer_email='waylan.limberg [at] icloud.com',
+ maintainer_email='waylan.limberg@icloud.com',
license='BSD License',
packages=['markdown', 'markdown.extensions'],
- scripts=['bin/%s' % SCRIPT_NAME],
- cmdclass={
- 'install_scripts': md_install_scripts,
- 'build_docs': build_docs,
- 'build': md_build
+ python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*',
+ 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',
+ 'smarty = markdown.extensions.smarty:SmartyExtension',
+ 'tables = markdown.extensions.tables:TableExtension',
+ 'toc = markdown.extensions.toc:TocExtension',
+ 'wikilinks = markdown.extensions.wikilinks:WikiLinkExtension',
+ 'legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension',
+ 'legacy_em = markdown.extensions.legacy_em:LegacyEmExtension',
+ ]
},
classifiers=[
'Development Status :: %s' % DEVSTATUS,
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
- 'Programming Language :: Python :: 3.2',
- 'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
+ 'Programming Language :: Python :: 3.5',
+ 'Programming Language :: Python :: 3.6',
+ 'Programming Language :: Python :: 3.7',
'Topic :: Communications :: Email :: Filters',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
'Topic :: Internet :: WWW/HTTP :: Site Management',
--- /dev/null
+coverage<4.0
+pyyaml
+pytidylib
+mkdocs>=1.0
+mkdocs-nature
-import os
-import markdown
-import codecs
-import difflib
-try:
- import nose
-except ImportError as e:
- msg = e.args[0]
- msg = msg + ". The nose testing framework is required to run the Python-" \
- "Markdown tests. Run `pip install nose` to install the latest version."
- e.args = (msg,) + e.args[1:]
- raise
-from .plugins import HtmlOutput, Markdown, MarkdownSyntaxError
-try:
- import tidylib
-except ImportError:
- tidylib = None
-try:
- import yaml
-except ImportError as e:
- msg = e.args[0]
- msg = msg + ". A YAML library is required to run the Python-Markdown " \
- "tests. Run `pip install pyyaml` to install the latest version."
- e.args = (msg,) + e.args[1:]
- raise
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
-test_dir = os.path.abspath(os.path.dirname(__file__))
+A Python implementation of John Gruber's Markdown.
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
-class YamlConfig():
- def __init__(self, defaults, filename):
- """ Set defaults and load config file if it exists. """
- self.DEFAULT_SECTION = 'DEFAULT'
- self._defaults = defaults
- self._config = {}
- if os.path.exists(filename):
- with codecs.open(filename, encoding="utf-8") as f:
- self._config = yaml.load(f)
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
- def get(self, section, option):
- """ Get config value for given section and option key. """
- if section in self._config and option in self._config[section]:
- return self._config[section][option]
- return self._defaults[option]
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
- def get_section(self, file):
- """ Get name of config section for given file. """
- filename = os.path.basename(file)
- if filename in self._config:
- return filename
- else:
- return self.DEFAULT_SECTION
-
- def get_args(self, file):
- """ Get args to pass to markdown from config for a given file. """
- args = {}
- section = self.get_section(file)
- if section in self._config:
- for key in self._config[section].keys():
- # Filter out args unique to testing framework
- if key not in self._defaults.keys():
- args[key] = self.get(section, key)
- return args
-
-
-def get_config(dir_name):
- """ Get config for given directory name. """
- defaults = {
- 'normalize': False,
- 'skip': False,
- 'input_ext': '.txt',
- 'output_ext': '.html'
- }
- config = YamlConfig(defaults, os.path.join(dir_name, 'test.cfg'))
- return config
-
-
-def normalize(text):
- """ Normalize whitespace for a string of html using tidylib. """
- output, errors = tidylib.tidy_fragment(text, options={
- 'drop_empty_paras': 0,
- 'fix_backslash': 0,
- 'fix_bad_comments': 0,
- 'fix_uri': 0,
- 'join_styles': 0,
- 'lower_literals': 0,
- 'merge_divs': 0,
- 'output_xhtml': 1,
- 'quote_ampersand': 0,
- 'newline': 'LF'
- })
- return output
-
-
-class CheckSyntax(object):
- def __init__(self, description=None):
- if description:
- self.description = 'TestSyntax: "%s"' % description
-
- def __call__(self, file, config):
- """ Compare expected output to actual output and report result. """
- cfg_section = config.get_section(file)
- if config.get(cfg_section, 'skip'):
- raise nose.plugins.skip.SkipTest('Test skipped per config.')
- input_file = file + config.get(cfg_section, 'input_ext')
- with codecs.open(input_file, encoding="utf-8") as f:
- input = f.read()
- output_file = file + config.get(cfg_section, 'output_ext')
- with codecs.open(output_file, encoding="utf-8") as f:
- # Normalize line endings
- # (on windows, git may have altered line endings).
- expected_output = f.read().replace("\r\n", "\n")
- output = markdown.markdown(input, **config.get_args(file))
- if tidylib and config.get(cfg_section, 'normalize'):
- # Normalize whitespace with tidylib before comparing.
- expected_output = normalize(expected_output)
- output = normalize(output)
- elif config.get(cfg_section, 'normalize'):
- # Tidylib is not available. Skip this test.
- raise nose.plugins.skip.SkipTest(
- 'Test skipped. Tidylib not available on system.'
- )
- diff = [l for l in difflib.unified_diff(
- expected_output.splitlines(True),
- output.splitlines(True),
- output_file,
- 'actual_output.html',
- n=3
- )]
- if diff:
- raise MarkdownSyntaxError(
- 'Output from "%s" failed to match expected '
- 'output.\n\n%s' % (input_file, ''.join(diff))
- )
-
-
-def TestSyntax():
- for dir_name, sub_dirs, files in os.walk(test_dir):
- # Get dir specific config settings.
- config = get_config(dir_name)
- # Loop through files and generate tests.
- for file in files:
- root, ext = os.path.splitext(file)
- if ext == config.get(config.get_section(file), 'input_ext'):
- path = os.path.join(dir_name, root)
- check_syntax = CheckSyntax(
- description=os.path.relpath(path, test_dir)
- )
- yield check_syntax, path, config
-
-
-def generate(file, config):
- """ Write expected output file for given input. """
- cfg_section = config.get_section(file)
- if config.get(cfg_section, 'skip') or config.get(cfg_section, 'normalize'):
- print('Skipping:', file)
- return None
- input_file = file + config.get(cfg_section, 'input_ext')
- output_file = file + config.get(cfg_section, 'output_ext')
- if not os.path.isfile(output_file) or \
- os.path.getmtime(output_file) < os.path.getmtime(input_file):
- print('Generating:', file)
- markdown.markdownFromFile(input=input_file, output=output_file,
- encoding='utf-8', **config.get_args(file))
- else:
- print('Already up-to-date:', file)
-
-
-def generate_all():
- """ Generate expected output for all outdated tests. """
- for dir_name, sub_dirs, files in os.walk(test_dir):
- # Get dir specific config settings.
- config = get_config(dir_name)
- # Loop through files and generate tests.
- for file in files:
- root, ext = os.path.splitext(file)
- if ext == config.get(config.get_section(file), 'input_ext'):
- generate(os.path.join(dir_name, root), config)
-
-
-def run():
- nose.main(addplugins=[HtmlOutput(), Markdown()])
+License: BSD (see LICENSE.md for details).
+"""
<hr class="foo" id="bar" >
-<p><some <a href="http://example.com">weird</a> stuff></p>
\ No newline at end of file
+<p><some <a href="http://example.com">weird</a> stuff></p>
+<p><some>> <<unbalanced>> <<brackets></p>
\ No newline at end of file
<hr class="foo" id="bar" >
<some [weird](http://example.com) stuff>
+
+<some>> <<unbalanced>> <<brackets>
\ No newline at end of file
<h2>Lists</h2>
<p>Unordered (bulleted) lists use asterisks, pluses, and hyphens (<code>*</code>,
<code>+</code>, and <code>-</code>) as list markers. These three markers are
-interchangable; this:</p>
+interchangeable; this:</p>
<pre><code>* Candy.
* Gum.
* Booze.
Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
`+`, and `-`) as list markers. These three markers are
-interchangable; this:
+interchangeable; this:
* Candy.
* Gum.
<h3 id="list">Lists</h3>
<p>Markdown supports ordered (numbered) and unordered (bulleted) lists.</p>
-<p>Unordered lists use asterisks, pluses, and hyphens -- interchangably
+<p>Unordered lists use asterisks, pluses, and hyphens -- interchangeably
-- as list markers:</p>
<pre><code>* Red
* Green
Markdown supports ordered (numbered) and unordered (bulleted) lists.
-Unordered lists use asterisks, pluses, and hyphens -- interchangably
+Unordered lists use asterisks, pluses, and hyphens -- interchangeably
-- as list markers:
* Red
<p>For something completely different.</p>
<p>You can also use a custom CSS class name.</p>
</div>
+<div class="admonition class1 class2 class3">
+<p class="admonition-title">And now...</p>
+<p>For something completely different.</p>
+<p>Several class names can be separated by space chars.</p>
+</div>
+<div class="admonition note anotherclass">
+<p class="admonition-title">Note</p>
+<p>The default title is the capitalized first class name.</p>
+</div>
<div class="admonition tip">
<p>An explicitly empty string prevents the title from being rendered.</p>
+</div>
+<p>No body:</p>
+<div class="admonition note">
+<p class="admonition-title">Note</p>
+</div>
+<p>Extra whitespace after the title should not alter output:</p>
+<div class="admonition note">
+<p class="admonition-title">Note</p>
</div>
\ No newline at end of file
You can also use a custom CSS class name.
+!!! class1 class2 class3 "And now..."
+ For something completely different.
+
+ Several class names can be separated by space chars.
+
+!!! note anotherclass
+ The default title is the capitalized first class name.
+
!!! tip ""
An explicitly empty string prevents the title from being rendered.
+
+No body:
+
+!!! note
+
+Extra whitespace after the title should not alter output:
+
+!!! note
# This should be ignored.
{: #someid .someclass }
</code></pre>
-<h3 id="hash3">No colon for compatability with Headerid ext</h3>
+<h3 id="hash3">No colon for compatibility with Headerid ext</h3>
<p id="the_end">Also a codespan: <code class="foo">{: .someclass}</code>.</p>
<h3 _:="{:" id="hash5">Bad Syntax</h3>
<ul>
<dd><em class="inline">dd</em></dd>
<dt><em class="inline">DT3</em></dt>
<dd>Some dd</dd>
-</dl>
\ No newline at end of file
+</dl>
+<h1>Bad attributes</h1>
+<p>Key without <em foo="foo">value</em></p>
+<p>Value without <em>key</em></p>
+<p>No <em>key or value</em></p>
+<p><em>Weirdness</em></p>
+<p><em>More weirdness</em></p>
+<p>This should not cause a <em foo="a">crash</em></p>
+<p>Attr_lists do not contain <em>newlines</em>{ foo=bar
+key=value }</p>
\ No newline at end of file
# This should be ignored.
{: #someid .someclass }
-### No colon for compatability with Headerid ext { #hash3 }
+### No colon for compatibility with Headerid ext { #hash3 }
Also a codespan: `{: .someclass}`{: .foo}.
{: #the_end}
*DT3*{.inline}
: Some dd
+# Bad attributes
+
+Key without *value*{ foo= }
+
+Value without *key*{ =bar }
+
+No *key or value*{ = }
+
+*Weirdness*{ == }
+
+*More weirdness*{ === }
+
+This should not cause a *crash*{ foo=a=b }
+
+Attr_lists do not contain *newlines*{ foo=bar
+key=value }
<p>This is the body with a footnote<sup id="fnref:1"><a class="footnote-ref" href="#fn:1" rel="footnote">1</a></sup> or two<sup id="fnref:2"><a class="footnote-ref" href="#fn:2" rel="footnote">2</a></sup> or more<sup id="fnref:3"><a class="footnote-ref" href="#fn:3" rel="footnote">3</a></sup> <sup id="fnref:4"><a class="footnote-ref" href="#fn:4" rel="footnote">4</a></sup> <sup id="fnref:5"><a class="footnote-ref" href="#fn:5" rel="footnote">5</a></sup>.</p>
<p>Also a reference that does not exist[^6].</p>
+<p>Duplicate<sup id="fnref:a"><a class="footnote-ref" href="#fn:a" rel="footnote">6</a></sup> footnotes<sup id="fnref2:a"><a class="footnote-ref" href="#fn:a" rel="footnote">6</a></sup> test<sup id="fnref3:a"><a class="footnote-ref" href="#fn:a" rel="footnote">6</a></sup>.</p>
+<p>Duplicate<sup id="fnref:b"><a class="footnote-ref" href="#fn:b" rel="footnote">7</a></sup> footnotes<sup id="fnref2:b"><a class="footnote-ref" href="#fn:b" rel="footnote">7</a></sup> test<sup id="fnref3:b"><a class="footnote-ref" href="#fn:b" rel="footnote">7</a></sup>.</p>
+<p>Single after duplicates<sup id="fnref:c"><a class="footnote-ref" href="#fn:c" rel="footnote">8</a></sup>.</p>
+<p>Test emphasis at end of footnote<sup id="fnref:d"><a class="footnote-ref" href="#fn:d" rel="footnote">9</a></sup></p>
+<p>Complex footnote content<sup id="fnref:e"><a class="footnote-ref" href="#fn:e" rel="footnote">10</a></sup></p>
<div class="footnote">
<hr />
<ol>
Second line of first paragraph is not intended.
Nor is third... <a class="footnote-backref" href="#fnref:5" rev="footnote" title="Jump back to footnote 5 in the text">↩</a></p>
</li>
+<li id="fn:a">
+<p>1 <a class="footnote-backref" href="#fnref:a" rev="footnote" title="Jump back to footnote 6 in the text">↩</a><a class="footnote-backref" href="#fnref2:a" rev="footnote" title="Jump back to footnote 6 in the text">↩</a><a class="footnote-backref" href="#fnref3:a" rev="footnote" title="Jump back to footnote 6 in the text">↩</a></p>
+</li>
+<li id="fn:b">
+<p>2 <a class="footnote-backref" href="#fnref:b" rev="footnote" title="Jump back to footnote 7 in the text">↩</a><a class="footnote-backref" href="#fnref2:b" rev="footnote" title="Jump back to footnote 7 in the text">↩</a><a class="footnote-backref" href="#fnref3:b" rev="footnote" title="Jump back to footnote 7 in the text">↩</a></p>
+</li>
+<li id="fn:c">
+<p>3 <a class="footnote-backref" href="#fnref:c" rev="footnote" title="Jump back to footnote 8 in the text">↩</a></p>
+</li>
+<li id="fn:d">
+<p><em>emphasis works</em></p>
+<p><em>emphasis still works</em> <a class="footnote-backref" href="#fnref:d" rev="footnote" title="Jump back to footnote 9 in the text">↩</a></p>
+</li>
+<li id="fn:e">
+<ol>
+<li>
+<p>The top couple half figure, contrary sides and hands across with bottom couple,</p>
+<p>Half figure back on your own sides, and turn partner to places,</p>
+<p>Swing partners with right hands into straight line long-ways, as in a reel, and</p>
+<p>Set,</p>
+<p>Hey and return to places,</p>
+<p>The other three couples do the same.</p>
+</li>
+<li>
+<p>Top and bottom couples meet and set,</p>
+<p>Then each gentleman leas the opposite lady to the couple on his left, and set,</p>
+<p>Aach four right and left,</p>
+<p>Swing side couples to places, and turn partners all eight,</p>
+<p>The other two couple o the same.</p>
+</li>
+</ol>
+<p><a class="footnote-backref" href="#fnref:e" rev="footnote" title="Jump back to footnote 10 in the text">↩</a></p>
+</li>
</ol>
</div>
\ No newline at end of file
Also a reference that does not exist[^6].
+Duplicate[^a] footnotes[^a] test[^a].
+
+Duplicate[^b] footnotes[^b] test[^b].
+
+Single after duplicates[^c].
+
+Test emphasis at end of footnote[^d]
+
+Complex footnote content[^e]
+
[^1]: Footnote that ends with a list:
* item 1
[^5]: First line of first paragraph.
Second line of first paragraph is not intended.
Nor is third...
+
+[^a]: 1
+[^b]: 2
+[^c]: 3
+
+[^d]:
+ _emphasis works_
+
+ _emphasis still works_
+
+[^e]:
+ 1. The top couple half figure, contrary sides and hands across with bottom couple,
+
+ Half figure back on your own sides, and turn partner to places,
+
+ Swing partners with right hands into straight line long-ways, as in a reel, and
+
+ Set,
+
+ Hey and return to places,
+
+ The other three couples do the same.
+
+ 2. Top and bottom couples meet and set,
+
+ Then each gentleman leas the opposite lady to the couple on his left, and set,
+
+ Aach four right and left,
+
+ Swing side couples to places, and turn partners all eight,
+
+ The other two couple o the same.
--- /dev/null
+<blockquote>
+<blockquote>
+<div class="footnote">
+<hr />
+<ol>
+<li id="fn:1">
+<p>A Footnote. <a class="footnote-backref" href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text">↩</a></p>
+</li>
+</ol>
+</div>
+<p>Some text with a footnote<sup id="fnref:1"><a class="footnote-ref" href="#fn:1" rel="footnote">1</a></sup>.</p>
+</blockquote>
+</blockquote>
\ No newline at end of file
--- /dev/null
+>> ///Footnotes Go Here///
+>>
+>> Some text with a footnote[^1].
+
+[^1]: A Footnote.
<h3 id="list">Lists</h3>
<p>Markdown supports ordered (numbered) and unordered (bulleted) lists.</p>
-<p>Unordered lists use asterisks, pluses, and hyphens -- interchangably
+<p>Unordered lists use asterisks, pluses, and hyphens -- interchangeably
-- as list markers:</p>
<pre><code>* Red
* Green
Markdown supports ordered (numbered) and unordered (bulleted) lists.
-Unordered lists use asterisks, pluses, and hyphens -- interchangably
+Unordered lists use asterisks, pluses, and hyphens -- interchangeably
-- as list markers:
* Red
<p>Markdown is <em>still</em> active here.</p>
</div>
-<p>Markdown is <em>active again</em> here.</p>
\ No newline at end of file
+<p>Markdown is <em>active again</em> here.</p>
+<div>
+<p>foo bar</p>
+<p><em>bar</em>
+</p>
+</div>
+<div name="issue584">
+<div>
+<p><a href="http://example.com">link</a></p>
+</div>
+</div>
+<div name="issue584">
+<div>
+<p><abbr title="Abbreviation">abbr</abbr></p>
+</div>
+</div>
+<div name="issue584">
+<div>
+<p>footnote<sup id="fnref:1"><a class="footnote-ref" href="#fn:1" rel="footnote">1</a></sup></p>
+</div>
+</div>
+<div name="issue584">
+<div>
+<p><a href="http://example.com">link</a></p>
+</div>
+</div>
+<div name="issue584">
+<div>
+<p><abbr title="Abbreviation">abbr</abbr></p>
+</div>
+</div>
+<div name="issue584">
+<div>
+<p>footnote<sup id="fnref:2"><a class="footnote-ref" href="#fn:2" rel="footnote">2</a></sup></p>
+</div>
+</div>
+<div class="footnote">
+<hr />
+<ol>
+<li id="fn:1">
+<ol>
+<li>
+<p>The top couple half figure, contrary sides and hands across with bottom couple,</p>
+<p>Half figure back on your own sides, and turn partner to places,</p>
+<p>Swing partners with right hands into straight line long-ways, as in a reel, and</p>
+<p>Set,</p>
+<p>Hey and return to places,</p>
+<p>The other three couples do the same.</p>
+</li>
+<li>
+<p>Top and bottom couples meet and set,</p>
+<p>Then each gentleman leas the opposite lady to the couple on his left, and set,</p>
+<p>Aach four right and left,</p>
+<p>Swing side couples to places, and turn partners all eight,</p>
+<p>The other two couple o the same.</p>
+</li>
+</ol>
+<p><a class="footnote-backref" href="#fnref:1" rev="footnote" title="Jump back to footnote 1 in the text">↩</a></p>
+</li>
+<li id="fn:2">
+<ol>
+<li>
+<p>The top couple half figure, contrary sides and hands across with bottom couple,</p>
+<p>Half figure back on your own sides, and turn partner to places,</p>
+<p>Swing partners with right hands into straight line long-ways, as in a reel, and</p>
+<p>Set,</p>
+<p>Hey and return to places,</p>
+<p>The other three couples do the same.</p>
+</li>
+<li>
+<p>Top and bottom couples meet and set,</p>
+<p>Then each gentleman leas the opposite lady to the couple on his left, and set,</p>
+<p>Aach four right and left,</p>
+<p>Swing side couples to places, and turn partners all eight,</p>
+<p>The other two couple o the same.</p>
+</li>
+</ol>
+<p><a class="footnote-backref" href="#fnref:2" rev="footnote" title="Jump back to footnote 2 in the text">↩</a></p>
+</li>
+</ol>
+</div>
\ No newline at end of file
</div>
Markdown is *active again* here.
+
+<div markdown=1>
+foo bar
+
+<em>bar</em>
+</div>
+
+<div markdown="1" name="issue584">
+
+[link]: http://example.com
+
+<div markdown="1">
+[link][link]
+</div>
+
+</div>
+
+<div markdown="1" name="issue584">
+
+*[abbr]: Abbreviation
+
+<div markdown="1">
+abbr
+</div>
+
+</div>
+
+<div markdown="1" name="issue584">
+
+[^1]:
+ 1. The top couple half figure, contrary sides and hands across with bottom couple,
+
+ Half figure back on your own sides, and turn partner to places,
+
+ Swing partners with right hands into straight line long-ways, as in a reel, and
+
+ Set,
+
+ Hey and return to places,
+
+ The other three couples do the same.
+
+ 2. Top and bottom couples meet and set,
+
+ Then each gentleman leas the opposite lady to the couple on his left, and set,
+
+ Aach four right and left,
+
+ Swing side couples to places, and turn partners all eight,
+
+ The other two couple o the same.
+
+<div markdown="1">
+footnote[^1]
+</div>
+
+</div>
+
+<div markdown="1" name="issue584">
+
+[link]: http://example.com
+
+
+
+
+<div markdown="1">
+[link][link]
+</div>
+
+</div>
+
+<div markdown="1" name="issue584">
+
+*[abbr]: Abbreviation
+
+
+
+
+<div markdown="1">
+abbr
+</div>
+
+</div>
+
+<div markdown="1" name="issue584">
+
+[^2]:
+ 1. The top couple half figure, contrary sides and hands across with bottom couple,
+
+ Half figure back on your own sides, and turn partner to places,
+
+ Swing partners with right hands into straight line long-ways, as in a reel, and
+
+ Set,
+
+ Hey and return to places,
+
+ The other three couples do the same.
+
+ 2. Top and bottom couples meet and set,
+
+ Then each gentleman leas the opposite lady to the couple on his left, and set,
+
+ Aach four right and left,
+
+ Swing side couples to places, and turn partners all eight,
+
+ The other two couple o the same.
+
+
+
+
+<div markdown="1">
+footnote[^2]
+</div>
+
+</div>
<table>
<thead>
<tr>
-<th>Item</th>
+<th align="left">Item</th>
<th align="right">Value</th>
</tr>
</thead>
<tbody>
<tr>
-<td>Computer</td>
+<td align="left">Computer</td>
<td align="right">$1600</td>
</tr>
<tr>
-<td>Phone</td>
+<td align="left">Phone</td>
<td align="right">$12</td>
</tr>
<tr>
-<td>Pipe</td>
+<td align="left">Pipe</td>
<td align="right">$1</td>
</tr>
</tbody>
------------ | -------------
Content Cell | Content Cell
Content Cell | Content Cell
-</code></pre>
\ No newline at end of file
+</code></pre>
+<table>
+<thead>
+<tr>
+<th>First Header</th>
+<th>Second Header</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td></td>
+<td></td>
+</tr>
+</tbody>
+</table>
+<p>More inline code block tests</p>
+<table>
+<thead>
+<tr>
+<th>Column 1</th>
+<th>Column 2</th>
+<th>Column 3</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>word 1</td>
+<td>word 2</td>
+<td>word 3</td>
+</tr>
+<tr>
+<td>word 1</td>
+<td><code>word 2</code></td>
+<td>word 3</td>
+</tr>
+<tr>
+<td>word 1</td>
+<td>`word 2</td>
+<td>word 3</td>
+</tr>
+<tr>
+<td>word 1</td>
+<td>`word 2</td>
+<td>word 3</td>
+</tr>
+<tr>
+<td>word 1</td>
+<td><code>word |2</code></td>
+<td>word 3</td>
+</tr>
+<tr>
+<td>words</td>
+<td><code>some | code</code></td>
+<td>more words</td>
+</tr>
+<tr>
+<td>words</td>
+<td><code>some | code</code></td>
+<td>more words</td>
+</tr>
+<tr>
+<td>words</td>
+<td><code>some | code</code></td>
+<td>more words</td>
+</tr>
+<tr>
+<td>words</td>
+<td><code>some ` | ` code</code></td>
+<td>more words</td>
+</tr>
+<tr>
+<td>words</td>
+<td><code>some ` | ` code</code></td>
+<td>more words</td>
+</tr>
+<tr>
+<td>words</td>
+<td><code>some ` | ` code</code></td>
+<td>more words</td>
+</tr>
+</tbody>
+</table>
+<p>A test for issue #440:</p>
+<table>
+<thead>
+<tr>
+<th>foo</th>
+<th>bar</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>foo</td>
+<td>(<code>bar</code>) and <code>baz</code>.</td>
+</tr>
+</tbody>
+</table>
+<p>Lists are not tables</p>
+<ul>
+<li>this | should | not</li>
+<li>be | a | table</li>
+</ul>
+<p>Add tests for issue #449</p>
+<table>
+<thead>
+<tr>
+<th>Odd backticks</th>
+<th>Even backticks</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~]</code></td>
+<td><code>[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^`_`{|}~]</code></td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Escapes</th>
+<th>More Escapes</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>`\</code></td>
+<td><code>\</code></td>
+</tr>
+</tbody>
+</table>
+<p>Only the first backtick can be escaped</p>
+<table>
+<thead>
+<tr>
+<th>Escaped</th>
+<th>Bacticks</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>`<code>\</code></td>
+<td>``</td>
+</tr>
+</tbody>
+</table>
+<p>Test escaped pipes</p>
+<table>
+<thead>
+<tr>
+<th>Column 1</th>
+<th>Column 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td><code>|</code> |</td>
+<td>Pipes are okay in code and escaped. |</td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Column 1</th>
+<th>Column 2</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>row1</td>
+<td>row1 |</td>
+</tr>
+<tr>
+<td>row2</td>
+<td>row2</td>
+</tr>
+</tbody>
+</table>
+<p>Test header escapes</p>
+<table>
+<thead>
+<tr>
+<th><code>`\</code> |</th>
+<th><code>\</code> |</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>row1</td>
+<td>row1</td>
+</tr>
+<tr>
+<td>row2</td>
+<td>row2</td>
+</tr>
+</tbody>
+</table>
+<p>Escaped pipes in format row should not be a table</p>
+<p>| Column1 | Column2 |
+| ------- || ------- |
+| row1 | row1 |
+| row2 | row2 |</p>
+<p>Test escaped code in Table</p>
+<table>
+<thead>
+<tr>
+<th>Should not be code</th>
+<th>Should be code</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>`Not code`</td>
+<td>\<code>code</code></td>
+</tr>
+<tr>
+<td>\`Not code\`</td>
+<td>\\<code>code</code></td>
+</tr>
+</tbody>
+</table>
+<p>Single column tables</p>
+<table>
+<thead>
+<tr>
+<th>Is a Table</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td></td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Is a Table</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td></td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Is a Table</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td></td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Is a Table</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>row</td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Is a Table</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>row</td>
+</tr>
+</tbody>
+</table>
+<table>
+<thead>
+<tr>
+<th>Is a Table</th>
+</tr>
+</thead>
+<tbody>
+<tr>
+<td>row</td>
+</tr>
+</tbody>
+</table>
+<h2>| Is not a Table</h2>
+<p>| row</p>
+<h2>Is not a Table |</h2>
+<p>row |</p>
+<p>| Is not a Table
+| --------------
+row</p>
+<p>Is not a Table |
+-------------- |
+row</p>
\ No newline at end of file
| Content Cell | Content Cell |
Four spaces is a code block:
-
+
First Header | Second Header
------------ | -------------
Content Cell | Content Cell
Content Cell | Content Cell
+
+| First Header | Second Header |
+| ------------ | ------------- |
+
+More inline code block tests
+
+Column 1 | Column 2 | Column 3
+---------|----------|---------
+word 1 | word 2 | word 3
+word 1 | `word 2` | word 3
+word 1 | \`word 2 | word 3
+word 1 | `word 2 | word 3
+word 1 | `word |2` | word 3
+words |`` some | code `` | more words
+words |``` some | code ``` | more words
+words |```` some | code ```` | more words
+words |`` some ` | ` code `` | more words
+words |``` some ` | ` code ``` | more words
+words |```` some ` | ` code ```` | more words
+
+A test for issue #440:
+
+foo | bar
+--- | ---
+foo | (`bar`) and `baz`.
+
+Lists are not tables
+
+ - this | should | not
+ - be | a | table
+
+Add tests for issue #449
+
+Odd backticks | Even backticks
+------------ | -------------
+``[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^_`{|}~]`` | ``[!\"\#$%&'()*+,\-./:;<=>?@\[\\\]^`_`{|}~]``
+
+Escapes | More Escapes
+------- | ------
+`` `\`` | `\`
+
+Only the first backtick can be escaped
+
+Escaped | Bacticks
+------- | ------
+\`` \` | \`\`
+
+Test escaped pipes
+
+Column 1 | Column 2
+-------- | --------
+`|` \| | Pipes are okay in code and escaped. \|
+
+| Column 1 | Column 2 |
+| -------- | -------- |
+| row1 | row1 \|
+| row2 | row2 |
+
+Test header escapes
+
+| `` `\`` \| | `\` \|
+| ---------- | ---- |
+| row1 | row1 |
+| row2 | row2 |
+
+Escaped pipes in format row should not be a table
+
+| Column1 | Column2 |
+| ------- \|| ------- |
+| row1 | row1 |
+| row2 | row2 |
+
+Test escaped code in Table
+
+Should not be code | Should be code
+------------------ | --------------
+\`Not code\` | \\`code`
+\\\`Not code\\\` | \\\\`code`
+
+Single column tables
+
+| Is a Table |
+| ---------- |
+
+| Is a Table
+| ----------
+
+Is a Table |
+---------- |
+
+| Is a Table |
+| ---------- |
+| row |
+
+| Is a Table
+| ----------
+| row
+
+Is a Table |
+---------- |
+row |
+
+| Is not a Table
+--------------
+| row
+
+Is not a Table |
+--------------
+row |
+
+| Is not a Table
+| --------------
+row
+
+Is not a Table |
+-------------- |
+row
+++ /dev/null
-DEFAULT:
- extensions:
- - markdown.extensions.extra
-
-loose_def_list:
- extensions:
- - markdown.extensions.def_list
-
-simple_def-lists:
- extensions:
- - markdown.extensions.def_list
-
-abbr:
- extensions:
- - markdown.extensions.abbr
-
-footnotes:
- extensions:
- - markdown.extensions.footnotes
-
-tables:
- extensions:
- - markdown.extensions.tables
-
-tables_and_attr_list:
- extensions:
- - markdown.extensions.tables
- - markdown.extensions.attr_list
-
-extra_config:
- extensions:
- - markdown.extensions.extra
- extension_configs:
- markdown.extensions.extra:
- markdown.extensions.footnotes:
- PLACE_MARKER: ~~~placemarker~~~
<ul>
<li>Unordered again
1. not a list item</li>
-</ul>
\ No newline at end of file
+</ul>
+<ol start="3">
+<li>Bird</li>
+<li>McHale</li>
+<li>Parish</li>
+</ol>
+<p>Not a list</p>
+<ol start="3">
+<li>Bird</li>
+<li>McHale</li>
+<li>Parish</li>
+</ol>
\ No newline at end of file
* Unordered again
1. not a list item
+
+3. Bird
+1. McHale
+8. Parish
+
+Not a list
+
+3. Bird
+1. McHale
+8. Parish
\ No newline at end of file
-<p>’.
+<p>’.<br />
1440–80’s<br />
-1440–‘80s<br />
-1440—‘80s<br />
+1440–’80s<br />
+1440—’80s<br />
1960s<br />
1960’s<br />
-one two ‘60s<br />
-‘60s</p>
+one two ’60s<br />
+’60s</p>
<p>It’s fun. What’s fun?<br />
“Isn’t this fun”? — she said…<br />
“‘Quoted’ words in a larger quote.”<br />
“<a href="http://example.com">Link</a>” — she said.</p>
<p>“Ellipsis within quotes…”</p>
<p>Кавычки-«ёлочки»<br />
+«hello»<br />
Anführungszeichen-»Chevrons«</p>
<hr />
<p>Escaped -- ndash<br />
Escaped ellipsis...</p>
<p>‘Escaped "quotes" in real ones’<br />
'“Real” quotes in escaped ones'</p>
-<p>Skip <code>"code" -- --- 'spans' ...</code>.</p>
+<p>Skip <code><<all>> "code" -- --- 'spans' ...</code>.</p>
<pre><code>Also skip "code" 'blocks'
foo -- bar --- baz ...
-</code></pre>
\ No newline at end of file
+</code></pre>
+<p>A line that ‘wraps’ with
+<em>emphasis</em> at the beginning of the next line.</p>
\ No newline at end of file
-'.
+'.
1440--80's
1440--'80s
1440---'80s
"Ellipsis within quotes..."
Кавычки-<<ёлочки>>
+<<hello>>
Anführungszeichen->>Chevrons<<
--- -- ---
'Escaped \"quotes\" in real ones'
\'"Real" quotes in escaped ones\'
-Skip `"code" -- --- 'spans' ...`.
+Skip `<<all>> "code" -- --- 'spans' ...`.
Also skip "code" 'blocks'
foo -- bar --- baz ...
+
+A line that 'wraps' with
+*emphasis* at the beginning of the next line.
+++ /dev/null
-attr_list:
- extensions:
- - markdown.extensions.attr_list
- - markdown.extensions.def_list
- - markdown.extensions.smarty
-
-codehilite:
- extensions:
- - markdown.extensions.codehilite
- # This passes or not based on version of pygments.
- skip: True
-
-toc:
- extensions:
- - markdown.extensions.toc
-
-toc_invalid:
- extensions:
- - markdown.extensions.toc
-
-toc_out_of_order:
- extensions:
- - markdown.extensions.toc
-
-toc_nested:
- extensions:
- - markdown.extensions.toc
- extension_configs:
- markdown.extensions.toc:
- permalink: True
-
-toc_nested2:
- extensions:
- - markdown.extensions.toc
- extension_configs:
- markdown.extensions.toc:
- permalink: "[link]"
-
-toc_nested_list:
- extensions:
- - markdown.extensions.toc
-
-wikilinks:
- extensions:
- - markdown.extensions.wikilinks
-
-fenced_code:
- extensions:
- - markdown.extensions.fenced_code
-
-github_flavored:
- extensions:
- - markdown.extensions.fenced_code
-
-sane_lists:
- extensions:
- - markdown.extensions.sane_lists
-
-nl2br_w_attr_list:
- extensions:
- - markdown.extensions.nl2br
- - markdown.extensions.attr_list
-
-admonition:
- extensions:
- - markdown.extensions.admonition
-
-smarty:
- extensions:
- - markdown.extensions.smarty
- extension_configs:
- markdown.extensions.smarty:
- smart_angled_quotes: True
Quote Level from the Text menu.</p>
<h2 id="lists">Lists</h2>
<p>Markdown supports ordered (numbered) and unordered (bulleted) lists.</p>
-<p>Unordered lists use asterisks, pluses, and hyphens -- interchangably
+<p>Unordered lists use asterisks, pluses, and hyphens -- interchangeably
-- as list markers:</p>
<pre><code>* Red
* Green
Markdown supports ordered (numbered) and unordered (bulleted) lists.
-Unordered lists use asterisks, pluses, and hyphens -- interchangably
+Unordered lists use asterisks, pluses, and hyphens -- interchangeably
-- as list markers:
* Red
+++ /dev/null
-<p><a href="http://www.freewisdom.org/this&that">link</a></p>
\ No newline at end of file
+++ /dev/null
-[link](http://www.freewisdom.org/this&that)
+++ /dev/null
-<p><img alt="img" id="foo" src="http://example.com/i.jpg" /></p>
\ No newline at end of file
+++ /dev/null
-![img{@id=foo}][img]
-
- [img]: http://example.com/i.jpg
-
+++ /dev/null
-<p id="TABLE.OF.CONTENTS"></p>
-<ul>
-<li id="TABLEOFCONTENTS"></li>
-</ul>
-<p id="TABLEOFCONTENTS">Or in the middle of the text </p>
-<p id="tableofcontents"></p>
\ No newline at end of file
+++ /dev/null
-{@id=TABLE.OF.CONTENTS}
-
-
-* {@id=TABLEOFCONTENTS}
-
-
-Or in the middle of the text {@id=TABLEOFCONTENTS}
-
-{@id=tableofcontents}
-
-<p>\`This should not be in code.\`
-`This also should not be in code.`
+<p>`This should not be in code.`
+\<code>This should be in code.\\</code>
+\`This should not be in code.\`
`And finally this should not be in code.`</p>
\ No newline at end of file
-\\`This should not be in code.\\`
-\`This also should not be in code.\`
+\`This should not be in code.\`
+\\`This should be in code.\\`
+\\\`This should not be in code.\\\`
\`And finally this should not be in code.`
<p><strong>Python</strong>(パイソン)は、<a href="http://en.wikipedia.org/wiki/Guido_van_Rossum">Guido van Rossum</a> によって作られたオープンソースのオブジェクト指向スクリプト言語。<a href="http://ja.wikipedia.org/wiki/Perl">Perl</a>とともに欧米で広く普及している。イギリスのテレビ局 BBC が製作したコメディ番組『空飛ぶモンティ・パイソン』にちなんで名付けられた。 (Pythonには、爬虫類のニシキヘビの意味があり、Python言語のマスコットやアイコンとして使われることがある。)</p>
<p>|||||||||||||||||||||||||||||THIS SHOULD BE LTR|||||||||||||||||||||||||</p>
-<p dir="rtl">|||||||||||||||||||||||||||||THIS SHOULD BE RTL||||||||||||||||||||||||| </p>
-<p dir="ltr">(<strong>بايثون</strong> لغة برمجة حديثة بسيطة، واضحة، سريعة ، تستخدم أسلوب البرمجة الكائنية (THIS SHOULD BE LTR ) وقابلة للتطوير بالإضافة إلى أنها مجانية و مفتوح </p>
+<p>|||||||||||||||||||||||||||||THIS SHOULD BE RTL||||||||||||||||||||||||| </p>
+<p>(<strong>بايثون</strong> لغة برمجة حديثة بسيطة، واضحة، سريعة ، تستخدم أسلوب البرمجة الكائنية (THIS SHOULD BE LTR ) وقابلة للتطوير بالإضافة إلى أنها مجانية و مفتوح </p>
<p>پایتون زبان برنامهنویسی تفسیری و سطح بالا ، شیگرا و یک زبان برنامهنویسی تفسیری سمت سرور قدرتمند است که توسط گیدو ون روسوم در سال ۱۹۹۰ ساخته شد. این زبان در ویژگیها شبیه پرل، روبی، اسکیم، اسمالتاک و تیسیال است و از مدیریت خودکار حافظه استفاده میکند</p>
<p>Python,是一种面向对象的、直譯式的计算机程序设计语言,也是一种功能强大而完善的通用型语言,已经具有十多年的发展历史,成熟且稳定。</p>
<p>ބްލޫ ވޭލްގެ ދޫ މަތީގައި އެއްފަހަރާ 50 މީހުންނަށް ތިބެވިދާނެވެ. ބޮޑު މަހުގެ ދުލަކީ އެހާމެ ބޮޑު އެއްޗެކެވެ.</p>
|||||||||||||||||||||||||||||THIS SHOULD BE LTR|||||||||||||||||||||||||
-|||||||||||||||||||||||||||||THIS SHOULD BE RTL||||||||||||||||||||||||| {@dir=rtl}
+|||||||||||||||||||||||||||||THIS SHOULD BE RTL|||||||||||||||||||||||||
-(**بايثون** لغة برمجة حديثة بسيطة، واضحة، سريعة ، تستخدم أسلوب البرمجة الكائنية (THIS SHOULD BE LTR ) وقابلة للتطوير {@dir=ltr} بالإضافة إلى أنها مجانية و مفتوح
+(**بايثون** لغة برمجة حديثة بسيطة، واضحة، سريعة ، تستخدم أسلوب البرمجة الكائنية (THIS SHOULD BE LTR ) وقابلة للتطوير بالإضافة إلى أنها مجانية و مفتوح
Even a lazy line.</p>
<hr />
<p>The last line.</p>
+</blockquote>
+<p>foo</p>
+<blockquote>
+<p>bar</p>
+<hr />
+</blockquote>
+<hr />
+<blockquote>
+<p>baz</p>
</blockquote>
\ No newline at end of file
> ---
> The last line.
+
+foo
+> bar
+> ***
+---
+> baz
<h2>Lists</h2>
<p>Unordered (bulleted) lists use asterisks, pluses, and hyphens (<code>*</code>,
<code>+</code>, and <code>-</code>) as list markers. These three markers are
-interchangable; this:</p>
\ No newline at end of file
+interchangeable; this:</p>
\ No newline at end of file
Unordered (bulleted) lists use asterisks, pluses, and hyphens (`*`,
`+`, and `-`) as list markers. These three markers are
-interchangable; this:
+interchangeable; this:
<h1>Title</h1>
<ul>
-<li><em><a href="https://pythonhosted.org/Markdown/">Python in Markdown</a> by some
+<li><em><a href="http://example.com">Python in Markdown</a> by some
great folks</em> - This <em>does</em> work as expected.</li>
-<li><em><a href="https://pythonhosted.org/Markdown/">Python in Markdown</a> by some
+<li><em><a href="http://example.com">Python in Markdown</a> by some
great folks</em> - This <em>does</em> work as expected.</li>
-<li><a href="https://pythonhosted.org/Markdown/"><em>Python in Markdown</em></a> by some
+<li><a href="http://example.com"><em>Python in Markdown</em></a> by some
great folks - This <em>does</em> work as expected.</li>
-<li><a href="https://pythonhosted.org/Markdown/"><em>Python in Markdown</em></a> <em>by some
+<li><a href="http://example.com"><em>Python in Markdown</em></a> <em>by some
great folks</em> - This <em>does</em> work as expected.</li>
</ul>
-<p><em><a href="https://pythonhosted.org/Markdown/">Python in Markdown</a> by some
+<p><em><a href="http://example.com">Python in Markdown</a> by some
great folks</em> - This <em>does</em> work as expected.</p>
\ No newline at end of file
# Title
- - *[Python in Markdown](https://pythonhosted.org/Markdown/) by some
+ - *[Python in Markdown](http://example.com) by some
great folks* - This *does* work as expected.
- - _[Python in Markdown](https://pythonhosted.org/Markdown/) by some
+ - _[Python in Markdown](http://example.com) by some
great folks_ - This *does* work as expected.
- - [_Python in Markdown_](https://pythonhosted.org/Markdown/) by some
+ - [_Python in Markdown_](http://example.com) by some
great folks - This *does* work as expected.
- - [_Python in Markdown_](https://pythonhosted.org/Markdown/) _by some
+ - [_Python in Markdown_](http://example.com) _by some
great folks_ - This *does* work as expected.
-_[Python in Markdown](https://pythonhosted.org/Markdown/) by some
+_[Python in Markdown](http://example.com) by some
great folks_ - This *does* work as expected.
<p><em><strong>test test</strong> test test</em></p>
<p><strong><em>test test</em> test test</strong></p>
<p><strong><em>test</em></strong></p>
-<p><strong>test</strong>_</p>
+<p><strong>test</strong></p>
<p><strong><em>test</em> test</strong>_</p>
<p><strong><em>test</em> test</strong></p>
<p><em>test_test test_test</em></p>
___test___
-__test___
+__test__
___test_ test___
Html with various attributes.
</div>
+<div>
+ <div>
+ Div with a blank line
+
+ in the middle.
+ </div>
+ <div>
+ This gets treated as HTML.
+ </div>
+</div>
+
<p>And of course <script>blah</script>.</p>
<p><a href="script>stuff</script">this <script>link</a></p>
<p>Some funky <x\]> inline stuff with markdown escaping syntax.</p>
Html with various attributes.
</div>
+<div>
+ <div>
+ Div with a blank line
+
+ in the middle.
+ </div>
+ <div>
+ This gets treated as HTML.
+ </div>
+</div>
+
And of course <script>blah</script>.
[this <script>link](<script>stuff</script>)
+++ /dev/null
-<p><img alt="Poster" src="http://humane_man.jpg" title="The most humane man." /></p>
-<p><img alt="Poster" src="http://humane_man.jpg" title="The most humane man." /></p>
-<p><img alt="Blank" src="" /></p>
\ No newline at end of file
+++ /dev/null
-
-
-
-![Poster][]
-
-[Poster]:http://humane_man.jpg "The most humane man."
-
-![Blank]()
\ No newline at end of file
-<h1 id="inthebeginning">Header </h1>
-<p>Now, let's try something <em class="special">inline</em>, to see if it works</p>
<p>Blah blah blah <a href="http://www.slashdot.org">http://www.slashdot.org</a></p>
<ul>
<li>Basic list</li>
-
-# Header {@id=inthebeginning}
-
-Now, let's try something *inline{@class=special}*, to see if it works
-
-
Blah blah blah <http://www.slashdot.org>
* Basic list
<p><img alt="asif" src="http://fourthought.com/images/ftlogo.png" title="Fourthought logo" /></p>
-<p><a href="http://fourthought.com/"><img alt="" src="http://fourthought.com/images/ftlogo.png" style="float: left; margin: 10px; border: none;" title="Fourthought logo" /></a></p>
+<p><a href="http://fourthought.com/"><img alt="Alt text" src="http://fourthought.com/images/ftlogo.png" title="Fourthought logo" /></a></p>
<p><a href="http://link.com/"><img alt="text" src="x" /></a></p>
\ No newline at end of file

-[](http://fourthought.com/)
[](http://link.com/)
<p>THIS_SHOULD_STAY_AS_IS</p>
<p>Here is some <em>emphasis</em>, ok?</p>
<p>Ok, at least <em>this</em> should work.</p>
-<p>THIS<strong>SHOULD</strong>STAY</p>
+<p>THIS__SHOULD__STAY</p>
<p>Here is some <strong>strong</strong> stuff.</p>
<p>THIS<strong><em>SHOULD</em></strong>STAY?</p>
\ No newline at end of file
+++ /dev/null
-<p>A test of the most<br>
-basic of html/xhtml differences.</p>
\ No newline at end of file
+++ /dev/null
-A test of the most
-basic of html/xhtml differences.
\ No newline at end of file
+++ /dev/null
-<p>A numbered list from daringfireball:</p>
-<ol start="3">
-<li>Bird</li>
-<li>McHale</li>
-<li>Parish</li>
-</ol>
-<p>Again:</p>
-<ol start="3">
-<li>Bird</li>
-<li>McHale</li>
-<li>Parish</li>
-</ol>
-<p>Now starting with 1:</p>
-<ol>
-<li>Bird</li>
-<li>McHale</li>
-<li>Parish</li>
-</ol>
\ No newline at end of file
+++ /dev/null
-A numbered list from daringfireball:
-
-3. Bird
-1. McHale
-8. Parish
-
-Again:
-
-3. Bird
-1. McHale
-8. Parish
-
-Now starting with 1:
-
-1. Bird
-1. McHale
-8. Parish
+++ /dev/null
-<p>Regression <em>test</em> for issue 87</p>
-<p>It's run with enable_attributes=False so this {@id=explanation} should not become an attribute</p>
\ No newline at end of file
+++ /dev/null
-Regression *test* for issue 87
-
-It's run with enable_attributes=False so this {@id=explanation} should not become an attribute
+++ /dev/null
-<p><em>connected</em>words_</p>
\ No newline at end of file
+++ /dev/null
-_connected_words_
\ No newline at end of file
+++ /dev/null
-lazy_ol_off:
- lazy_ol: False
-
-html4:
- output_format: html4
-
-no-attributes:
- enable_attributes: False
-
-no-smart-emphasis:
- smart_emphasis: False
\ No newline at end of file
+++ /dev/null
-DEFAULT:
- extensions:
- - extra
- normalize: True
- input_ext: .text
- output_ext: .xhtml
- skip: True
+++ /dev/null
-DEFAULT:
- normalize: True
- input_ext: .text
- output_ext: .xhtml
- #skip: True
-
-Quotes in attributes:
- # attributes get output in differant order
- skip: True
-
-Inline HTML (Span):
- # Backtick in raw HTML attribute TODO: fixme
- skip: True
-
-Backslash escapes:
- # Weird whitespace issue in output
- skip: True
-
-Ins & del:
- # Our behavior follows markdown.pl I think PHP is wrong here
- skip: True
-
-Auto Links:
- # TODO: fix raw HTML so is doesn't match <hr@example.com> as a <hr>.
- skip: True
-
-Empty List Item:
- # We match markdown.pl here. Maybe someday we'll support this
- skip: True
-
-Headers:
- # TODO: fix headers to not require blank line before
- skip: True
-
-Mixed OLs and ULs:
- # We match markdown.pl here. I think PHP is wrong here
- skip: True
-
-Emphasis:
- # We have various minor differances in combined & incorrect em markup.
- # Maybe fix a few of them - but most aren't too important
- skip: True
-
-Code block in a list item:
- # We match markdown.pl - not sure how php gets that output??
- skip: True
-
-PHP-Specific Bugs:
- # Not sure what to make of the escaping stuff here. Why is PHP not removing a blackslash?
- skip: True
+++ /dev/null
-DEFAULT:
- input_ext: .text
- normalize: True
- # comment out next line to run these tests
- #skip: True
-
-Yuri-Footnotes:
- extensions: footnotes
- skip: True
-
+++ /dev/null
-DEFAULT:
- input_ext: .text
- normalize: True
- # comment out next line to run these tests
- #skip: True
-
-Images:
- # the attributes don't get ordered the same so we skip this
- skip: True
-
-Code Blocks:
- # some weird whitespace issue
- skip: True
-
-Links, reference style:
- # weird issue with nested brackets TODO: fixme
- skip: True
-
-Backslash escapes:
- # backticks in raw html attributes TODO: fixme
- skip: True
-
-Code Spans:
- # more backticks in raw html attributes TODO: fixme
- skip: True
+++ /dev/null
-import traceback
-from nose.plugins import Plugin
-from nose.plugins.errorclass import ErrorClass, ErrorClassPlugin
-
-
-class MarkdownSyntaxError(Exception):
- pass
-
-
-class Markdown(ErrorClassPlugin):
- """ Add MarkdownSyntaxError and ensure proper formatting. """
- mdsyntax = ErrorClass(
- MarkdownSyntaxError,
- label='MarkdownSyntaxError',
- isfailure=True
- )
- enabled = True
-
- def configure(self, options, conf):
- self.conf = conf
-
- def addError(self, test, err):
- """ Ensure other plugins see the error by returning nothing here. """
- pass
-
- def formatError(self, test, err):
- """ Remove unnessecary and unhelpful traceback from error report. """
- et, ev, tb = err
- if et.__name__ == 'MarkdownSyntaxError':
- return et, ev, ''
- return err
-
-
-def escape(html):
- """ Escape HTML for display as source within HTML. """
- html = html.replace('&', '&')
- html = html.replace('<', '<')
- html = html.replace('>', '>')
- return html
-
-
-class HtmlOutput(Plugin):
- """Output test results as ugly, unstyled html. """
-
- name = 'html-output'
- score = 2 # run late
- enabled = True
-
- def __init__(self):
- super(HtmlOutput, self).__init__()
- self.html = [
- '<html><head>',
- '<title>Test output</title>',
- '</head><body>'
- ]
-
- def configure(self, options, conf):
- self.conf = conf
-
- def addSuccess(self, test):
- self.html.append('<span>ok</span>')
-
- def addError(self, test, err):
- err = self.formatErr(err)
- self.html.append('<span>ERROR</span>')
- self.html.append('<pre>%s</pre>' % escape(err))
-
- def addFailure(self, test, err):
- err = self.formatErr(err)
- self.html.append('<span>FAIL</span>')
- self.html.append('<pre>%s</pre>' % escape(err))
-
- def finalize(self, result):
- self.html.append('<div>')
- self.html.append(
- "Ran %d test%s" %
- (result.testsRun, result.testsRun != 1 and "s" or "")
- )
- self.html.append('</div>')
- self.html.append('<div>')
- if not result.wasSuccessful():
- self.html.extend(['<span>FAILED (',
- 'failures=%d ' % len(result.failures),
- 'errors=%d' % len(result.errors)])
- for cls in list(result.errorClasses.keys()):
- storage, label, isfail = result.errorClasses[cls]
- if len(storage):
- self.html.append(' %ss=%d' % (label, len(storage)))
- self.html.append(')</span>')
- else:
- self.html.append('OK')
- self.html.append('</div></body></html>')
- f = open('test-output.html', 'w')
- for l in self.html:
- f.write(l)
- f.close()
-
- def formatErr(self, err):
- exctype, value, tb = err
- return ''.join(traceback.format_exception(exctype, value, tb))
-
- def startContext(self, ctx):
- try:
- n = ctx.__name__
- except AttributeError:
- n = str(ctx).replace('<', '').replace('>', '')
- self.html.extend(['<fieldset>', '<legend>', n, '</legend>'])
- try:
- path = ctx.__file__.replace('.pyc', '.py')
- self.html.extend(['<div>', path, '</div>'])
- except AttributeError:
- pass
-
- def stopContext(self, ctx):
- self.html.append('</fieldset>')
-
- def startTest(self, test):
- self.html.extend([
- '<div><span>',
- test.shortDescription() or str(test),
- '</span>'
- ])
-
- def stopTest(self, test):
- self.html.append('</div>')
+++ /dev/null
-<p>to:</p>
-<p><td /><td style="text-align: center; white-space: nowrap;"><br /></p>
-<blockquote>
-<p>3) You don't need to alter all localization files.
- Adding the new labels to the en_US files will do it.</p>
-</blockquote>
\ No newline at end of file
+++ /dev/null
-to:
-
-<td /><td style="text-align: center; white-space: nowrap;"><br />
-
-> 3) You don't need to alter all localization files.
-> Adding the new labels to the en_US files will do it.
+++ /dev/null
-<p>Simple block on one line:</p>
-<p><div>foo</div></p>
-<p>And nested without indentation:</p>
-<p><div>
-<div>
-<div>
-foo
-</div>
-</div>
-<div>bar</div>
-</div></p>
\ No newline at end of file
+++ /dev/null
-Simple block on one line:
-
-<div>foo</div>
-
-And nested without indentation:
-
-<div>
-<div>
-<div>
-foo
-</div>
-</div>
-<div>bar</div>
-</div>
+++ /dev/null
-<p>Paragraph one.</p>
-<p><!-- This is a simple comment --></p>
-<p><!--
- This is another comment.
---></p>
-<p>Paragraph two.</p>
-<p><!-- one comment block -- -- with two comments --></p>
-<p>The end.</p>
\ No newline at end of file
+++ /dev/null
-Paragraph one.
-
-<!-- This is a simple comment -->
-
-<!--
- This is another comment.
--->
-
-Paragraph two.
-
-<!-- one comment block -- -- with two comments -->
-
-The end.
+++ /dev/null
-<p>Here's a simple block:</p>
-<p><div>
- foo
-</div></p>
-<p>This should be a code block, though:</p>
-<pre><code><div>
- foo
-</div>
-</code></pre>
-<p>As should this:</p>
-<pre><code><div>foo</div>
-</code></pre>
-<p>Now, nested:</p>
-<p><div>
- <div>
- <div>
- foo
- </div>
- </div>
-</div></p>
-<p>This should just be an HTML comment:</p>
-<p><!-- Comment --></p>
-<p>Multiline:</p>
-<p><!--
-Blah
-Blah
---></p>
-<p>Code block:</p>
-<pre><code><!-- Comment -->
-</code></pre>
-<p>Just plain comment, with trailing spaces on the line:</p>
-<p><!-- foo --> </p>
-<p>Code:</p>
-<pre><code><hr />
-</code></pre>
-<p>Hr's:</p>
-<p><hr></p>
-<p><hr/></p>
-<p><hr /></p>
-<p><hr> </p>
-<p><hr/> </p>
-<p><hr /> </p>
-<p><hr class="foo" id="bar" /></p>
-<p><hr class="foo" id="bar"/></p>
-<p><hr class="foo" id="bar" ></p>
-<p><some <a href="http://example.com">weird</a> stuff></p>
\ No newline at end of file
+++ /dev/null
-Here's a simple block:
-
-<div>
- foo
-</div>
-
-This should be a code block, though:
-
- <div>
- foo
- </div>
-
-As should this:
-
- <div>foo</div>
-
-Now, nested:
-
-<div>
- <div>
- <div>
- foo
- </div>
- </div>
-</div>
-
-This should just be an HTML comment:
-
-<!-- Comment -->
-
-Multiline:
-
-<!--
-Blah
-Blah
--->
-
-Code block:
-
- <!-- Comment -->
-
-Just plain comment, with trailing spaces on the line:
-
-<!-- foo -->
-
-Code:
-
- <hr />
-
-Hr's:
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr class="foo" id="bar" />
-
-<hr class="foo" id="bar"/>
-
-<hr class="foo" id="bar" >
-
-<some [weird](http://example.com) stuff>
+++ /dev/null
-<p><a href="">XSS</a>
-See http://security.stackexchange.com/q/30330/1261 for details.</p>
\ No newline at end of file
+++ /dev/null
-[XSS](javascript://%0Aalert%28'XSS'%29;)
-See http://security.stackexchange.com/q/30330/1261 for details.
-
+++ /dev/null
-<p>Here's a simple block:</p>
-<p></p>
-<p>This should be a code block, though:</p>
-<pre><code><div>
- foo
-</div>
-</code></pre>
-<p>As should this:</p>
-<pre><code><div>foo</div>
-</code></pre>
-<p>Now, nested:</p>
-<p></p>
-<p>This should just be an HTML comment:</p>
-<p></p>
-<p>Multiline:</p>
-<p></p>
-<p>Code block:</p>
-<pre><code><!-- Comment -->
-</code></pre>
-<p>Just plain comment, with trailing spaces on the line:</p>
-<p></p>
-<p>Code:</p>
-<pre><code><hr />
-</code></pre>
-<p>Hr's:</p>
-<p></p>
-<p></p>
-<p></p>
-<p></p>
-<p></p>
-<p></p>
-<p></p>
-<p></p>
-<p></p>
-<p></p>
\ No newline at end of file
+++ /dev/null
-Here's a simple block:
-
-<div>
- foo
-</div>
-
-This should be a code block, though:
-
- <div>
- foo
- </div>
-
-As should this:
-
- <div>foo</div>
-
-Now, nested:
-
-<div>
- <div>
- <div>
- foo
- </div>
- </div>
-</div>
-
-This should just be an HTML comment:
-
-<!-- Comment -->
-
-Multiline:
-
-<!--
-Blah
-Blah
--->
-
-Code block:
-
- <!-- Comment -->
-
-Just plain comment, with trailing spaces on the line:
-
-<!-- foo -->
-
-Code:
-
- <hr />
-
-Hr's:
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr class="foo" id="bar" />
-
-<hr class="foo" id="bar"/>
-
-<hr class="foo" id="bar" >
-
-<some [weird](http://example.com) stuff>
+++ /dev/null
-<p>Here's a simple block:</p>
-<p>[HTML_REMOVED]</p>
-<p>This should be a code block, though:</p>
-<pre><code><div>
- foo
-</div>
-</code></pre>
-<p>As should this:</p>
-<pre><code><div>foo</div>
-</code></pre>
-<p>Now, nested:</p>
-<p>[HTML_REMOVED]</p>
-<p>This should just be an HTML comment:</p>
-<p>[HTML_REMOVED]</p>
-<p>Multiline:</p>
-<p>[HTML_REMOVED]</p>
-<p>Code block:</p>
-<pre><code><!-- Comment -->
-</code></pre>
-<p>Just plain comment, with trailing spaces on the line:</p>
-<p>[HTML_REMOVED]</p>
-<p>Code:</p>
-<pre><code><hr />
-</code></pre>
-<p>Hr's:</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
-<p>[HTML_REMOVED]</p>
\ No newline at end of file
+++ /dev/null
-Here's a simple block:
-
-<div>
- foo
-</div>
-
-This should be a code block, though:
-
- <div>
- foo
- </div>
-
-As should this:
-
- <div>foo</div>
-
-Now, nested:
-
-<div>
- <div>
- <div>
- foo
- </div>
- </div>
-</div>
-
-This should just be an HTML comment:
-
-<!-- Comment -->
-
-Multiline:
-
-<!--
-Blah
-Blah
--->
-
-Code block:
-
- <!-- Comment -->
-
-Just plain comment, with trailing spaces on the line:
-
-<!-- foo -->
-
-Code:
-
- <hr />
-
-Hr's:
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr>
-
-<hr/>
-
-<hr />
-
-<hr class="foo" id="bar" />
-
-<hr class="foo" id="bar"/>
-
-<hr class="foo" id="bar" >
-
-<some [weird](http://example.com) stuff>
+++ /dev/null
-<p>This should be stripped/escaped in safe_mode.</p>
-<p><script>
-alert("Hello world!")
-</script></p>
-<p>With blank lines.</p>
-<p><script></p>
-<p>alert("Hello world!")</p>
-<p></script></p>
-<p>Now with some weirdness</p>
-<p><code><script <!--
-alert("Hello world!")
-</script <></code> `</p>
-<p>Try another way.</p>
-<p><script <!--
-alert("Hello world!")
-</script <></p>
-<p>This time with blank lines.</p>
-<p><script <!--</p>
-<p>alert("Hello world!")</p>
-<p></script <></p>
\ No newline at end of file
+++ /dev/null
-This should be stripped/escaped in safe_mode.
-
-<script>
-alert("Hello world!")
-</script>
-
-With blank lines.
-
-<script>
-
-alert("Hello world!")
-
-</script>
-
-Now with some weirdness
-
-``<script <!--
-alert("Hello world!")
-</script <>`` `
-
-Try another way.
-
-<script <!--
-alert("Hello world!")
-</script <>
-
-This time with blank lines.
-
-<script <!--
-
-alert("Hello world!")
-
-</script <>
+++ /dev/null
-DEFAULT:
- safe_mode: escape
-
-remove:
- safe_mode: remove
-
-replace:
- safe_mode: replace
+++ /dev/null
-<p>These links should be unsafe and not allowed in safe_mode</p>
-<p><a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a>
-<a href="">link</a></p>
-<p><img alt="img" src="" />
-<a href="">ref</a>
-<img alt="imgref" src="" /></p>
-<p>These should work regardless:</p>
-<p><a href="relative/url.html">relative</a>
-<a href="mailto:foo@bar.com">email</a>
-<a href="news:some.news.group.com">news scheme</a>
-<a href="http://example.com">http link</a></p>
\ No newline at end of file
+++ /dev/null
-These links should be unsafe and not allowed in safe_mode
-
-[link](javascript:alert%28'Hello%20world!'%29)
-[link](vbscript:msgbox%28%22Hello%20world!%22%29)
-[link](livescript:alert%28'Hello%20world!'%29)
-[link](mocha:[code])
-[link](jAvAsCrIpT:alert%28'Hello%20world!'%29)
-[link](ja vas cr ipt:alert%28'Hello%20world!'%29)
-[link](ja vas cr ipt:alert%28'Hello%20world!'%29)
-[link](ja vas cr ipt:alert%28'Hello%20world!'%29)
-[link](ja%09 %0Avas cr
ipt:alert%28'Hello%20world!'%29)
-[link](ja%20vas%20cr%20ipt:alert%28'Hello%20world!'%29)
-[link](live%20script:alert%28'Hello%20world!'%29)
-
-
-[ref][]
-![imgref][]
-
-[ref]: javascript:alert%29'XSS'%29
-[imgref]: javascript:alert%29'XSS'%29
-
-These should work regardless:
-
-[relative](relative/url.html)
-[email](mailto:foo@bar.com)
-[news scheme](news:some.news.group.com)
-[http link](http://example.com)
-#!/usr/bin/python
+# -*- coding: utf-8 -*-
"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+
Python-Markdown Regression Tests
================================
Tests of the various APIs with the python markdown lib.
-
"""
from __future__ import unicode_literals
import unittest
import sys
import os
-import types
import markdown
import warnings
from markdown.__main__ import parse_options
from logging import DEBUG, WARNING, CRITICAL
import yaml
import tempfile
+from io import BytesIO
+from xml.etree.ElementTree import ProcessingInstruction
+
PY3 = sys.version_info[0] == 3
+if not PY3:
+ def bytes(string, encoding):
+ return string.encode(encoding)
+
+
class TestMarkdownBasics(unittest.TestCase):
""" Tests basics of the Markdown class. """
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'])
+class TestConvertFile(unittest.TestCase):
+ """ Tests of ConvertFile. """
+
+ def setUp(self):
+ self.saved = sys.stdin, sys.stdout
+ sys.stdin = BytesIO(bytes('foo', encoding='utf-8'))
+ sys.stdout = BytesIO()
+
+ def tearDown(self):
+ sys.stdin, sys.stdout = self.saved
+
+ def getTempFiles(self, src):
+ """ Return the file names for two temp files. """
+ infd, infile = tempfile.mkstemp(suffix='.txt')
+ with os.fdopen(infd, 'w') as fp:
+ fp.write(src)
+ outfd, outfile = tempfile.mkstemp(suffix='.html')
+ return infile, outfile, outfd
+
+ def testFileNames(self):
+ infile, outfile, outfd = self.getTempFiles('foo')
+ markdown.markdownFromFile(input=infile, output=outfile)
+ with os.fdopen(outfd, 'r') as fp:
+ output = fp.read()
+ self.assertEqual(output, '<p>foo</p>')
+
+ def testFileObjects(self):
+ infile = BytesIO(bytes('foo', encoding='utf-8'))
+ outfile = BytesIO()
+ markdown.markdownFromFile(input=infile, output=outfile)
+ outfile.seek(0)
+ self.assertEqual(outfile.read().decode('utf-8'), '<p>foo</p>')
+
+ def testStdinStdout(self):
+ markdown.markdownFromFile()
+ sys.stdout.seek(0)
+ self.assertEqual(sys.stdout.read().decode('utf-8'), '<p>foo</p>')
+
+
class TestBlockParser(unittest.TestCase):
""" Tests of the BlockParser class. """
""" Test HtmlStash.store. """
self.assertEqual(self.placeholder, self.stash.get_placeholder(0))
self.assertEqual(self.stash.html_counter, 1)
- self.assertEqual(self.stash.rawHtmlBlocks, [('foo', False)])
+ self.assertEqual(self.stash.rawHtmlBlocks, ['foo'])
def testStoreMore(self):
""" Test HtmlStash.store with additional blocks. """
self.assertEqual(self.stash.html_counter, 2)
self.assertEqual(
self.stash.rawHtmlBlocks,
- [('foo', False), ('bar', False)]
- )
-
- def testSafeStore(self):
- """ Test HtmlStash.store with 'safe' html. """
- self.stash.store('bar', True)
- self.assertEqual(
- self.stash.rawHtmlBlocks,
- [('foo', False), ('bar', True)]
+ ['foo', 'bar']
)
def testReset(self):
self.assertEqual(self.stash.html_counter, 0)
self.assertEqual(self.stash.rawHtmlBlocks, [])
- def testUnsafeHtmlInSafeMode(self):
- """ Test that unsafe HTML gets escaped in safe_mode. """
- output = markdown.markdown('foo', extensions=[self.build_extension()], safe_mode='escape')
- self.assertEqual(output, '<p><script>print("evil")</script></p>')
-
- def build_extension(self):
- """ Build an extention that addes unsafe html to Stash in same_mode. """
- class Unsafe(markdown.treeprocessors.Treeprocessor):
- def run(self, root):
- el = root.find('p')
- el.text = self.markdown.htmlStash.store('<script>print("evil")</script>', safe=False)
- return root
-
- class StoreUnsafeHtml(markdown.extensions.Extension):
- def extendMarkdown(self, md, md_globals):
- md.treeprocessors.add('unsafe', Unsafe(md), '_end')
-
- return StoreUnsafeHtml()
-
-class TestOrderedDict(unittest.TestCase):
- """ Test OrderedDict storage class. """
-
- def setUp(self):
- self.odict = markdown.odict.OrderedDict()
- self.odict['first'] = 'This'
- self.odict['third'] = 'a'
- self.odict['fourth'] = 'self'
- self.odict['fifth'] = 'test'
-
- def testValues(self):
- """ Test output of OrderedDict.values(). """
- self.assertEqual(list(self.odict.values()), ['This', 'a', 'self', 'test'])
-
- def testKeys(self):
- """ Test output of OrderedDict.keys(). """
- self.assertEqual(
- list(self.odict.keys()),
- ['first', 'third', 'fourth', 'fifth']
- )
-
- def testItems(self):
- """ Test output of OrderedDict.items(). """
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('third', 'a'),
- ('fourth', 'self'),
- ('fifth', 'test')
- ]
- )
-
- def testAddBefore(self):
- """ Test adding an OrderedDict item before a given key. """
- self.odict.add('second', 'is', '<third')
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('second', 'is'),
- ('third', 'a'),
- ('fourth', 'self'),
- ('fifth', 'test')
- ]
- )
-
- def testAddAfter(self):
- """ Test adding an OrderDict item after a given key. """
- self.odict.add('second', 'is', '>first')
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('second', 'is'),
- ('third', 'a'),
- ('fourth', 'self'),
- ('fifth', 'test')
- ]
- )
-
- def testAddAfterEnd(self):
- """ Test adding an OrderedDict item after the last key. """
- self.odict.add('sixth', '.', '>fifth')
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('third', 'a'),
- ('fourth', 'self'),
- ('fifth', 'test'),
- ('sixth', '.')
- ]
- )
-
- def testAdd_begin(self):
- """ Test adding an OrderedDict item using "_begin". """
- self.odict.add('zero', 'CRAZY', '_begin')
- self.assertEqual(
- list(self.odict.items()), [
- ('zero', 'CRAZY'),
- ('first', 'This'),
- ('third', 'a'),
- ('fourth', 'self'),
- ('fifth', 'test')
- ]
- )
-
- def testAdd_end(self):
- """ Test adding an OrderedDict item using "_end". """
- self.odict.add('sixth', '.', '_end')
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('third', 'a'),
- ('fourth', 'self'),
- ('fifth', 'test'),
- ('sixth', '.')
- ]
- )
-
- def testAddBadLocation(self):
- """ Test Error on bad location in OrderedDict.add(). """
- self.assertRaises(ValueError, self.odict.add, 'sixth', '.', '<seventh')
- self.assertRaises(ValueError, self.odict.add, 'second', 'is', 'third')
-
- def testDeleteItem(self):
- """ Test deletion of an OrderedDict item. """
- del self.odict['fourth']
- self.assertEqual(
- list(self.odict.items()),
- [('first', 'This'), ('third', 'a'), ('fifth', 'test')]
- )
-
- def testChangeValue(self):
- """ Test OrderedDict change value. """
- self.odict['fourth'] = 'CRAZY'
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('third', 'a'),
- ('fourth', 'CRAZY'),
- ('fifth', 'test')
- ]
- )
-
- def testChangeOrder(self):
- """ Test OrderedDict change order. """
- self.odict.link('fourth', '<third')
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('fourth', 'self'),
- ('third', 'a'),
- ('fifth', 'test')
- ]
- )
-
- def textBadLink(self):
- """ Test OrderedDict change order with bad location. """
- self.assertRaises(ValueError, self.odict.link('fourth', '<bad'))
- # Check for data integrity ("fourth" wasn't deleted).'
- self.assertEqual(
- list(self.odict.items()), [
- ('first', 'This'),
- ('third', 'a'),
- ('fourth', 'self'),
- ('fifth', 'test')
- ]
- )
+class Item(object):
+ """ A dummy Registry item object for testing. """
+ def __init__(self, data):
+ self.data = data
+
+ def __repr__(self):
+ return repr(self.data)
+
+ def __eq__(self, other):
+ return self.data == other
+
+
+class RegistryTests(unittest.TestCase):
+ """ Test the processor registry. """
+
+ def testCreateRegistry(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ self.assertEqual(len(r), 1)
+ self.assertTrue(isinstance(r, markdown.util.Registry))
+
+ def testRegisterWithoutPriority(self):
+ r = markdown.util.Registry()
+ with self.assertRaises(TypeError):
+ r.register(Item('a'))
+
+ def testSortRegistry(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b'), 'b', 21)
+ r.register(Item('c'), 'c', 20.5)
+ self.assertEqual(len(r), 3)
+ self.assertEqual(list(r), ['b', 'c', 'a'])
+
+ def testIsSorted(self):
+ r = markdown.util.Registry()
+ self.assertFalse(r._is_sorted)
+ r.register(Item('a'), 'a', 20)
+ list(r)
+ self.assertTrue(r._is_sorted)
+ r.register(Item('b'), 'b', 21)
+ self.assertFalse(r._is_sorted)
+ r['a']
+ self.assertTrue(r._is_sorted)
+ r._is_sorted = False
+ r.get_index_for_name('a')
+ self.assertTrue(r._is_sorted)
+ r._is_sorted = False
+ repr(r)
+ self.assertTrue(r._is_sorted)
+
+ def testDeregister(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b'), 'b', 30)
+ r.register(Item('c'), 'c', 40)
+ self.assertEqual(len(r), 3)
+ r.deregister('b')
+ self.assertEqual(len(r), 2)
+ r.deregister('c', strict=False)
+ self.assertEqual(len(r), 1)
+ # deregister non-existant item with strict=False
+ r.deregister('d', strict=False)
+ self.assertEqual(len(r), 1)
+ with self.assertRaises(ValueError):
+ # deregister non-existant item with strict=True
+ r.deregister('e')
+ self.assertEqual(list(r), ['a'])
+
+ def testRegistryContains(self):
+ r = markdown.util.Registry()
+ item = Item('a')
+ r.register(item, 'a', 20)
+ self.assertTrue('a' in r)
+ self.assertTrue(item in r)
+ self.assertFalse('b' in r)
+
+ def testRegistryIter(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b'), 'b', 30)
+ self.assertEqual(list(r), ['b', 'a'])
+
+ def testRegistryGetItemByIndex(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b'), 'b', 30)
+ self.assertEqual(r[0], 'b')
+ self.assertEqual(r[1], 'a')
+ with self.assertRaises(IndexError):
+ r[3]
+
+ def testRegistryGetItemByItem(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b'), 'b', 30)
+ self.assertEqual(r['a'], 'a')
+ self.assertEqual(r['b'], 'b')
+ with self.assertRaises(KeyError):
+ r['c']
+
+ def testRegistrySetItem(self):
+ r = markdown.util.Registry()
+ with self.assertRaises(TypeError):
+ r[0] = 'a'
+ # TODO: restore this when deprecated __setitem__ is removed.
+ # with self.assertRaises(TypeError):
+ # r['a'] = 'a'
+ # TODO: remove this when deprecated __setitem__ is removed.
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+
+ r['a'] = Item('a')
+ self.assertEqual(list(r), ['a'])
+ r['b'] = Item('b')
+ self.assertEqual(list(r), ['a', 'b'])
+ r['a'] = Item('a1')
+ self.assertEqual(list(r), ['a1', 'b'])
+
+ # Check the warnings
+ self.assertEqual(len(w), 3)
+ self.assertTrue(all(issubclass(x.category, DeprecationWarning) for x in w))
+
+ def testRegistryDelItem(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ with self.assertRaises(TypeError):
+ del r[0]
+ # TODO: restore this when deprecated __del__ is removed.
+ # with self.assertRaises(TypeError):
+ # del r['a']
+ # TODO: remove this when deprecated __del__ is removed.
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+
+ r.register(Item('b'), 'b', 15)
+ r.register(Item('c'), 'c', 10)
+ del r['b']
+ self.assertEqual(list(r), ['a', 'c'])
+ del r['a']
+ self.assertEqual(list(r), ['c'])
+ with self.assertRaises(TypeError):
+ del r['badname']
+ del r['c']
+ self.assertEqual(list(r), [])
+
+ # Check the warnings
+ self.assertEqual(len(w), 3)
+ self.assertTrue(all(issubclass(x.category, DeprecationWarning) for x in w))
+
+ def testRegistrySlice(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b'), 'b', 30)
+ r.register(Item('c'), 'c', 40)
+ slc = r[1:]
+ self.assertEqual(len(slc), 2)
+ self.assertTrue(isinstance(slc, markdown.util.Registry))
+ self.assertEqual(list(slc), ['b', 'a'])
+
+ def testGetIndexForName(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b'), 'b', 30)
+ self.assertEqual(r.get_index_for_name('a'), 1)
+ self.assertEqual(r.get_index_for_name('b'), 0)
+ with self.assertRaises(ValueError):
+ r.get_index_for_name('c')
+
+ def testRegisterDupplicate(self):
+ r = markdown.util.Registry()
+ r.register(Item('a'), 'a', 20)
+ r.register(Item('b1'), 'b', 10)
+ self.assertEqual(list(r), ['a', 'b1'])
+ self.assertEqual(len(r), 2)
+ r.register(Item('b2'), 'b', 30)
+ self.assertEqual(len(r), 2)
+ self.assertEqual(list(r), ['b2', 'a'])
+
+ def testRegistryDeprecatedAdd(self):
+ with warnings.catch_warnings(record=True) as w:
+ warnings.simplefilter("always")
+
+ r = markdown.util.Registry()
+ # Add first item
+ r.add('c', Item('c'), '_begin')
+ self.assertEqual(list(r), ['c'])
+ # Added to beginning
+ r.add('b', Item('b'), '_begin')
+ self.assertEqual(list(r), ['b', 'c'])
+ # Add before first item
+ r.add('a', Item('a'), '<b')
+ self.assertEqual(list(r), ['a', 'b', 'c'])
+ # Add before non-first item
+ r.add('a1', Item('a1'), '<b')
+ self.assertEqual(list(r), ['a', 'a1', 'b', 'c'])
+ # Add after non-last item
+ r.add('b1', Item('b1'), '>b')
+ self.assertEqual(list(r), ['a', 'a1', 'b', 'b1', 'c'])
+ # Add after last item
+ r.add('d', Item('d'), '>c')
+ self.assertEqual(list(r), ['a', 'a1', 'b', 'b1', 'c', 'd'])
+ # Add to end
+ r.add('e', Item('e'), '_end')
+ self.assertEqual(list(r), ['a', 'a1', 'b', 'b1', 'c', 'd', 'e'])
+ with self.assertRaises(ValueError):
+ r.add('f', Item('f'), 'badlocation')
+
+ # Check the warnings
+ self.assertEqual(len(w), 7)
+ self.assertTrue(all(issubclass(x.category, DeprecationWarning) for x in w))
class TestErrors(unittest.TestCase):
def testNonUnicodeSource(self):
""" Test falure on non-unicode source text. """
- if sys.version_info < (3, 0):
+ if not PY3:
source = "foo".encode('utf-16')
self.assertRaises(UnicodeDecodeError, markdown.markdown, source)
""" Test loading a non Extension object as an extension. """
self.assertRaises(TypeError, markdown.Markdown, extensions=[object])
+ def testDotNotationExtensionWithBadClass(self):
+ """ Test Extension loading with non-existant class name (`path.to.module:Class`). """
+ self.assertRaises(
+ AttributeError,
+ markdown.Markdown,
+ extensions=['markdown.extensions.footnotes:MissingExtension']
+ )
+
def testBaseExtention(self):
""" Test that the base Extension class will raise NotImplemented. """
self.assertRaises(
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 testStringConfigExtention(self):
- """ Test that passing configs to an Extension in the name raises a DeprecationWarning. """
- self.assertRaises(
- DeprecationWarning,
- 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=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):
"""
def testCommentIsBlockLevel(self):
""" Test that an ElementTree Comment is recognized as BlockLevel. """
- self.assertFalse(markdown.util.isBlockLevel(self.comment.tag))
+ md = markdown.Markdown()
+ self.assertFalse(md.is_block_level(self.comment.tag))
def testCommentSerialization(self):
""" Test that an ElementTree Comment serializes properly. """
def testCommentPrettify(self):
""" Test that an ElementTree Comment is prettified properly. """
- pretty = markdown.treeprocessors.PrettifyTreeprocessor()
+ pretty = markdown.treeprocessors.PrettifyTreeprocessor(markdown.Markdown())
pretty.run(self.comment)
self.assertEqual(
markdown.serializers.to_html_string(self.comment),
class testElementTailTests(unittest.TestCase):
""" Element Tail Tests """
def setUp(self):
- self.pretty = markdown.treeprocessors.PrettifyTreeprocessor()
+ self.pretty = markdown.treeprocessors.PrettifyTreeprocessor(markdown.Markdown())
def testBrTailNoNewline(self):
""" Test that last <br> in tree has a new line tail """
def testHtml(self):
""" Test HTML serialization. """
el = markdown.util.etree.Element('div')
+ el.set('id', 'foo<&">')
p = markdown.util.etree.SubElement(el, 'p')
- p.text = 'foo'
+ p.text = 'foo <&escaped>'
+ p.set('hidden', 'hidden')
markdown.util.etree.SubElement(el, 'hr')
+ non_element = markdown.util.etree.SubElement(el, None)
+ non_element.text = 'non-element text'
+ script = markdown.util.etree.SubElement(non_element, 'script')
+ script.text = '<&"test\nescaping">'
+ el.tail = "tail text"
self.assertEqual(
markdown.serializers.to_html_string(el),
- '<div><p>foo</p><hr></div>'
+ '<div id="foo<&">">'
+ '<p hidden>foo <&escaped></p>'
+ '<hr>'
+ 'non-element text'
+ '<script><&"test\nescaping"></script>'
+ '</div>tail text'
)
def testXhtml(self):
"""" Test XHTML serialization. """
el = markdown.util.etree.Element('div')
+ el.set('id', 'foo<&">')
p = markdown.util.etree.SubElement(el, 'p')
- p.text = 'foo'
+ p.text = 'foo<&escaped>'
+ p.set('hidden', 'hidden')
markdown.util.etree.SubElement(el, 'hr')
+ non_element = markdown.util.etree.SubElement(el, None)
+ non_element.text = 'non-element text'
+ script = markdown.util.etree.SubElement(non_element, 'script')
+ script.text = '<&"test\nescaping">'
+ el.tail = "tail text"
self.assertEqual(
markdown.serializers.to_xhtml_string(el),
- '<div><p>foo</p><hr /></div>'
+ '<div id="foo<&">">'
+ '<p hidden="hidden">foo<&escaped></p>'
+ '<hr />'
+ 'non-element text'
+ '<script><&"test\nescaping"></script>'
+ '</div>tail text'
)
def testMixedCaseTags(self):
'<MixedCase>not valid <EMPHASIS>html</EMPHASIS><HR /></MixedCase>'
)
+ def testProsessingInstruction(self):
+ """ Test serialization of ProcessignInstruction. """
+ pi = ProcessingInstruction('foo', text='<&"test\nescaping">')
+ self.assertIs(pi.tag, ProcessingInstruction)
+ self.assertEqual(
+ markdown.serializers.to_xhtml_string(pi),
+ '<?foo <&"test\nescaping">?>'
+ )
+
+ def testQNameTag(self):
+ """ Test serialization of QName tag. """
+ div = markdown.util.etree.Element('div')
+ qname = markdown.util.etree.QName('http://www.w3.org/1998/Math/MathML', 'math')
+ math = markdown.util.etree.SubElement(div, qname)
+ math.set('display', 'block')
+ sem = markdown.util.etree.SubElement(math, 'semantics')
+ msup = markdown.util.etree.SubElement(sem, 'msup')
+ mi = markdown.util.etree.SubElement(msup, 'mi')
+ mi.text = 'x'
+ mn = markdown.util.etree.SubElement(msup, 'mn')
+ mn.text = '2'
+ ann = markdown.util.etree.SubElement(sem, 'annotations')
+ ann.text = 'x^2'
+ self.assertEqual(
+ markdown.serializers.to_xhtml_string(div),
+ '<div>'
+ '<math display="block" xmlns="http://www.w3.org/1998/Math/MathML">'
+ '<semantics>'
+ '<msup>'
+ '<mi>x</mi>'
+ '<mn>2</mn>'
+ '</msup>'
+ '<annotations>x^2</annotations>'
+ '</semantics>'
+ '</math>'
+ '</div>'
+ )
+
+ def testQNameAttribute(self):
+ """ Test serialization of QName attribute. """
+ div = markdown.util.etree.Element('div')
+ div.set(markdown.util.etree.QName('foo'), markdown.util.etree.QName('bar'))
+ self.assertEqual(
+ markdown.serializers.to_xhtml_string(div),
+ '<div foo="bar"></div>'
+ )
+
+ def testBadQNameTag(self):
+ """ Test serialization of QName with no tag. """
+ qname = markdown.util.etree.QName('http://www.w3.org/1998/Math/MathML')
+ el = markdown.util.etree.Element(qname)
+ self.assertRaises(ValueError, markdown.serializers.to_xhtml_string, el)
+
+ def testQNameEscaping(self):
+ """ Test QName escaping. """
+ qname = markdown.util.etree.QName('<&"test\nescaping">', 'div')
+ el = markdown.util.etree.Element(qname)
+ self.assertEqual(
+ markdown.serializers.to_xhtml_string(el),
+ '<div xmlns="<&"test escaping">"></div>'
+ )
+
+ def testQNamePreEscaping(self):
+ """ Test QName that is already partially escaped. """
+ qname = markdown.util.etree.QName('<&"test escaping">', 'div')
+ el = markdown.util.etree.Element(qname)
+ self.assertEqual(
+ markdown.serializers.to_xhtml_string(el),
+ '<div xmlns="<&"test escaping">"></div>'
+ )
+
def buildExtension(self):
""" Build an extension which registers fakeSerializer. """
def fakeSerializer(elem):
return '<div><p>foo</p></div>'
class registerFakeSerializer(markdown.extensions.Extension):
- def extendMarkdown(self, md, md_globals):
+ def extendMarkdown(self, md):
md.output_formats['fake'] = fakeSerializer
return registerFakeSerializer()
'<p>foo</p>'
)
+ def testXHTMLOutput(self):
+ self.assertEqual(
+ markdown.markdown('foo \nbar', output_format='xhtml'),
+ '<p>foo<br />\nbar</p>'
+ )
+
+ def testHTMLOutput(self):
+ self.assertEqual(
+ markdown.markdown('foo \nbar', output_format='html'),
+ '<p>foo<br>\nbar</p>'
+ )
+
class testAtomicString(unittest.TestCase):
""" Test that AtomicStrings are honored (not parsed). """
'input': None,
'output': None,
'encoding': None,
- 'output_format': 'xhtml1',
+ 'output_format': 'xhtml',
'lazy_ol': True,
'extensions': [],
'extension_configs': {},
self.default_options['encoding'] = 'utf-8'
self.assertEqual(options, self.default_options)
- def testSafeModeOption(self):
- options, logging_level = parse_options(['-s', 'escape'])
- self.default_options['safe_mode'] = 'escape'
- self.assertEqual(options, self.default_options)
-
def testOutputFormatOption(self):
- options, logging_level = parse_options(['-o', 'html5'])
- self.default_options['output_format'] = 'html5'
+ options, logging_level = parse_options(['-o', 'html'])
+ self.default_options['output_format'] = 'html'
self.assertEqual(options, self.default_options)
def testNoLazyOlOption(self):
"""
self.create_config_file(config)
self.assertRaises(yaml.YAMLError, parse_options, ['-c', self.tempfile])
+
+
+class TestEscapeAppend(unittest.TestCase):
+ """ Tests escape character append. """
+
+ def testAppend(self):
+ """ Test that appended escapes are only in the current instance. """
+ md = markdown.Markdown()
+ md.ESCAPED_CHARS.append('|')
+ self.assertEqual('|' in md.ESCAPED_CHARS, True)
+ md2 = markdown.Markdown()
+ self.assertEqual('|' not in md2.ESCAPED_CHARS, True)
+
+
+class TestAncestorExclusion(unittest.TestCase):
+ """ Tests exclusion of tags in ancestor list. """
+
+ class AncestorExample(markdown.inlinepatterns.SimpleTagInlineProcessor):
+ """ Ancestor Test. """
+
+ ANCESTOR_EXCLUDES = ('a',)
+
+ def handleMatch(self, m, data):
+ """ Handle match. """
+ el = markdown.util.etree.Element(self.tag)
+ el.text = m.group(2)
+ return el, m.start(0), m.end(0)
+
+ class AncestorExtension(markdown.Extension):
+
+ def __init__(self, *args, **kwargs):
+ """Initialize."""
+
+ self.config = {}
+
+ def extendMarkdown(self, md):
+ """Modify inline patterns."""
+
+ pattern = r'(\+)([^\+]+)\1'
+ md.inlinePatterns.register(TestAncestorExclusion.AncestorExample(pattern, 'strong'), 'ancestor-test', 0)
+
+ def setUp(self):
+ """Setup markdown object."""
+ self.md = markdown.Markdown(extensions=[TestAncestorExclusion.AncestorExtension()])
+
+ def test_ancestors(self):
+ """ Test that an extension can exclude parent tags. """
+ test = """
+Some +test+ and a [+link+](http://test.com)
+"""
+ result = """<p>Some <strong>test</strong> and a <a href="http://test.com">+link+</a></p>"""
+
+ self.md.reset()
+ self.assertEqual(self.md.convert(test), result)
+
+ def test_ancestors_tail(self):
+ """ Test that an extension can exclude parent tags when dealing with a tail. """
+ test = """
+[***+em+*+strong+**](http://test.com)
+"""
+ result = """<p><a href="http://test.com"><strong><em>+em+</em>+strong+</strong></a></p>"""
+
+ self.md.reset()
+ self.assertEqual(self.md.convert(test), result)
+# -*- coding: utf-8 -*-
"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+
Python-Markdown Extension Regression Tests
==========================================
A collection of regression tests to confirm that the included extensions
continue to work as advertised. This used to be accomplished by doctests.
-
"""
from __future__ import unicode_literals
-import datetime
import unittest
import markdown
+class TestCaseWithAssertStartsWith(unittest.TestCase):
+
+ def assertStartsWith(self, expectedPrefix, text, msg=None):
+ if not text.startswith(expectedPrefix):
+ if len(expectedPrefix) + 5 < len(text):
+ text = text[:len(expectedPrefix) + 5] + '...'
+ standardMsg = '%s not found at the start of %s' % (repr(expectedPrefix),
+ repr(text))
+ self.fail(self._formatMessage(msg, standardMsg))
+
+
class TestExtensionClass(unittest.TestCase):
""" Test markdown.extensions.Extension. """
""" 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. """
)
-class TestCodeHilite(unittest.TestCase):
+class TestCodeHilite(TestCaseWithAssertStartsWith):
""" Test codehilite extension. """
def setUp(self):
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.assertTrue(md.convert(text).startswith('<div class="codehilite"><pre>'))
+ self.assertStartsWith('<div class="codehilite"><pre>', md.convert(text))
else:
self.assertEqual(
md.convert(text),
# Different versions of pygments output slightly different markup.
# So we use 'startwith' and test just enough to confirm that
# pygments received and processed linenums.
- self.assertTrue(
- md.convert(text).startswith(
- '<table class="codehilitetable"><tr><td class="linenos">'
- )
+ self.assertStartsWith(
+ '<table class="codehilitetable"><tr><td class="linenos">',
+ md.convert(text)
)
else:
self.assertEqual(
md = markdown.Markdown(
extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=False)])
if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite">'
- '<pre><span class="c"># A Code Comment</span>\n'
- '</pre></div>'
- )
+ self.assertStartsWith('<div class="codehilite"><pre><span', md.convert(text))
else:
self.assertEqual(
md.convert(text),
extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)])
if self.has_pygments:
# Pygments can use random lexer here as we did not specify the language
- self.assertTrue(md.convert(text).startswith('<div class="codehilite"><pre>'))
+ self.assertStartsWith('<div class="codehilite"><pre>', md.convert(text))
else:
self.assertEqual(
md.convert(text),
# Differant versions of pygments output slightly different markup.
# So we use 'startwith' and test just enough to confirm that
# pygments received and processed linenums.
- self.assertTrue(
- md.convert(text).startswith(
- '<table class="codehilitetable"><tr><td class="linenos">'
- )
+ self.assertStartsWith(
+ '<table class="codehilitetable"><tr><td class="linenos">',
+ md.convert(text)
)
else:
self.assertEqual(
extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)]
)
if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite">'
- '<pre><span class="c"># A Code Comment</span>\n'
- '</pre></div>'
- )
+ self.assertStartsWith('<div class="codehilite"><pre><span', md.convert(text))
else:
self.assertEqual(
md.convert(text),
def testHighlightLinesWithColon(self):
# Test with hl_lines delimited by single or double quotes.
- text0 = '\t:::Python hl_lines="2"\n\t#line 1\n\t#line 2\n\t#line 3'
- text1 = "\t:::Python hl_lines='2'\n\t#line 1\n\t#line 2\n\t#line 3"
+ text0 = '\t:::Python hl_lines="1"\n\t#line 1\n\t#line 2\n\t#line 3'
+ 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.assertEqual(
- md.convert(text),
- '<div class="codehilite"><pre>'
- '<span class="c">#line 1</span>\n'
- '<span class="hll"><span class="c">#line 2</span>\n</span>'
- '<span class="c">#line 3</span>\n'
- '</pre></div>'
+ self.assertStartsWith(
+ '<div class="codehilite"><pre><span class="hll"',
+ md.convert(text).replace('<span></span>', '')
)
else:
self.assertEqual(
)
-class TestFencedCode(unittest.TestCase):
+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
md = markdown.Markdown(
extensions=[
markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
- 'markdown.extensions.fenced_code'
+ 'fenced_code'
]
)
if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite"><pre>'
- '<span class="hll">line 1\n</span>'
- 'line 2\n'
- '<span class="hll">line 3\n</span>'
- '</pre></div>'
+ self.assertStartsWith(
+ '<div class="codehilite"><pre><span class="hll"',
+ md.convert(text).replace('<span></span>', '')
)
else:
self.assertEqual(
md = markdown.Markdown(
extensions=[
markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
- 'markdown.extensions.fenced_code'
+ 'fenced_code'
]
)
if self.has_pygments:
- self.assertEqual(
- md.convert(text),
- '<div class="codehilite"><pre>'
- '<span class="hll"><span class="c">#line 1</span>\n</span>'
- '<span class="c">#line 2</span>\n'
- '<span class="hll"><span class="c">#line 3</span>\n</span>'
- '</pre></div>'
+ self.assertStartsWith(
+ '<div class="codehilite"><pre><span class="hll"',
+ md.convert(text).replace('<span></span>', '')
)
else:
self.assertEqual(
'#line 3</code></pre>'
)
+ def testFencedLanguageAndPygmentsDisabled(self):
+ """ Test if fenced_code honors CodeHilite option use_pygments=False. """
-class TestHeaderId(unittest.TestCase):
- """ Test HeaderId Extension. """
-
- def setUp(self):
- self.md = markdown.Markdown(extensions=['markdown.extensions.headerid'])
-
- def testBasicHeaderId(self):
- """ Test Basic HeaderID """
-
- text = "# Some Header #"
- self.assertEqual(
- self.md.convert(text),
- '<h1 id="some-header">Some Header</h1>'
- )
-
- def testNoAutoIds(self):
- """ Test HeaderIDs with no auto generated IDs. """
-
- text = '# Some Header\n# Another Header'
- self.assertEqual(
- markdown.markdown(text, [markdown.extensions.headerid.HeaderIdExtension(forceid=False)]),
- '<h1>Some Header</h1>\n'
- '<h1>Another Header</h1>'
- )
-
- def testHeaderIdWithMetaData(self):
- """ Test Header IDs with MetaData extension. """
-
- text = '''header_level: 2
-header_forceid: Off
-
-# A Header'''
- self.assertEqual(
- markdown.markdown(text, ['markdown.extensions.headerid', 'markdown.extensions.meta']),
- '<h2>A Header</h2>'
- )
-
- def testHeaderIdWithAttr_List(self):
- """ Test HeaderIDs with Attr_List extension. """
-
- text = '# Header1 {: #foo }\n# Header2 {: .bar }'
- self.assertEqual(
- markdown.markdown(text, ['markdown.extensions.headerid', 'markdown.extensions.attr_list']),
- '<h1 id="foo">Header1</h1>\n'
- '<h1 class="bar" id="header2">Header2</h1>'
- )
- # Switch order extensions are loaded - should be no change in behavior.
- self.assertEqual(
- markdown.markdown(text, ['markdown.extensions.attr_list', 'markdown.extensions.headerid']),
- '<h1 id="foo">Header1</h1>\n'
- '<h1 class="bar" id="header2">Header2</h1>'
+ text = '```python\nfrom __future__ import braces\n```'
+ md = markdown.Markdown(
+ extensions=[
+ markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False),
+ 'fenced_code'
+ ]
)
+ self.assertTrue('<code class="language-python">' in md.convert(text))
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. """
self.assertEqual(self.md.convert(text), '')
self.assertEqual(self.md.Meta, {'title': ['No newline']})
- def testYamlObjectMetaData(self):
- """ Test metadata specified as a complex YAML object. """
- md = markdown.Markdown(extensions=[markdown.extensions.meta.MetaExtension(yaml=True)])
- text = '''---
-Author: John Doe
-Date: 2014-11-29 14:15:16
-Integer: 0x16
----
+ def testMetaDataReset(self):
+ """ Test that reset call remove Meta entirely """
-Some content.'''
- self.assertEqual(md.convert(text), '<p>Some content.</p>')
- self.assertEqual(
- md.Meta, {
- 'Author': 'John Doe',
- 'Date': datetime.datetime(2014, 11, 29, 14, 15, 16),
- 'Integer': 22
- }
- )
+ text = '''Title: A Test Doc.
+Author: Waylan Limberg
+ John Doe
+Blank_Data:
+
+The body. This is paragraph one.'''
+ self.md.convert(text)
+
+ self.md.reset()
+ self.assertEqual(self.md.Meta, {})
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):
""" Test Simple Settings. """
self.assertEqual(markdown.markdown(
- self.text, [
+ self.text, extensions=[
markdown.extensions.wikilinks.WikiLinkExtension(
base_url='/wiki/',
end_url='.html',
""" 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', '')
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 '
""" 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
self.assertEqual(RE.match(test).groups(), expected)
-class TestTOC(unittest.TestCase):
+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. """
'<h1 id="header-1">Header 1</h1>\n'
'<h2 id="header-2">Header 2</h2>'
)
- self.assertTrue(md.toc.startswith('<div class="toc">'))
+ self.assertStartsWith('<div class="toc">', md.toc)
def testReset(self):
""" Test TOC Reset. """
self.assertEqual(self.md.toc, '')
self.md.convert('# Header 1\n\n## Header 2')
- self.assertTrue(self.md.toc.startswith('<div class="toc">'))
+ self.assertStartsWith('<div class="toc">', self.md.toc)
self.md.reset()
self.assertEqual(self.md.toc, '')
'<h2 id="header-2"><a class="toclink" href="#header-2">Header <em>2</em></a></h2>'
)
+ def testAnchorLinkWithSingleInlineCode(self):
+ """ Test TOC Anchorlink with single inline code. """
+ md = markdown.Markdown(
+ extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)]
+ )
+ text = '# This is `code`.'
+ self.assertEqual(
+ md.convert(text),
+ '<h1 id="this-is-code">' # noqa
+ '<a class="toclink" href="#this-is-code">' # noqa
+ 'This is <code>code</code>.' # noqa
+ '</a>' # noqa
+ '</h1>' # noqa
+ )
+
+ def testAnchorLinkWithDoubleInlineCode(self):
+ """ Test TOC Anchorlink with double inline code. """
+ md = markdown.Markdown(
+ extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)]
+ )
+ text = '# This is `code` and `this` too.'
+ self.assertEqual(
+ md.convert(text),
+ '<h1 id="this-is-code-and-this-too">' # noqa
+ '<a class="toclink" href="#this-is-code-and-this-too">' # noqa
+ 'This is <code>code</code> and <code>this</code> too.' # noqa
+ '</a>' # noqa
+ '</h1>' # noqa
+ )
+
+ def testPermalink(self):
+ """ Test TOC Permalink. """
+ md = markdown.Markdown(
+ extensions=[markdown.extensions.toc.TocExtension(permalink=True)]
+ )
+ text = '# Header'
+ self.assertEqual(
+ md.convert(text),
+ '<h1 id="header">' # noqa
+ 'Header' # noqa
+ '<a class="headerlink" href="#header" title="Permanent link">¶</a>' # noqa
+ '</h1>' # noqa
+ )
+
+ def testPermalinkWithSingleInlineCode(self):
+ """ Test TOC Permalink with single inline code. """
+ md = markdown.Markdown(
+ extensions=[markdown.extensions.toc.TocExtension(permalink=True)]
+ )
+ text = '# This is `code`.'
+ self.assertEqual(
+ md.convert(text),
+ '<h1 id="this-is-code">' # noqa
+ 'This is <code>code</code>.' # noqa
+ '<a class="headerlink" href="#this-is-code" title="Permanent link">¶</a>' # noqa
+ '</h1>' # noqa
+ )
+
+ def testPermalinkWithDoubleInlineCode(self):
+ """ Test TOC Permalink with double inline code. """
+ md = markdown.Markdown(
+ extensions=[markdown.extensions.toc.TocExtension(permalink=True)]
+ )
+ text = '# This is `code` and `this` too.'
+ self.assertEqual(
+ md.convert(text),
+ '<h1 id="this-is-code-and-this-too">' # noqa
+ 'This is <code>code</code> and <code>this</code> too.' # noqa
+ '<a class="headerlink" href="#this-is-code-and-this-too" title="Permanent link">¶</a>' # noqa
+ '</h1>' # noqa
+ )
+
def testTitle(self):
""" Test TOC Title. """
md = markdown.Markdown(
extensions=[markdown.extensions.toc.TocExtension(title='Table of Contents')]
)
md.convert('# Header 1\n\n## Header 2')
- self.assertTrue(md.toc.startswith('<div class="toc"><span class="toctitle">Table of Contents</span><ul>'))
+ self.assertStartsWith(
+ '<div class="toc"><span class="toctitle">Table of Contents</span><ul>',
+ md.toc
+ )
def testWithAttrList(self):
""" Test TOC with attr_list Extension. """
- md = markdown.Markdown(extensions=['markdown.extensions.toc', 'markdown.extensions.attr_list'])
- text = '# Header 1\n\n## Header 2 { #foo }'
+ md = markdown.Markdown(extensions=['toc', 'attr_list'])
+ text = '# Header 1\n\n## Header 2 { #foo }\n\n## Header 3 { data-toc-label="Foo Bar"}'
self.assertEqual(
md.convert(text),
'<h1 id="header-1">Header 1</h1>\n'
- '<h2 id="foo">Header 2</h2>'
+ '<h2 id="foo">Header 2</h2>\n'
+ '<h2 id="header-3">Header 3</h2>'
)
self.assertEqual(
md.toc,
'<div class="toc">\n'
- '<ul>\n' # noqa
- '<li><a href="#header-1">Header 1</a>' # noqa
- '<ul>\n' # noqa
- '<li><a href="#foo">Header 2</a></li>\n' # noqa
- '</ul>\n' # noqa
- '</li>\n' # noqa
- '</ul>\n' # noqa
+ '<ul>\n' # noqa
+ '<li><a href="#header-1">Header 1</a>' # noqa
+ '<ul>\n' # noqa
+ '<li><a href="#foo">Header 2</a></li>\n' # noqa
+ '<li><a href="#header-3">Foo Bar</a></li>\n' # noqa
+ '</ul>\n' # noqa
+ '</li>\n' # noqa
+ '</ul>\n' # noqa
'</div>\n'
)
+ self.assertEqual(
+ md.toc_tokens,
+ [
+ {
+ 'level': 1,
+ 'id': 'header-1',
+ 'name': 'Header 1',
+ 'children': [
+ {'level': 2, 'id': 'foo', 'name': 'Header 2', 'children': []},
+ {'level': 2, 'id': 'header-3', 'name': 'Foo Bar', 'children': []}
+ ]
+ }
+ ]
+ )
def testUniqueFunc(self):
""" Test 'unique' function. """
self.assertEqual(unique('foo', ids), 'foo_1')
self.assertEqual(ids, set(['foo', 'foo_1']))
+ def testTocInHeaders(self):
+
+ text = '[TOC]\n#[TOC]'
+ self.assertEqual(
+ self.md.convert(text),
+ '<div class="toc">\n' # noqa
+ '<ul>\n' # noqa
+ '<li><a href="#toc">[TOC]</a></li>\n' # noqa
+ '</ul>\n' # noqa
+ '</div>\n' # noqa
+ '<h1 id="toc">[TOC]</h1>' # noqa
+ )
+
+ text = '#[TOC]\n[TOC]'
+ self.assertEqual(
+ self.md.convert(text),
+ '<h1 id="toc">[TOC]</h1>\n' # noqa
+ '<div class="toc">\n' # noqa
+ '<ul>\n' # noqa
+ '<li><a href="#toc">[TOC]</a></li>\n' # noqa
+ '</ul>\n' # noqa
+ '</div>' # noqa
+ )
+
+ text = '[TOC]\n# *[TOC]*'
+ self.assertEqual(
+ self.md.convert(text),
+ '<div class="toc">\n' # noqa
+ '<ul>\n' # noqa
+ '<li><a href="#toc">[TOC]</a></li>\n' # noqa
+ '</ul>\n' # noqa
+ '</div>\n' # noqa
+ '<h1 id="toc"><em>[TOC]</em></h1>' # noqa
+ )
+
+ def testMaxLevel(self):
+ """ Test toc_depth setting """
+ md = markdown.Markdown(
+ extensions=[markdown.extensions.toc.TocExtension(toc_depth=2)]
+ )
+ text = '# Header 1\n\n## Header 2\n\n###Header 3 not in TOC'
+ self.assertEqual(
+ md.convert(text),
+ '<h1 id="header-1">Header 1</h1>\n'
+ '<h2 id="header-2">Header 2</h2>\n'
+ '<h3>Header 3 not in TOC</h3>'
+ )
+ self.assertEqual(
+ md.toc,
+ '<div class="toc">\n'
+ '<ul>\n' # noqa
+ '<li><a href="#header-1">Header 1</a>' # noqa
+ '<ul>\n' # noqa
+ '<li><a href="#header-2">Header 2</a></li>\n' # noqa
+ '</ul>\n' # noqa
+ '</li>\n' # noqa
+ '</ul>\n' # noqa
+ '</div>\n'
+ )
+
+ self.assertNotIn("Header 3", md.toc)
+
+ def testMaxLevelwithBaseLevel(self):
+ """ Test toc_depth setting together with baselevel """
+ md = markdown.Markdown(
+ extensions=[markdown.extensions.toc.TocExtension(toc_depth=3,
+ baselevel=2)]
+ )
+ text = '# Some Header\n\n## Next Level\n\n### Too High'
+ self.assertEqual(
+ md.convert(text),
+ '<h2 id="some-header">Some Header</h2>\n'
+ '<h3 id="next-level">Next Level</h3>\n'
+ '<h4>Too High</h4>'
+ )
+ self.assertEqual(
+ md.toc,
+ '<div class="toc">\n'
+ '<ul>\n' # noqa
+ '<li><a href="#some-header">Some Header</a>' # noqa
+ '<ul>\n' # noqa
+ '<li><a href="#next-level">Next Level</a></li>\n' # noqa
+ '</ul>\n' # noqa
+ '</li>\n' # noqa
+ '</ul>\n' # noqa
+ '</div>\n'
+ )
+ self.assertNotIn("Too High", md.toc)
+
class TestSmarty(unittest.TestCase):
def setUp(self):
config = {
- 'markdown.extensions.smarty': [
+ 'smarty': [
('smart_angled_quotes', True),
('substitutions', {
'ndash': '\u2013',
]
}
self.md = markdown.Markdown(
- extensions=['markdown.extensions.smarty'],
+ extensions=['smarty'],
extension_configs=config
)
is the ‚mdash‘: \u2014
Must not be confused with ‚ndash‘ (\u2013) \u2026 ]</p>"""
self.assertEqual(self.md.convert(text), correct)
+
+
+class TestFootnotes(unittest.TestCase):
+ """ Test Footnotes extension. """
+
+ def testBacklinkText(self):
+ md = markdown.Markdown(
+ extensions=['footnotes'],
+ extension_configs={'footnotes': {'BACKLINK_TEXT': 'back'}}
+ )
+ text = 'paragraph[^1]\n\n[^1]: A Footnote'
+ self.assertEqual(
+ md.convert(text),
+ '<p>paragraph<sup id="fnref:1"><a class="footnote-ref" href="#fn:1" rel="footnote">1</a></sup></p>\n'
+ '<div class="footnote">\n'
+ '<hr />\n'
+ '<ol>\n'
+ '<li id="fn:1">\n'
+ '<p>A Footnote <a class="footnote-backref" href="#fnref:1" rev="footnote"'
+ ' title="Jump back to footnote 1 in the text">back</a></p>\n'
+ '</li>\n'
+ '</ol>\n'
+ '</div>'
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import LegacyTestCase, Kwargs
+import os
+import warnings
+
+# Warnings should cause tests to fail...
+warnings.simplefilter('error')
+# Except for the warnings that shouldn't
+warnings.filterwarnings('default', category=PendingDeprecationWarning)
+warnings.filterwarnings('default', category=DeprecationWarning, module='markdown')
+
+parent_test_dir = os.path.abspath(os.path.dirname(__file__))
+
+
+class TestBasic(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'basic')
+
+
+class TestMisc(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'misc')
+
+
+class TestPhp(LegacyTestCase):
+ """
+ Notes on "excluded" tests:
+
+ Quotes in attributes: attributes get output in differant order
+
+ Inline HTML (Span): Backtick in raw HTML attribute TODO: fixme
+
+ Backslash escapes: Weird whitespace issue in output
+
+ Ins & del: Our behavior follows markdown.pl I think PHP is wrong here
+
+ Auto Links: TODO: fix raw HTML so is doesn't match <hr@example.com> as a <hr>.
+
+ Empty List Item: We match markdown.pl here. Maybe someday we'll support this
+
+ Headers: TODO: fix headers to not require blank line before
+
+ Mixed OLs and ULs: We match markdown.pl here. I think PHP is wrong here
+
+ Emphasis: We have various minor differances in combined & incorrect em markup.
+ Maybe fix a few of them - but most aren't too important
+
+ Code block in a list item: We match markdown.pl - not sure how php gets that output??
+
+ PHP-Specific Bugs: Not sure what to make of the escaping stuff here.
+ Why is PHP not removing a blackslash?
+ """
+ location = os.path.join(parent_test_dir, 'php')
+ normalize = True
+ input_ext = '.text'
+ output_ext = '.xhtml'
+ exclude = [
+ 'Quotes_in_attributes',
+ 'Inline_HTML_(Span)',
+ 'Backslash_escapes',
+ 'Ins_&_del',
+ 'Auto_Links',
+ 'Empty_List_Item',
+ 'Headers',
+ 'Mixed_OLs_and_ULs',
+ 'Emphasis',
+ 'Code_block_in_a_list_item',
+ 'PHP_Specific_Bugs'
+ ]
+
+
+# class TestPhpExtra(LegacyTestCase):
+# location = os.path.join(parent_test_dir, 'php/extra')
+# normalize = True
+# input_ext = '.text'
+# output_ext = '.xhtml'
+# default_kwargs = Kwargs(extensions=['extra'])
+
+
+class TestPl2004(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'pl/Tests_2004')
+ normalize = True
+ input_ext = '.text'
+ exclude = ['Yuri_Footnotes', 'Yuri_Attributes']
+
+
+class TestPl2007(LegacyTestCase):
+ """
+ Notes on "excluded" tests:
+
+ Images: the attributes don't get ordered the same so we skip this
+
+ Code Blocks: some weird whitespace issue
+
+ Links, reference style: weird issue with nested brackets TODO: fixme
+
+ Backslash escapes: backticks in raw html attributes TODO: fixme
+
+ Code Spans: more backticks in raw html attributes TODO: fixme
+ """
+ location = os.path.join(parent_test_dir, 'pl/Tests_2007')
+ normalize = True
+ input_ext = '.text'
+ exclude = [
+ 'Images',
+ 'Code_Blocks',
+ 'Links,_reference_style',
+ 'Backslash_escapes',
+ 'Code_Spans'
+ ]
+
+
+class TestExtensions(LegacyTestCase):
+ location = os.path.join(parent_test_dir, 'extensions')
+ exclude = ['codehilite']
+
+ attr_list = Kwargs(extensions=['attr_list', 'def_list', 'smarty'])
+
+ codehilite = Kwargs(extensions=['codehilite'])
+
+ toc = Kwargs(extensions=['toc'])
+
+ toc_invalid = Kwargs(extensions=['toc'])
+
+ toc_out_of_order = Kwargs(extensions=['toc'])
+
+ toc_nested = Kwargs(
+ extensions=['toc'],
+ extension_configs={'toc': {'permalink': True}}
+ )
+
+ toc_nested2 = Kwargs(
+ extensions=['toc'],
+ extension_configs={'toc': {'permalink': "[link]"}}
+ )
+
+ toc_nested_list = Kwargs(extensions=['toc'])
+
+ wikilinks = Kwargs(extensions=['wikilinks'])
+
+ fenced_code = Kwargs(extensions=['fenced_code'])
+
+ github_flavored = Kwargs(extensions=['fenced_code'])
+
+ sane_lists = Kwargs(extensions=['sane_lists'])
+
+ nl2br_w_attr_list = Kwargs(extensions=['nl2br', 'attr_list'])
+
+ admonition = Kwargs(extensions=['admonition'])
+
+ smarty = Kwargs(
+ 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=['extra'])
+
+ loose_def_list = Kwargs(extensions=['def_list'])
+
+ simple_def_lists = Kwargs(extensions=['def_list'])
+
+ abbr = Kwargs(extensions=['abbr'])
+
+ footnotes = Kwargs(extensions=['footnotes'])
+
+ tables = Kwargs(extensions=['tables'])
+
+ tables_and_attr_list = Kwargs(extensions=['tables', 'attr_list'])
+
+ extra_config = Kwargs(
+ extensions=['extra'],
+ extension_configs={
+ 'extra': {
+ 'footnotes': {
+ 'PLACE_MARKER': '~~~placemarker~~~'
+ }
+ }
+ }
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestCodeBlocks(TestCase):
+
+ def test_spaced_codeblock(self):
+ self.assertMarkdownRenders(
+ ' # A code block.',
+
+ self.dedent(
+ """
+ <pre><code># A code block.
+ </code></pre>
+ """
+ )
+ )
+
+ def test_tabbed_codeblock(self):
+ self.assertMarkdownRenders(
+ '\t# A code block.',
+
+ self.dedent(
+ """
+ <pre><code># A code block.
+ </code></pre>
+ """
+ )
+ )
+
+ def test_multiline_codeblock(self):
+ self.assertMarkdownRenders(
+ ' # Line 1\n # Line 2\n',
+
+ self.dedent(
+ """
+ <pre><code># Line 1
+ # Line 2
+ </code></pre>
+ """
+ )
+ )
+
+ def test_codeblock_with_blankline(self):
+ self.assertMarkdownRenders(
+ ' # Line 1\n\n # Line 2\n',
+
+ self.dedent(
+ """
+ <pre><code># Line 1
+
+ # Line 2
+ </code></pre>
+ """
+ )
+ )
+
+ def test_codeblock_escape(self):
+ self.assertMarkdownRenders(
+ ' <foo & bar>',
+
+ self.dedent(
+ """
+ <pre><code><foo & bar>
+ </code></pre>
+ """
+ )
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+import unittest
+from markdown.test_tools import TestCase
+
+
+class TestSetextHeaders(TestCase):
+
+ def test_setext_h1(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is an H1
+ =============
+ """
+ ),
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_setext_h2(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is an H2
+ -------------
+ """
+ ),
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_setext_h1_mismatched_length(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is an H1
+ ===
+ """
+ ),
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_setext_h2_mismatched_length(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is an H2
+ ---
+ """
+ ),
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_setext_h1_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is an H1
+ =============
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h1>This is an H1</h1>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_setext_h2_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is an H2
+ -------------
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h2>This is an H2</h2>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ # TODO: fix this
+ # see http://johnmacfarlane.net/babelmark2/?normalize=1&text=Paragraph%0AAn+H1%0A%3D%3D%3D%3D%3D
+ @unittest.skip('This is broken in Python-Markdown')
+ def test_p_followed_by_setext_h1(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is a Paragraph.
+ Followed by an H1 with no blank line.
+ =====================================
+ """
+ ),
+ self.dedent(
+ """
+ <p>This is a Paragraph.</p>
+ <h1>Followed by an H1 with no blank line.</h1>
+ """
+ )
+ )
+
+ # TODO: fix this
+ # see http://johnmacfarlane.net/babelmark2/?normalize=1&text=Paragraph%0AAn+H2%0A-----
+ @unittest.skip('This is broken in Python-Markdown')
+ def test_p_followed_by_setext_h2(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is a Paragraph.
+ Followed by an H2 with no blank line.
+ -------------------------------------
+ """
+ ),
+ self.dedent(
+ """
+ <p>This is a Paragraph.</p>
+ <h2>Followed by an H2 with no blank line.</h2>
+ """
+ )
+ )
+
+
+class TestHashHeaders(TestCase):
+
+ def test_hash_h1_open(self):
+ self.assertMarkdownRenders(
+ '# This is an H1',
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_hash_h2_open(self):
+ self.assertMarkdownRenders(
+ '## This is an H2',
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_hash_h3_open(self):
+ self.assertMarkdownRenders(
+ '### This is an H3',
+
+ '<h3>This is an H3</h3>'
+ )
+
+ def test_hash_h4_open(self):
+ self.assertMarkdownRenders(
+ '#### This is an H4',
+
+ '<h4>This is an H4</h4>'
+ )
+
+ def test_hash_h5_open(self):
+ self.assertMarkdownRenders(
+ '##### This is an H5',
+
+ '<h5>This is an H5</h5>'
+ )
+
+ def test_hash_h6_open(self):
+ self.assertMarkdownRenders(
+ '###### This is an H6',
+
+ '<h6>This is an H6</h6>'
+ )
+
+ def test_hash_gt6_open(self):
+ self.assertMarkdownRenders(
+ '####### This is an H6',
+
+ '<h6># This is an H6</h6>'
+ )
+
+ def test_hash_h1_open_missing_space(self):
+ self.assertMarkdownRenders(
+ '#This is an H1',
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_hash_h2_open_missing_space(self):
+ self.assertMarkdownRenders(
+ '##This is an H2',
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_hash_h3_open_missing_space(self):
+ self.assertMarkdownRenders(
+ '###This is an H3',
+
+ '<h3>This is an H3</h3>'
+ )
+
+ def test_hash_h4_open_missing_space(self):
+ self.assertMarkdownRenders(
+ '####This is an H4',
+
+ '<h4>This is an H4</h4>'
+ )
+
+ def test_hash_h5_open_missing_space(self):
+ self.assertMarkdownRenders(
+ '#####This is an H5',
+
+ '<h5>This is an H5</h5>'
+ )
+
+ def test_hash_h6_open_missing_space(self):
+ self.assertMarkdownRenders(
+ '######This is an H6',
+
+ '<h6>This is an H6</h6>'
+ )
+
+ def test_hash_gt6_open_missing_space(self):
+ self.assertMarkdownRenders(
+ '#######This is an H6',
+
+ '<h6>#This is an H6</h6>'
+ )
+
+ def test_hash_h1_closed(self):
+ self.assertMarkdownRenders(
+ '# This is an H1 #',
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_hash_h2_closed(self):
+ self.assertMarkdownRenders(
+ '## This is an H2 ##',
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_hash_h3_closed(self):
+ self.assertMarkdownRenders(
+ '### This is an H3 ###',
+
+ '<h3>This is an H3</h3>'
+ )
+
+ def test_hash_h4_closed(self):
+ self.assertMarkdownRenders(
+ '#### This is an H4 ####',
+
+ '<h4>This is an H4</h4>'
+ )
+
+ def test_hash_h5_closed(self):
+ self.assertMarkdownRenders(
+ '##### This is an H5 #####',
+
+ '<h5>This is an H5</h5>'
+ )
+
+ def test_hash_h6_closed(self):
+ self.assertMarkdownRenders(
+ '###### This is an H6 ######',
+
+ '<h6>This is an H6</h6>'
+ )
+
+ def test_hash_gt6_closed(self):
+ self.assertMarkdownRenders(
+ '####### This is an H6 #######',
+
+ '<h6># This is an H6</h6>'
+ )
+
+ def test_hash_h1_closed_missing_space(self):
+ self.assertMarkdownRenders(
+ '#This is an H1#',
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_hash_h2_closed_missing_space(self):
+ self.assertMarkdownRenders(
+ '##This is an H2##',
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_hash_h3_closed_missing_space(self):
+ self.assertMarkdownRenders(
+ '###This is an H3###',
+
+ '<h3>This is an H3</h3>'
+ )
+
+ def test_hash_h4_closed_missing_space(self):
+ self.assertMarkdownRenders(
+ '####This is an H4####',
+
+ '<h4>This is an H4</h4>'
+ )
+
+ def test_hash_h5_closed_missing_space(self):
+ self.assertMarkdownRenders(
+ '#####This is an H5#####',
+
+ '<h5>This is an H5</h5>'
+ )
+
+ def test_hash_h6_closed_missing_space(self):
+ self.assertMarkdownRenders(
+ '######This is an H6######',
+
+ '<h6>This is an H6</h6>'
+ )
+
+ def test_hash_gt6_closed_missing_space(self):
+ self.assertMarkdownRenders(
+ '#######This is an H6#######',
+
+ '<h6>#This is an H6</h6>'
+ )
+
+ def test_hash_h1_closed_mismatch(self):
+ self.assertMarkdownRenders(
+ '# This is an H1 ##',
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_hash_h2_closed_mismatch(self):
+ self.assertMarkdownRenders(
+ '## This is an H2 #',
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_hash_h3_closed_mismatch(self):
+ self.assertMarkdownRenders(
+ '### This is an H3 #',
+
+ '<h3>This is an H3</h3>'
+ )
+
+ def test_hash_h4_closed_mismatch(self):
+ self.assertMarkdownRenders(
+ '#### This is an H4 #',
+
+ '<h4>This is an H4</h4>'
+ )
+
+ def test_hash_h5_closed_mismatch(self):
+ self.assertMarkdownRenders(
+ '##### This is an H5 #',
+
+ '<h5>This is an H5</h5>'
+ )
+
+ def test_hash_h6_closed_mismatch(self):
+ self.assertMarkdownRenders(
+ '###### This is an H6 #',
+
+ '<h6>This is an H6</h6>'
+ )
+
+ def test_hash_gt6_closed_mismatch(self):
+ self.assertMarkdownRenders(
+ '####### This is an H6 ##################',
+
+ '<h6># This is an H6</h6>'
+ )
+
+ def test_hash_h1_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ # This is an H1
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h1>This is an H1</h1>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_hash_h2_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ## This is an H2
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h2>This is an H2</h2>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_hash_h3_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ### This is an H3
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h3>This is an H3</h3>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_hash_h4_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ #### This is an H4
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h4>This is an H4</h4>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_hash_h5_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ##### This is an H5
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h5>This is an H5</h5>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_hash_h6_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ###### This is an H6
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h6>This is an H6</h6>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_hash_h1_leading_space(self):
+ self.assertMarkdownRenders(
+ ' # This is an H1',
+
+ '<p># This is an H1</p>'
+ )
+
+ def test_hash_h2_leading_space(self):
+ self.assertMarkdownRenders(
+ ' ## This is an H2',
+
+ '<p>## This is an H2</p>'
+ )
+
+ def test_hash_h3_leading_space(self):
+ self.assertMarkdownRenders(
+ ' ### This is an H3',
+
+ '<p>### This is an H3</p>'
+ )
+
+ def test_hash_h4_leading_space(self):
+ self.assertMarkdownRenders(
+ ' #### This is an H4',
+
+ '<p>#### This is an H4</p>'
+ )
+
+ def test_hash_h5_leading_space(self):
+ self.assertMarkdownRenders(
+ ' ##### This is an H5',
+
+ '<p>##### This is an H5</p>'
+ )
+
+ def test_hash_h6_leading_space(self):
+ self.assertMarkdownRenders(
+ ' ###### This is an H6',
+
+ '<p>###### This is an H6</p>'
+ )
+
+ def test_hash_h1_open_trailing_space(self):
+ self.assertMarkdownRenders(
+ '# This is an H1 ',
+
+ '<h1>This is an H1</h1>'
+ )
+
+ def test_hash_h2_open_trailing_space(self):
+ self.assertMarkdownRenders(
+ '## This is an H2 ',
+
+ '<h2>This is an H2</h2>'
+ )
+
+ def test_hash_h3_open_trailing_space(self):
+ self.assertMarkdownRenders(
+ '### This is an H3 ',
+
+ '<h3>This is an H3</h3>'
+ )
+
+ def test_hash_h4_open_trailing_space(self):
+ self.assertMarkdownRenders(
+ '#### This is an H4 ',
+
+ '<h4>This is an H4</h4>'
+ )
+
+ def test_hash_h5_open_trailing_space(self):
+ self.assertMarkdownRenders(
+ '##### This is an H5 ',
+
+ '<h5>This is an H5</h5>'
+ )
+
+ def test_hash_h6_open_trailing_space(self):
+ self.assertMarkdownRenders(
+ '###### This is an H6 ',
+
+ '<h6>This is an H6</h6>'
+ )
+
+ def test_hash_gt6_open_trailing_space(self):
+ self.assertMarkdownRenders(
+ '####### This is an H6 ',
+
+ '<h6># This is an H6</h6>'
+ )
+
+ # TODO: Possibly change the following behavior. While this follows the behavior
+ # of markdown.pl, it is rather uncommon and not nessecarily intuitive.
+ # See: http://johnmacfarlane.net/babelmark2/?normalize=1&text=%23+This+is+an+H1+%23+
+ def test_hash_h1_closed_trailing_space(self):
+ self.assertMarkdownRenders(
+ '# This is an H1 # ',
+
+ '<h1>This is an H1 #</h1>'
+ )
+
+ def test_hash_h2_closed_trailing_space(self):
+ self.assertMarkdownRenders(
+ '## This is an H2 ## ',
+
+ '<h2>This is an H2 ##</h2>'
+ )
+
+ def test_hash_h3_closed_trailing_space(self):
+ self.assertMarkdownRenders(
+ '### This is an H3 ### ',
+
+ '<h3>This is an H3 ###</h3>'
+ )
+
+ def test_hash_h4_closed_trailing_space(self):
+ self.assertMarkdownRenders(
+ '#### This is an H4 #### ',
+
+ '<h4>This is an H4 ####</h4>'
+ )
+
+ def test_hash_h5_closed_trailing_space(self):
+ self.assertMarkdownRenders(
+ '##### This is an H5 ##### ',
+
+ '<h5>This is an H5 #####</h5>'
+ )
+
+ def test_hash_h6_closed_trailing_space(self):
+ self.assertMarkdownRenders(
+ '###### This is an H6 ###### ',
+
+ '<h6>This is an H6 ######</h6>'
+ )
+
+ def test_hash_gt6_closed_trailing_space(self):
+ self.assertMarkdownRenders(
+ '####### This is an H6 ####### ',
+
+ '<h6># This is an H6 #######</h6>'
+ )
+
+ def test_no_blank_lines_between_hashs(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ # This is an H1
+ ## This is an H2
+ """
+ ),
+ self.dedent(
+ """
+ <h1>This is an H1</h1>
+ <h2>This is an H2</h2>
+ """
+ )
+ )
+
+ def test_random_hash_levels(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ### H3
+ ###### H6
+ # H1
+ ##### H5
+ #### H4
+ ## H2
+ ### H3
+ """
+ ),
+ self.dedent(
+ """
+ <h3>H3</h3>
+ <h6>H6</h6>
+ <h1>H1</h1>
+ <h5>H5</h5>
+ <h4>H4</h4>
+ <h2>H2</h2>
+ <h3>H3</h3>
+ """
+ )
+ )
+
+ def test_hash_followed_by_p(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ # This is an H1
+ Followed by a Paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <h1>This is an H1</h1>
+ <p>Followed by a Paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_p_followed_by_hash(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is a Paragraph.
+ # Followed by an H1 with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <p>This is a Paragraph.</p>
+ <h1>Followed by an H1 with no blank line.</h1>
+ """
+ )
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestHorizontalRules(TestCase):
+
+ def test_hr_asterisks(self):
+ self.assertMarkdownRenders(
+ '***',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_spaces(self):
+ self.assertMarkdownRenders(
+ '* * *',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_long(self):
+ self.assertMarkdownRenders(
+ '*******',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_spaces_long(self):
+ self.assertMarkdownRenders(
+ '* * * * * * *',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_1_indent(self):
+ self.assertMarkdownRenders(
+ ' ***',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_spaces_1_indent(self):
+ self.assertMarkdownRenders(
+ ' * * *',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_2_indent(self):
+ self.assertMarkdownRenders(
+ ' ***',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_spaces_2_indent(self):
+ self.assertMarkdownRenders(
+ ' * * *',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_3_indent(self):
+ self.assertMarkdownRenders(
+ ' ***',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_spaces_3_indent(self):
+ self.assertMarkdownRenders(
+ ' * * *',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_trailing_space(self):
+ self.assertMarkdownRenders(
+ '*** ',
+
+ '<hr />'
+ )
+
+ def test_hr_asterisks_spaces_trailing_space(self):
+ self.assertMarkdownRenders(
+ '* * * ',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens(self):
+ self.assertMarkdownRenders(
+ '---',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_spaces(self):
+ self.assertMarkdownRenders(
+ '- - -',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_long(self):
+ self.assertMarkdownRenders(
+ '-------',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_spaces_long(self):
+ self.assertMarkdownRenders(
+ '- - - - - - -',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_1_indent(self):
+ self.assertMarkdownRenders(
+ ' ---',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_spaces_1_indent(self):
+ self.assertMarkdownRenders(
+ ' - - -',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_2_indent(self):
+ self.assertMarkdownRenders(
+ ' ---',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_spaces_2_indent(self):
+ self.assertMarkdownRenders(
+ ' - - -',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_3_indent(self):
+ self.assertMarkdownRenders(
+ ' ---',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_spaces_3_indent(self):
+ self.assertMarkdownRenders(
+ ' - - -',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_trailing_space(self):
+ self.assertMarkdownRenders(
+ '--- ',
+
+ '<hr />'
+ )
+
+ def test_hr_hyphens_spaces_trailing_space(self):
+ self.assertMarkdownRenders(
+ '- - - ',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores(self):
+ self.assertMarkdownRenders(
+ '___',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_spaces(self):
+ self.assertMarkdownRenders(
+ '_ _ _',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_long(self):
+ self.assertMarkdownRenders(
+ '_______',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_spaces_long(self):
+ self.assertMarkdownRenders(
+ '_ _ _ _ _ _ _',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_1_indent(self):
+ self.assertMarkdownRenders(
+ ' ___',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_spaces_1_indent(self):
+ self.assertMarkdownRenders(
+ ' _ _ _',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_2_indent(self):
+ self.assertMarkdownRenders(
+ ' ___',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_spaces_2_indent(self):
+ self.assertMarkdownRenders(
+ ' _ _ _',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_3_indent(self):
+ self.assertMarkdownRenders(
+ ' ___',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_spaces_3_indent(self):
+ self.assertMarkdownRenders(
+ ' _ _ _',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_trailing_space(self):
+ self.assertMarkdownRenders(
+ '___ ',
+
+ '<hr />'
+ )
+
+ def test_hr_underscores_spaces_trailing_space(self):
+ self.assertMarkdownRenders(
+ '_ _ _ ',
+
+ '<hr />'
+ )
+
+ def test_hr_before_paragraph(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ***
+ An HR followed by a paragraph with no blank line.
+ """
+ ),
+ self.dedent(
+ """
+ <hr />
+ <p>An HR followed by a paragraph with no blank line.</p>
+ """
+ )
+ )
+
+ def test_hr_after_paragraph(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ A paragraph followed by an HR with no blank line.
+ ***
+ """
+ ),
+ self.dedent(
+ """
+ <p>A paragraph followed by an HR with no blank line.</p>
+ <hr />
+ """
+ )
+ )
+
+ def test_not_hr_2_asterisks(self):
+ self.assertMarkdownRenders(
+ '**',
+
+ '<p>**</p>'
+ )
+
+ def test_not_hr_2_asterisks_spaces(self):
+ self.assertMarkdownRenders(
+ '* *',
+
+ self.dedent(
+ """
+ <ul>
+ <li>*</li>
+ </ul>
+ """
+ )
+ )
+
+ def test_not_hr_2_hyphens(self):
+ self.assertMarkdownRenders(
+ '--',
+
+ '<p>--</p>'
+ )
+
+ def test_not_hr_2_hyphens_spaces(self):
+ self.assertMarkdownRenders(
+ '- -',
+
+ self.dedent(
+ """
+ <ul>
+ <li>-</li>
+ </ul>
+ """
+ )
+ )
+
+ def test_not_hr_2_underscores(self):
+ self.assertMarkdownRenders(
+ '__',
+
+ '<p>__</p>'
+ )
+
+ def test_not_hr_2_underscores_spaces(self):
+ self.assertMarkdownRenders(
+ '_ _',
+
+ '<p>_ _</p>'
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestParagraphBlocks(TestCase):
+
+ def test_simple_paragraph(self):
+ self.assertMarkdownRenders(
+ 'A simple paragraph.',
+
+ '<p>A simple paragraph.</p>'
+ )
+
+ def test_blank_line_before_paragraph(self):
+ self.assertMarkdownRenders(
+ '\nA paragraph preceded by a blank line.',
+
+ '<p>A paragraph preceded by a blank line.</p>'
+ )
+
+ def test_multiline_paragraph(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ This is a paragraph
+ on multiple lines
+ with hard returns.
+ """
+ ),
+ self.dedent(
+ """
+ <p>This is a paragraph
+ on multiple lines
+ with hard returns.</p>
+ """
+ )
+ )
+
+ def test_paragraph_long_line(self):
+ self.assertMarkdownRenders(
+ 'A very long long long long long long long long long long long long long long long long long long long '
+ 'long long long long long long long long long long long long long paragraph on 1 line.',
+
+ '<p>A very long long long long long long long long long long long long long long long long long long '
+ 'long long long long long long long long long long long long long long paragraph on 1 line.</p>'
+ )
+
+ def test_2_paragraphs_long_line(self):
+ self.assertMarkdownRenders(
+ 'A very long long long long long long long long long long long long long long long long long long long '
+ 'long long long long long long long long long long long long long paragraph on 1 line.\n\n'
+
+ 'A new long long long long long long long long long long long long long long long '
+ 'long paragraph on 1 line.',
+
+ '<p>A very long long long long long long long long long long long long long long long long long long '
+ 'long long long long long long long long long long long long long long paragraph on 1 line.</p>\n'
+ '<p>A new long long long long long long long long long long long long long long long '
+ 'long paragraph on 1 line.</p>'
+ )
+
+ def test_consecutive_paragraphs(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ Paragraph 1.
+
+ Paragraph 2.
+ """
+ ),
+ self.dedent(
+ """
+ <p>Paragraph 1.</p>
+ <p>Paragraph 2.</p>
+ """
+ )
+ )
+
+ def test_consecutive_paragraphs_tab(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ Paragraph followed by a line with a tab only.
+ \t
+ Paragraph after a line with a tab only.
+ """
+ ),
+ self.dedent(
+ """
+ <p>Paragraph followed by a line with a tab only.</p>
+ <p>Paragraph after a line with a tab only.</p>
+ """
+ )
+ )
+
+ def test_consecutive_paragraphs_space(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ Paragraph followed by a line with a space only.
+
+ Paragraph after a line with a space only.
+ """
+ ),
+ self.dedent(
+ """
+ <p>Paragraph followed by a line with a space only.</p>
+ <p>Paragraph after a line with a space only.</p>
+ """
+ )
+ )
+
+ def test_consecutive_multiline_paragraphs(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ Paragraph 1, line 1.
+ Paragraph 1, line 2.
+
+ Paragraph 2, line 1.
+ Paragraph 2, line 2.
+ """
+ ),
+ self.dedent(
+ """
+ <p>Paragraph 1, line 1.
+ Paragraph 1, line 2.</p>
+ <p>Paragraph 2, line 1.
+ Paragraph 2, line 2.</p>
+ """
+ )
+ )
+
+ def test_paragraph_leading_space(self):
+ self.assertMarkdownRenders(
+ ' A paragraph with 1 leading space.',
+
+ '<p>A paragraph with 1 leading space.</p>'
+ )
+
+ def test_paragraph_2_leading_spaces(self):
+ self.assertMarkdownRenders(
+ ' A paragraph with 2 leading spaces.',
+
+ '<p>A paragraph with 2 leading spaces.</p>'
+ )
+
+ def test_paragraph_3_leading_spaces(self):
+ self.assertMarkdownRenders(
+ ' A paragraph with 3 leading spaces.',
+
+ '<p>A paragraph with 3 leading spaces.</p>'
+ )
+
+ def test_paragraph_trailing_leading_space(self):
+ self.assertMarkdownRenders(
+ ' A paragraph with 1 trailing and 1 leading space. ',
+
+ '<p>A paragraph with 1 trailing and 1 leading space. </p>'
+ )
+
+ def test_paragraph_trailing_tab(self):
+ self.assertMarkdownRenders(
+ 'A paragraph with 1 trailing tab.\t',
+
+ '<p>A paragraph with 1 trailing tab. </p>'
+ )
+
+ def test_paragraphs_CR(self):
+ self.assertMarkdownRenders(
+ 'Paragraph 1, line 1.\rParagraph 1, line 2.\r\rParagraph 2, line 1.\rParagraph 2, line 2.\r',
+
+ self.dedent(
+ """
+ <p>Paragraph 1, line 1.
+ Paragraph 1, line 2.</p>
+ <p>Paragraph 2, line 1.
+ Paragraph 2, line 2.</p>
+ """
+ )
+ )
+
+ def test_paragraphs_LF(self):
+ self.assertMarkdownRenders(
+ 'Paragraph 1, line 1.\nParagraph 1, line 2.\n\nParagraph 2, line 1.\nParagraph 2, line 2.\n',
+
+ self.dedent(
+ """
+ <p>Paragraph 1, line 1.
+ Paragraph 1, line 2.</p>
+ <p>Paragraph 2, line 1.
+ Paragraph 2, line 2.</p>
+ """
+ )
+ )
+
+ def test_paragraphs_CR_LF(self):
+ self.assertMarkdownRenders(
+ 'Paragraph 1, line 1.\r\nParagraph 1, line 2.\r\n\r\nParagraph 2, line 1.\r\nParagraph 2, line 2.\r\n',
+
+ self.dedent(
+ """
+ <p>Paragraph 1, line 1.
+ Paragraph 1, line 2.</p>
+ <p>Paragraph 2, line 1.
+ Paragraph 2, line 2.</p>
+ """
+ )
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from __future__ import unicode_literals
+from markdown.test_tools import TestCase
+
+
+class TestLegacyAtrributes(TestCase):
+
+ maxDiff = None
+
+ def testLegacyAttrs(self):
+ self.assertMarkdownRenders(
+ self.dedent("""
+ # Header {@id=inthebeginning}
+
+ Now, let's try something *inline{@class=special}*, to see if it works
+
+ @id=TABLE.OF.CONTENTS}
+
+
+ * {@id=TABLEOFCONTENTS}
+
+
+ Or in the middle of the text {@id=TABLEOFCONTENTS}
+
+ {@id=tableofcontents}
+
+ [](http://fourthought.com/)
+
+ ![img{@id=foo}][img]
+
+ [img]: http://example.com/i.jpg
+ """),
+ self.dedent("""
+ <h1 id="inthebeginning">Header </h1>
+ <p>Now, let's try something <em class="special">inline</em>, to see if it works</p>
+ <p>@id=TABLE.OF.CONTENTS}</p>
+ <ul>
+ <li id="TABLEOFCONTENTS"></li>
+ </ul>
+ <p id="TABLEOFCONTENTS">Or in the middle of the text </p>
+ <p id="tableofcontents"></p>
+ <p><a href="http://fourthought.com/"><img alt="" src="http://fourthought.com/images/ftlogo.png" style="float: left; margin: 10px; border: none;" title="Fourthought logo" /></a></p>
+ <p><img alt="img" id="foo" src="http://example.com/i.jpg" /></p>
+ """), # noqa: E501
+ extensions=['legacy_attrs']
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from __future__ import unicode_literals
+from markdown.test_tools import TestCase
+
+
+class TestLegacyEm(TestCase):
+ def test_legacy_emphasis(self):
+ self.assertMarkdownRenders(
+ '_connected_words_',
+ '<p><em>connected</em>words_</p>',
+ extensions=['legacy_em']
+ )
+
+ def test_legacy_strong(self):
+ self.assertMarkdownRenders(
+ '__connected__words__',
+ '<p><strong>connected</strong>words__</p>',
+ extensions=['legacy_em']
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from __future__ import unicode_literals
+from markdown.test_tools import TestCase
+
+
+class TestTableBlocks(TestCase):
+
+ def test_empty_cells(self):
+ """Empty cells (nbsp)."""
+
+ text = """
+ | Second Header
+------------- | -------------
+ | Content Cell
+Content Cell |
+"""
+
+ self.assertMarkdownRenders(
+ text,
+ self.dedent(
+ """
+ <table>
+ <thead>
+ <tr>
+ <th> </th>
+ <th>Second Header</th>
+ </tr>
+ </thead>
+ <tbody>
+ <tr>
+ <td> </td>
+ <td>Content Cell</td>
+ </tr>
+ <tr>
+ <td>Content Cell</td>
+ <td> </td>
+ </tr>
+ </tbody>
+ </table>
+ """
+ ),
+ extensions=['tables']
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestAdvancedImages(TestCase):
+
+ def test_nested_square_brackets(self):
+ self.assertMarkdownRenders(
+ """![Text[[[[[[[]]]]]]][]](http://link.com/image.png) more text""",
+ """<p><img alt="Text[[[[[[[]]]]]]][]" src="http://link.com/image.png" /> more text</p>"""
+ )
+
+ def test_nested_round_brackets(self):
+ self.assertMarkdownRenders(
+ """))))))()).png) more text""",
+ """<p><img alt="Text" src="http://link.com/(((((((()))))))()).png" /> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles1(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/(.png" title="title" /> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles2(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/('.png" title="title" /> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles3(self):
+ self.assertMarkdownRenders(
+ """") more text""",
+ """<p><img alt="Text" src="http://link.com/(.png" title="title)" /> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles4(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/(.png" title="title" /> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles5(self):
+ self.assertMarkdownRenders(
+ """") more text""",
+ """<p><img alt="Text" src="http://link.com/(.png" title="title)" /> more text</p>"""
+ )
+
+ def test_mixed_title_quotes1(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/'.png" title="title" /> more text</p>"""
+ )
+
+ def test_mixed_title_quotes2(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/".png" title="title" /> more text</p>"""
+ )
+
+ def test_mixed_title_quotes3(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/with spaces.png" title=""and quotes" 'and title" />"""
+ """ more text</p>"""
+ )
+
+ def test_mixed_title_quotes4(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/with spaces'.png" title="and quotes" 'and title" />"""
+ """ more text</p>"""
+ )
+
+ def test_mixed_title_quotes5(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/with spaces .png" title=""and quotes""""
+ """ 'and title" /> more text</p>"""
+ )
+
+ def test_mixed_title_quotes6(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/with spaces "and quotes".png" title="and title" />"""
+ """ more text</p>"""
+ )
+
+ def test_single_quote(self):
+ self.assertMarkdownRenders(
+ """""",
+ """<p><img alt="test" src="link"notitle.png" /></p>"""
+ )
+
+ def test_angle_with_mixed_title_quotes(self):
+ self.assertMarkdownRenders(
+ """ more text""",
+ """<p><img alt="Text" src="http://link.com/with spaces '"and quotes".png" title="and title" />"""
+ """ more text</p>"""
+ )
+
+ def test_misc(self):
+ self.assertMarkdownRenders(
+ """""",
+ """<p><img alt="Poster" src="http://humane_man.jpg" title="The most humane man." /></p>"""
+ )
+
+ def test_misc_ref(self):
+ self.assertMarkdownRenders(
+ self.dedent(
+ """
+ ![Poster][]
+
+ [Poster]:http://humane_man.jpg "The most humane man."
+ """
+ ),
+ self.dedent(
+ """
+ <p><img alt="Poster" src="http://humane_man.jpg" title="The most humane man." /></p>
+ """
+ )
+ )
+
+ def test_misc_blank(self):
+ self.assertMarkdownRenders(
+ """![Blank]()""",
+ """<p><img alt="Blank" src="" /></p>"""
+ )
+
+ def test_misc_img_title(self):
+ self.assertMarkdownRenders(
+ """""",
+ """<p><img alt="Image" src="http://humane man.jpg" title="The most humane man." /></p>"""
+ )
+
+ def test_misc_img(self):
+ self.assertMarkdownRenders(
+ """""",
+ """<p><img alt="Image" src="http://humane man.jpg" /></p>"""
+ )
--- /dev/null
+# -*- coding: utf-8 -*-
+"""
+Python Markdown
+
+A Python implementation of John Gruber's Markdown.
+
+Documentation: https://python-markdown.github.io/
+GitHub: https://github.com/Python-Markdown/markdown/
+PyPI: https://pypi.org/project/Markdown/
+
+Started by Manfred Stienstra (http://www.dwerg.net/).
+Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
+Currently maintained by Waylan Limberg (https://github.com/waylan),
+Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
+
+Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
+Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
+Copyright 2004 Manfred Stienstra (the original version)
+
+License: BSD (see LICENSE.md for details).
+"""
+
+from markdown.test_tools import TestCase
+
+
+class TestAdvancedLinks(TestCase):
+
+ def test_nested_square_brackets(self):
+ self.assertMarkdownRenders(
+ """[Text[[[[[[[]]]]]]][]](http://link.com) more text""",
+ """<p><a href="http://link.com">Text[[[[[[[]]]]]]][]</a> more text</p>"""
+ )
+
+ def test_nested_round_brackets(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/(((((((()))))))())) more text""",
+ """<p><a href="http://link.com/(((((((()))))))())">Text</a> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles1(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/("title") more text""",
+ """<p><a href="http://link.com/(" title="title">Text</a> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles2(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/('"title") more text""",
+ """<p><a href="http://link.com/('" title="title">Text</a> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles3(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/("title)") more text""",
+ """<p><a href="http://link.com/(" title="title)">Text</a> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles4(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/( "title") more text""",
+ """<p><a href="http://link.com/(" title="title">Text</a> more text</p>"""
+ )
+
+ def test_uneven_brackets_with_titles5(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/( "title)") more text""",
+ """<p><a href="http://link.com/(" title="title)">Text</a> more text</p>"""
+ )
+
+ def test_mixed_title_quotes1(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/'"title") more text""",
+ """<p><a href="http://link.com/'" title="title">Text</a> more text</p>"""
+ )
+
+ def test_mixed_title_quotes2(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/"'title') more text""",
+ """<p><a href="http://link.com/"" title="title">Text</a> more text</p>"""
+ )
+
+ def test_mixed_title_quotes3(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/with spaces'"and quotes" 'and title') more text""",
+ """<p><a href="http://link.com/with spaces" title=""and quotes" 'and title">"""
+ """Text</a> more text</p>"""
+ )
+
+ def test_mixed_title_quotes4(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/with spaces'"and quotes" 'and title") more text""",
+ """<p><a href="http://link.com/with spaces'" title="and quotes" 'and title">Text</a> more text</p>"""
+ )
+
+ def test_mixed_title_quotes5(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/with spaces '"and quotes" 'and title') more text""",
+ """<p><a href="http://link.com/with spaces" title=""and quotes" 'and title">"""
+ """Text</a> more text</p>"""
+ )
+
+ def test_mixed_title_quotes6(self):
+ self.assertMarkdownRenders(
+ """[Text](http://link.com/with spaces "and quotes" 'and title') more text""",
+ """<p><a href="http://link.com/with spaces "and quotes"" title="and title">"""
+ """Text</a> more text</p>"""
+ )
+
+ def test_single_quote(self):
+ self.assertMarkdownRenders(
+ """[test](link"notitle)""",
+ """<p><a href="link"notitle">test</a></p>"""
+ )
+
+ def test_angle_with_mixed_title_quotes(self):
+ self.assertMarkdownRenders(
+ """[Text](<http://link.com/with spaces '"and quotes"> 'and title') more text""",
+ """<p><a href="http://link.com/with spaces '"and quotes"" title="and title">"""
+ """Text</a> more text</p>"""
+ )
+
+ def test_amp_in_url(self):
+ """Test amp in URLs."""
+
+ self.assertMarkdownRenders(
+ '[link](http://www.freewisdom.org/this&that)',
+ '<p><a href="http://www.freewisdom.org/this&that">link</a></p>'
+ )
+ self.assertMarkdownRenders(
+ '[title](http://example.com/?a=1&b=2)',
+ '<p><a href="http://example.com/?a=1&b=2">title</a></p>'
+ )
[tox]
-envlist = py27, py32, py33, py34, pypy, flake8, checkspelling
+envlist = py27, py34, py35, py36, py37, pypy, pypy3, flake8, checkspelling
[testenv]
-downloadcache = {toxworkdir}/cache
deps = -rtest-requirements.txt
-commands = coverage run --source=markdown {toxinidir}/run-tests.py {posargs}
+commands = coverage run --source=markdown -m unittest discover {toxinidir}/tests
coverage report --show-missing
[testenv:flake8]
deps = flake8
-commands = flake8 {toxinidir}/markdown {toxinidir}/tests {toxinidir}/setup.py {toxinidir}/run-tests.py
+commands = flake8 {toxinidir}/markdown {toxinidir}/tests {toxinidir}/setup.py
[testenv:checkspelling]
deps =
+ mkdocs
+ mkdocs_nature
commands = {toxinidir}/checkspelling.sh
[flake8]
-max-line-length = 119
\ No newline at end of file
+max-line-length = 119