Imported Upstream version 2.6
authorHyunjee Kim <hj0426.kim@samsung.com>
Tue, 31 Mar 2020 04:13:28 +0000 (13:13 +0900)
committerHyunjee Kim <hj0426.kim@samsung.com>
Tue, 31 Mar 2020 04:13:36 +0000 (13:13 +0900)
Change-Id: I8dbe940c230a83a5d8bacd421feb83707852ae1b
Signed-off-by: Hyunjee Kim <hj0426.kim@samsung.com>
77 files changed:
MANIFEST
PKG-INFO
docs/authors.txt
docs/change_log.txt
docs/cli.txt
docs/extensions/admonition.txt
docs/extensions/api.txt
docs/extensions/attr_list.txt
docs/extensions/code_hilite.txt
docs/extensions/extra.txt
docs/extensions/fenced_code_blocks.txt
docs/extensions/footnotes.txt
docs/extensions/header_id.txt
docs/extensions/meta_data.txt
docs/extensions/nl2br.txt
docs/extensions/smarty.txt
docs/extensions/toc.txt
docs/extensions/wikilinks.txt
docs/install.txt
docs/reference.txt
docs/release-2.0.1.txt
docs/release-2.0.txt
docs/release-2.1.0.txt
docs/release-2.2.0.txt
docs/release-2.3.txt
docs/release-2.4.txt
docs/release-2.5.txt
docs/release-2.6.txt [new file with mode: 0644]
docs/siteindex.txt
docs/test_suite.txt
makefile
markdown/__init__.py
markdown/__main__.py
markdown/__version__.py
markdown/blockparser.py
markdown/blockprocessors.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/headerid.py
markdown/extensions/meta.py
markdown/extensions/nl2br.py
markdown/extensions/sane_lists.py
markdown/extensions/smart_strong.py
markdown/extensions/smarty.py
markdown/extensions/tables.py
markdown/extensions/toc.py
markdown/extensions/wikilinks.py
markdown/inlinepatterns.py
markdown/odict.py
markdown/postprocessors.py
markdown/preprocessors.py
markdown/serializers.py
markdown/treeprocessors.py
markdown/util.py
run-tests.py
setup.py
tests/__init__.py
tests/extensions/extra/raw-html.html
tests/extensions/extra/raw-html.txt
tests/extensions/test.cfg
tests/extensions/toc_nested_list.html [new file with mode: 0644]
tests/extensions/toc_nested_list.txt [new file with mode: 0644]
tests/misc/em_strong_complex.html [new file with mode: 0644]
tests/misc/em_strong_complex.txt [new file with mode: 0644]
tests/misc/nested-patterns.html
tests/misc/nested-patterns.txt
tests/plugins.py
tests/test_apis.py
tests/test_extensions.py
tox.ini

index 78195a6787d09855d4f7d277eefc79e30c4210c9..3be3df0e737e5e108c5145ea9303a77295f71e26 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -29,6 +29,7 @@ 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
@@ -151,6 +152,8 @@ 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
@@ -238,6 +241,8 @@ 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
index 46c659d725b2592b8355ae91166ac455ee4a51cf..0cedb015d4dd6e4200080c1ca30f969f2d20634e 100644 (file)
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,16 +1,17 @@
-Metadata-Version: 1.0
+Metadata-Version: 1.1
 Name: Markdown
-Version: 2.5
+Version: 2.6
 Summary: Python implementation of Markdown.
 Home-page: https://pythonhosted.org/Markdown/
 Author: Waylan Limberg
-Author-email: waylan [at] gmail.com
+Author-email: waylan.limberg [at] icloud.com
 License: BSD License
-Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-2.5.tar.gz
-Description: This is a Python implementation of John Gruber's Markdown_. 
+Download-URL: http://pypi.python.org/packages/source/M/Markdown/Markdown-2.6.tar.gz
+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 
+        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/
index 53eaaba11dbec61304ca2a712911a4e690506bb2..84c69d001233b80f205f40b0368a57b5829a7349 100644 (file)
@@ -9,15 +9,15 @@ Primary Authors
 
 [Yuri Takteyev](http://freewisdom.org/)
 
-: Yuri has written much of the current code while procrastingating his Ph.D.
+: 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 mmany of the available extensions and later was asked to join Yuri, 
-  where he began fixing nummrious bugs, adding documentation and making general 
-  improvements to the existing codebase.
+  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
 
@@ -28,7 +28,7 @@ Artem Yunusov
 [Manfed Stienstra](http://www.dwerg.net/)
 
 : Manfed wrote the original version of the script and is responsible for 
-  various parts of the existing codebase.
+  various parts of the existing code base.
 
 David Wolever
 
@@ -40,7 +40,7 @@ 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 progect in any way.
+who has contributed to the project in any way.
 
 * Eric Abrahamsen
 * Jeff Balogh
index 59f625de4f06a0c3a0af4963fcdd59e5123dab63..7001d68bed0b25c804aee687218958d5b7568fe2 100644 (file)
@@ -1,17 +1,23 @@
 title:      Change Log
 prev_title: Test Suite
 prev_url:   test_suite.html
-next_title: Release Notes for v2.5
-next_url:   release-2.5.html
+next_title: Release Notes for v2.6
+next_url:   release-2.6.html
 
-Python-Markdown Changelog
+Python-Markdown Change Log
 =========================
 
-[TBD]: Released version 2.5.0 ([Notes](release-2.5.html))
+Feb 19, 2015: Released version 2.6 ([Notes](release-2.6.html)).
 
-Feb 16, 2014: Released version 2.4.0 ([Notes](release-2.4.html))
+Nov 19, 2014: Released version 2.5.2 (a bug-fix release).
 
-Mar 22, 2013: Released version 2.3.1 (a bugfix 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))
 
@@ -37,24 +43,24 @@ 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 Definistion List ext. Replaced old core with BlockProcessors.
+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 tarversing. Added treap implementation, then replaced with
-OrderedDEict. Renamed various processors to better reflect what they actually
-do. Refactored footnote ext to match php Extra's output.
+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)
+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' dir
+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
-seperate commanline script. (v2.0-alpha)
+separate command line script. (v2.0-alpha)
 
 July 2008: Switched from home-grown NanoDOM to ElementTree and
 various related bugs (thanks Artem Yunusov).
@@ -63,24 +69,24 @@ 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 repo to git from svn.
+distribution and other minor changes. Moved repository to git from svn.
 
-Mar 2008: Refactored extension api to accept either an
+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 bugfixes mostly regarding extensions.
+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 pre/post proccessors.
+and inheritance for Preprocessors/Postprocessors.
 
-Feb 9, 2008: Doublequotes no longer html escaped and rawhtml
+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 arg from Markdown
+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
@@ -90,39 +96,39 @@ 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
+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 regex for inline links.
+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 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 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 inlinePatterns. Added a 'safe' tag
-to htmlStash.
+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).
+comments.  Added "safe mode" (user's HTML tags are removed).
 
-Sept 6, 2006: Added exception for PHP tags when handling html blocks.
+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.
+with ampersand normalization and HTML blocks.
 
-July 10, 2006: Switched to using optparse.  Added proper support for
-unicode.
+July 10, 2006: Switched to using `optparse`.  Added proper support for
+Unicode.
 
 July 9, 2006: Fixed the `<!--@address.com>` problem (Tracker #1501354).
 
@@ -130,25 +136,25 @@ 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
+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)
+`_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 midword underlining.
+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 "mailto"
+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)
+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.)
@@ -159,14 +165,14 @@ 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 autolink fix by Tiago
+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
+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
+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
@@ -187,8 +193,8 @@ 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, autolinks, underscore emphasis. Cleaned up and refactored the
+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
@@ -197,7 +203,7 @@ 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
+Nov. 2004: Added links, blockquotes, HTML blocks to Manfred
 Stienstra's code
 
 Apr. 2004: Manfred's version at <http://www.dwerg.net/projects/markdown/>
index 5098771cd57461839fb4c4eee66f6d71dd4b49c6..c2b37e4a92ed61ce4242a736d5e5aded7777a4d6 100644 (file)
@@ -23,18 +23,18 @@ following format:
 
     $ python -m markdown [options] [args]
 
-That will run the module as a script with the options and args provided. 
+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. 
+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 args:
+Use the `--help` option for a list all available options and arguments:
 
     $ python -m markdown --help
 
@@ -44,7 +44,7 @@ follow the instructions below to use a wrapper script:
 Setup
 -----
 
-Upon installation, the ``markdown_py`` script will have been copied to
+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.
@@ -52,11 +52,11 @@ path.
 * **Windows**:
 
     Assuming a default install of Python on Windows, your "Scripts" directory 
-    is most likely something like ``C:\\Python26\Scripts``. Verify the location
+    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.
+    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.):
 
@@ -64,33 +64,33 @@ path.
     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.
+      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 isn't being
+    * 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
+    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.
+    more convenient to have `markdown_py` on your path.
 
 !!!Note 
-    Python-Markdown uses "markdown_py" as a script name because
+    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
+    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 
+To use `markdown_py` from the command line, run it as 
 
     $ markdown_py input_file.txt
 
@@ -133,15 +133,15 @@ above example might look like this:
                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 configs for that
+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 config files if [PyYaml] is
+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 config file is automatically detected.
+of your configuration file is automatically detected.
 
 !!!warning
-       The previously documented method of appending the extension configs as a string to the 
+       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.
 
index fcf866b9e1e5d17cacf2e122aa3264a04fadebe8..d68bd5de12b5788822a32306f8f943e434705267 100644 (file)
@@ -26,7 +26,7 @@ Admonitions are created using the following syntax:
 
         This is the second paragraph.
 
-`type` will be used as the CSS classname and as default title. It must be a
+`type` will be used as the CSS class name and as default title. It must be a
 single word. So, for instance:
 
     !!! note
index 8837818cd4a5ddbc6e74e23e70207c0127823955..66daa8685747cf35da34c2d2388cc6999341ac54 100644 (file)
@@ -8,14 +8,14 @@ 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
+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
+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.
+there are also Blockprocessors which are part of the core BlockParser.
 
-As the parser builds an [ElementTree][] object which is later rendered 
+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 
@@ -29,10 +29,10 @@ 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 
+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 
+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:
@@ -54,27 +54,27 @@ 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 
+`*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()``**: 
+* **`getCompiledRegExp()`**: 
 
     Returns a compiled regular expression.
 
-* **``handleMatch(m)``**: 
+* **`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 
+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
+match will be `m.group(2)` as `m.group(1)` will match everything before the
 pattern.
 
 For an example, consider this simplified emphasis pattern:
@@ -103,23 +103,24 @@ 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`.
+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)``**:
+* **`SimpleTextPattern(pattern)`**:
 
-    Returns simple text of ``group(2)`` of a ``pattern``.
+    Returns simple text of `group(2)` of a `pattern`.
 
-* **``SimpleTagPattern(pattern, tag)``**:
+* **`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').
+    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)``**:
+* **`SubstituteTagPattern(pattern, tag)`**:
 
-    Returns an element of type "`tag`" with no children or text (i.e.: 'br').
+    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 
@@ -129,14 +130,15 @@ situation.
 Treeprocessors {: #treeprocessors }
 -----------------------------------
 
-Treeprocessors manipulate an ElemenTree object after it has passed through the
+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 InlinePatterns on the text of each Element in the tree.
+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 Elementree 
-object) and returns either that root element or a modified root element.
+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:
 
@@ -144,11 +146,21 @@ A pseudo example:
 
     class MyTreeprocessor(Treeprocessor):
         def run(self, root):
-            #do stuff
-            return my_modified_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][] below.
+[Working with the ElementTree][workingwithetree] below.
 
 Postprocessors {: #postprocessors }
 -----------------------------------
@@ -157,8 +169,8 @@ 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 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 
@@ -174,127 +186,128 @@ contents to a document:
 BlockParser {: #blockparser }
 -----------------------------
 
-Sometimes, pre/tree/postprocessors and Inline Patterns aren't 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 Blockproccessors. 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
+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.
+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 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:
+The **`test`** method takes two arguments:
 
-* **``parent``**: The parent etree Element of the block. This can be useful as
-  the block may need to be treated differently if it is inside a list, for
+* **`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 
+* **`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:
+The **`run`** method takes two arguments:
 
-* **``parent``**: A pointer to the parent etree Element of the block. The run 
+* **`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 Elementree object is altered in place.
+  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 
+* **`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.
+  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
+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:
+Each Blockprocessor has the following utility methods available:
 
-* **``lastChild(parent)``**: 
+* **`lastChild(parent)`**: 
 
-    Returns the last child of the given etree Element or ``None`` if it had no 
-    children.
+    Returns the last child of the given ElementTree Element or `None` if it
+    had no children.
 
-* **``detab(text)``**: 
+* **`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)``**: 
+* **`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.
+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:
+**`State`** is a subclass of `list` and has the additional methods:
 
-* **``set(state)``**: 
+* **`set(state)`**: 
 
-    Set a new state to string ``state``. The new state is appended to the end 
+    Set a new state to string `state`. The new state is appended to the end 
     of the stack.
 
-* **``reset()``**: 
+* **`reset()`**: 
 
     Step back one step in the stack. The last state at the end is removed from 
     the stack.
 
-* **``isstate(state)``**: 
+* **`isstate(state)`**: 
 
     Test that the top (current) level of the stack is of the given string 
-    ``state``.
+    `state`.
 
-Note that to ensure that the state stack doesn't become corrupted, each time a
+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:
+An instance of the **`BlockParser`** is found at `Markdown.parser`.
+`BlockParser` has the following methods:
 
-* **``parseDocument(lines)``**: 
+* **`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 
+    passed an entire document and is the only method the `Markdown` class 
     calls directly.
 
-* **``parseChunk(parent, text)``**: 
+* **`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 
+    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)``**: 
+* **`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 
+    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.
+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.
 
@@ -302,34 +315,34 @@ Working with the ElementTree {: #working_with_et }
 --------------------------------------------------
 
 As mentioned, the Markdown parser converts a source document to an 
-[ElementTree][] object before serializing that back to Unicode text. 
+[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 
+`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.
+`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 (``Elementree``). In each instance, ``cElementTree`` is 
-tried first, then ``ElementTree`` if the faster C implementation is not 
+`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 
-[InlinePatterns][]. In such a situation, simply insert the text as you normally
-would and the text will be automatically run through the InlinePatterns. 
-However, if you do *not* want some text to be parsed by InlinePatterns,
-then insert the text as an ``AtomicString``.
+[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 InlinePatterns latter):
+the second cell (`td2`) will be run through Inline Patterns latter):
 
     table = etree.Element("table") 
     table.set("cellpadding", "2")                      # Set cellpadding to 2
@@ -341,7 +354,7 @@ the second cell (``td2``) will be run through InlinePatterns latter):
     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:
+adds a `class` attribute to `<a>` elements:
 
        def set_link_class(self, element):
                for child in element: 
@@ -358,53 +371,53 @@ 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 
+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. 
+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 
+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 }
+### `extendMarkdown` {: #extendmarkdown }
 
-The ``extendMarkdown`` method of a ``markdown.extensions.Extension`` class 
+The `extendMarkdown` method of a `markdown.extensions.Extension` class 
 accepts two arguments:
 
-* **``md``**:
+* **`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``
+    * `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.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``**:
+* **`md_globals`**:
 
     Contains all the various global variables within the markdown module.
 
@@ -436,32 +449,32 @@ 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.
+`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. 
+helper method `add()` to add additional items to an existing OrderedDict. 
 
-The ``add()`` method accepts three arguments:
+The `add()` method accepts three arguments:
 
-* **``key``**: A string. The key is used for later reference to the item.
+* **`key`**: A string. The key is used for later reference to the item.
 
-* **``value``**: The object instance stored in this item.
+* **`value`**: The object instance stored in this item.
 
-* **``location``**: Optional. The items location in relation to other items. 
+* **`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 
+    * 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 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. 
+    * A greater-than sign (`>`) followed by an existing key (i.e.: 
+      `">somekey"`) inserts that item after the existing key. 
 
 Consider the following example:
 
@@ -474,8 +487,8 @@ Consider the following example:
     [("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 
+`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'
@@ -484,16 +497,16 @@ insert another item into the OrderedDict.
 
 Now let's insert another item.
 
-    >>> od.add('twohalf', 2.5, '<three') # Insert before 'three'
+    >>> od.add('two-point-five', 2.5, '<three') # Insert before 'three'
     >>> od.keys()
-    ["one", "two", "twohalf", "three", "four"]
+    ["one", "two", "two-point-five", "three", "four"]
 
-Note that we also could have set the location of "twohalf" to be 'after two'
-(i.e.: ``'>two'``). However, it's unlikely that you will have control over the 
+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 
-'twohalf' in the above examples was loaded before a separate  extension which 
-adds 'two'. You may need to take this into consideration when adding your 
+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:
@@ -523,32 +536,32 @@ extension:
     md.reset()
     html2 = md.convert(text_without_footnote)
 
-Without calling ``reset``, the footnote definitions from the first document will
+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
+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:
+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`
+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
+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.
+`reset` method.
 
-### Config Settings {: #configsettings }
+### 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:
+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):
@@ -556,7 +569,7 @@ those parameters should be stored in ``self.config`` of your
                            'option2' : ['value2', 'description2'] }
             super(MyExtension, self).__init__(**kwargs)
 
-When implemented this way the config parameters can be over-ridden at 
+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'])
@@ -565,38 +578,38 @@ 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 config settings:
+following methods available to assist in working with configuration settings:
 
-* **``getConfig(key [, default])``**: 
+* **`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()``**:
+* **`getConfigs()`**:
 
        Returns a dict of all key/value pairs.
 
-* **``getConfigInfo()``**:
+* **`getConfigInfo()`**:
 
-       Returns all config descriptions as a list of tuples.
+       Returns all configuration descriptions as a list of tuples.
 
-* **``setConfig(key, value)``**:
+* **`setConfig(key, value)`**:
        
-       Sets a config setting for `key` with the given `value`. If `key` is
+       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
+       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
+       a Boolean value except when it is `None`. No conversion takes place
        when the previous value of `key` is a string.
 
-* **``setConfigs(items)``**:
+* **`setConfigs(items)`**:
 
-       Sets multiple config settings given a dict of key/value pairs.
+       Sets multiple configuration settings given a dict of key/value pairs.
 
-### makeExtension {: #makeextension }
+### `makeExtension` {: #makeextension }
 
 As noted in the [library reference] an instance of an extension can be passed
-directly to Markdown. In fact, this is the prefered way to use third-party 
+directly to Markdown. In fact, this is the preferred way to use third-party 
 extensions.
 
 For example:
@@ -606,7 +619,7 @@ For example:
     myext = myextension.MyExtension(option='value')
     md = markdown.Markdown(extensions=[myext])
 
-Markdown also accepts "named" third party extensions for those occassions
+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). 
 
@@ -616,7 +629,7 @@ 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 seperated by a colon.
+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
@@ -625,8 +638,8 @@ 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.
+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 
@@ -647,11 +660,11 @@ the module and call the `makeExtension` function to initiate your extension.
 
 
 [Preprocessors]: #preprocessors
-[InlinePatterns]: #inlinepatterns
+[Inline Patterns]: #inlinepatterns
 [Treeprocessors]: #treeprocessors
 [Postprocessors]: #postprocessors
 [BlockParser]: #blockparser
-[Working with the ElementTree]: #working_with_et
+[workingwithetree]: #working_with_et
 [Integrating your code into Markdown]: #integrating_into_markdown
 [extendMarkdown]: #extendmarkdown
 [OrderedDict]: #ordereddict
index 01329c66cd89e0fbecc505e9eed05c391ab1a0ed..195134731a9dc941cf098cc1e9fee5fad953a597 100644 (file)
@@ -71,7 +71,7 @@ The above results in the following output:
 ### Inline ###
 
 To define attributes on inline elements, the attribute list should be defined 
-immediately after the inline element with no whitespace.
+immediately after the inline element with no white space.
 
     [link](http://example.com){: class="foo bar" title="Some title!" }
 
index c775df73509ccdd44beb9e74ab2e80899e3476c5..5e802b82283440ddd1f2ad0ca523f5864d1b267c 100644 (file)
@@ -25,23 +25,23 @@ You will also need to [download][dl] and install the Pygments package on your
 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 display as un-highlighted code.
+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
+    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.
+    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 hiliter needs to know what language to use for
-the code block. There are three ways to tell the hiliter what language the code
-block contains and each one has a different result.
+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
@@ -52,9 +52,9 @@ block contains and each one has a different result.
 
 [syntax]: http://daringfireball.net/projects/markdown/syntax#precode
 
-### SheBang (with path) ###
+### Shebang (with path) ###
 
-If the first line of the codeblock contains a shebang, the language is derived
+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
@@ -65,7 +65,7 @@ Will result in:
     #!/usr/bin/python
     # Code goes here ...
 
-### SheBang (no path) ###
+### 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
@@ -92,7 +92,7 @@ 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. 
+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"
@@ -143,7 +143,7 @@ The following options are provided to configure the output:
     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
+    Using `False` will turn off all line numbers, even when using shebangs
     (`#!`) for language identification.
 
 * **`guess_lang`**:
@@ -157,7 +157,7 @@ The following options are provided to configure the output:
     `codehilite`.
 
 * **`pygments_style`**:
-    Pygments HTML Formatter Style (ColorScheme). Defaults to `default`.
+    Pygments HTML Formatter Style (`ColorScheme`). Defaults to `default`.
 
     !!! Note
         This is useful only when `noclasses` is set to `True`, otherwise the
@@ -165,3 +165,12 @@ The following options are provided to configure the output:
 
 * **`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
index bf4d12f880a2a8ddff1dcf288895dff1e80791d7..5347d81d1beee6dbbddb14d1800224a612b705ad 100644 (file)
@@ -1,7 +1,7 @@
 title:      Extra Extension
 prev_title: Extensions
 prev_url:   index.html
-next_title: Abreviations Extension
+next_title: Abbreviations Extension
 next_url:   abbreviations.html
 
 Python-Markdown Extra
@@ -45,11 +45,19 @@ your own clone of Extra under a different name
 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.
+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.
+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 overrident by a value of `span`, *block parsing will be skipped* regardless of tag. If the default is overriden by a value of `block`, *block parsing will occur* regardless of tag.
+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:
 ```
@@ -67,8 +75,9 @@ This is *true* markdown text.
 </div>
 ```
 
-### Nested Markdown Inside HTML BLocks
-Nested elements are more sensitive and must be used cautiously. To avoid unexpected results:
+### 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.
@@ -104,12 +113,12 @@ This `p` block *is* foolishly wrapped in further paragraph tags.
 The tail of the `BlockModeOverride` subelement.
 
 <div name="RawHtml">
-Raw html blocks may also be nested.
+Raw HTML blocks may also be nested.
 </div>
 
 </div>
 
-This text is after the markdown in html.
+This text is after the markdown in HTML.
 ```
 #### Result:
 ```
@@ -130,9 +139,9 @@ Note: Subelements are not required to have tail text.</div>
 </p>
 <p>The tail of the <code>BlockModeOverride</code> subelement.</p>
 <div name="RawHtml">
-Raw html blocks may also be nested.
+Raw HTML blocks may also be nested.
 </div>
 
 </div>
-<p>This text is after the markdown in html.</p>
+<p>This text is after the markdown in HTML.</p>
 ```
index 19999fd25060710b61218dc80dc9676a6ed7dbcb..982f5d4f33dadbc741f2139668c323539036fb46 100644 (file)
@@ -45,7 +45,7 @@ part of the list.
 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 
+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:
 
@@ -65,13 +65,13 @@ The above will output:
     <pre><code class="html">&lt;p&gt;HTML Document&lt;/p&gt;
     </code></pre>
 
-[Github][]'s backtick (`\``) syntax is also supported:
+[GitHub][]'s backtick (`\``) syntax is also supported:
 
     ```python
     # more python code
     ```
 
-[Github]: http://github.github.com/github-flavored-markdown/
+[GitHub]: http://github.github.com/github-flavored-markdown/
 
 ### Emphasized Lines ###
 
index 40081e5b5f435b877c0fbbe3c1dd44e0d5a3cd29..e9b0451f8be34b9719f916ad6ce4a82fe54bf57d 100644 (file)
@@ -38,7 +38,7 @@ 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 whitespace). The content would then 
+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 
index 2881c502358f5eb9487beba7c893256ad861960a..5557b1b0fdf7fe8af60f22fcd04a060fc2291675 100644 (file)
@@ -15,6 +15,13 @@ 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
 ------
 
@@ -48,14 +55,14 @@ The following options are provided to configure the output:
     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 
+    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
+        >>> from markdown.extensions.headerid import HeaderIdExtension
         >>> html = markdown.markdown(text, extensions=[HeaderIdExtension(level=3)])
         >>> print html
         <h3 id="some_header">Some Header</h3>
@@ -72,13 +79,14 @@ The following options are provided to configure the output:
         >>> text = '''
         ... # Some Header
         ... # Header with ID # { #foo }'''
-        >>> html = markdown.markdown(text, 
-                        extensions=['attr_list', HeaderIdExtension(forceid=False)])
+        >>> 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 whitespace in id.
+* **`separator`**: Word separator. Character which replaces white space in id.
 
     Default: `-`
 
index 406755b3f933956e31355abc27e4356144f4ceb7..3057bfacac0229ef5e7d10f26657a304baf3a8c7 100644 (file)
@@ -44,9 +44,16 @@ 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. All meta-data is stripped from the 
-document prior to any further processing by Markdown.
+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
 -----
@@ -54,7 +61,15 @@ Usage
 See [Extensions](index.html) for general extension usage, specify `markdown.extensions.meta`
 as the name of the extension.
 
-This extension does not accept any special configuration options.
+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
 -----------------------
@@ -85,8 +100,13 @@ 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 
+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
index 5cc070c1950600db13774527676a5274d1fb04ab..8ba27c8f9bff50ce7b925664993aeaad991cc400 100644 (file)
@@ -4,13 +4,13 @@ prev_url:   meta_data.html
 next_title: Sane Lists Extension
 next_url:   sane_lists.html
 
-NL2BR
-=====
+New-Line-to-Break Extension
+===========================
 
 Summary
 -------
 
-The NL2BR extension will cause newlines to be treated as hard breaks; like
+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/
@@ -23,7 +23,7 @@ Example
     ... Line 1
     ... Line 2
     ... """
-    >>> html = markdown.markdown(text, extensions=['nl2br'])
+    >>> html = markdown.markdown(text, extensions=['markdown.extensions.nl2br'])
     >>> print html
     <p>Line 1<br />
     Line 2</p>
index a0680af257aa7233799da5931d59961444ffe759..d511da1118b17465a1502eb7210b87a15703908e 100644 (file)
@@ -26,7 +26,7 @@ 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 config to get correct quotes for 
+For example, one might use the following configuration to get correct quotes for 
 the German language:
 
     extensionConfigs = {
@@ -41,8 +41,8 @@ the German language:
     }
 
 !!! note
-    This extension reimplements the Python [SmartyPants] 
-    library by intregated it into the markdown parser.
+    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 
index 7b5d11906e36d2882b4f19eb72faa1347c133914..2cd1fa15f51706a96896ec67be372e8ddd0cc2d5 100644 (file)
@@ -18,6 +18,20 @@ 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:
@@ -41,6 +55,14 @@ would generate the following output:
     <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
 -----
 
@@ -53,36 +75,53 @@ 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]`.
-
-    If a `marker` is not found in the document, then the Table of Contents is
-    available as an attribute of the Markdown class. This allows one to insert
-    the Table of Contents elsewhere in their page template. For example:
-
-        >>> text = '''
-        # Header 1
-        
-        ## Header 2
-        '''
-        >>> md = markdown.Markdown(extensions=['markdown.extensions.toc'])
-        >>> html = md.convert(text)
-        >>> render_some_template(context={'body': html, 'toc': md.toc})
-
-* **`slugify`**:
-    Callable to generate anchors based on header text. Defaults to a built in
-    `slugify` method. The callable must accept one argument which contains the
-    text content of the header and return a string which will be used as the
-    anchor text.
+    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`**:
-    Setting to `True` will cause the headers link to themselves. Default is
-    `False`.
+    Set to `True` to cause all headers to link to themselves. Default is `False`.
 
 * **`permalink`**:
-    Set to `True` to have this extension generate a Sphinx-style permanent links
-    near the headers (for use with Sphinx stylesheets).
+    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 (&para; or "`&para;`") 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
index b52e0d052745520deb947cbcb259e887772aab7b..948b957f8be19883b87b855564284cd0ff52710f 100644 (file)
@@ -25,12 +25,12 @@ number, dashes, underscores and spaces surrounded by double brackets. Therefore
 
     [[Bracketed]]
 
-would produce the following html:
+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
+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
@@ -54,7 +54,7 @@ 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 HTML class `wikilink`.
 
 The following options are provided to change the default behavior:
 
@@ -74,7 +74,7 @@ The following options are provided to change the default behavior:
 
 ### Examples ###
 
-For an example, let us suppose links should always point to the subdirectory
+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
@@ -104,7 +104,7 @@ The option is also provided to change or remove the class attribute.
     ...     extensions=[WikiLinkExtension(html_class='myclass')]
     ... )
 
-Would cause all wikilinks to be assigned to the class `myclass`.
+Would cause all WikiLinks to be assigned to the class `myclass`.
 
     <a href="/WikiLink/" class="myclass">WikiLink</a>
 
index d24e1bae5d6d0bd8f7c013d577064a29224da576..19893986cce9a9681c07944a9fd97868322836bb 100644 (file)
@@ -24,7 +24,7 @@ That's it! You're ready to [use](reference.html) Python-Markdown. Enjoy!
 Installing on Windows {: #windows }
 -----------------------------------
 
-Download the Windows installer (.exe) from 
+Download the Windows installer (`.exe`) from 
 [PyPI](http://pypi.python.org/pypi/Markdown)
 
 Double-click the file and follow the instructions.
@@ -58,7 +58,7 @@ 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
+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:
 
index 2e1985d6817523c3392e537e0efc00e1895724ad..ac5c724967a317f537d5f1001983d0ff4aa15ec2 100644 (file)
@@ -35,7 +35,7 @@ method appropriately ([see below](#convert)).
 
 The following options are available on the `markdown.markdown` function:
 
-* __`text`__{: #text } (required): The source unicode string.
+* __`text`__{: #text } (required): The source Unicode string.
 
     !!! note "Important"
         Python-Markdown expects **Unicode** as input (although
@@ -69,14 +69,14 @@ The following options are available on the `markdown.markdown` function:
         extensions=[MyExtension(), 'path.to.my.ext']
     
     !!! note
-        The prefered method is to pass in an instance of an extension. Strings
-               should only be used when it is impossable to import the Extension Class
+        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:
+    [`extension_configs`](#extension_configs) keyword. For example:
 
         from markdown.extensions import Extension
         class MyExtension(Extension):
@@ -89,8 +89,8 @@ The following options are available on the `markdown.markdown` function:
     supported. Therefore, to import the 'extra' extension, one could do 
     `extensions=['markdown.extensions.extra']`  
 
-       Additionaly, a Class may be specified in the name. The class must be at the end of 
-       the name and be seperated by a colon from the module. 
+       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:
        
@@ -108,7 +108,7 @@ The following options are available on the `markdown.markdown` function:
 
     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.
+    [`extension_configs`](#extension_configs) keyword.
 
     !!! seealso "See Also"
         See the documentation of the [Extension API](extensions/api.html) for 
@@ -122,7 +122,7 @@ The following options are available on the `markdown.markdown` function:
     configuration settings directly to the class when initializing it. 
     
     !!! Note
-        The prefered method is to pass in an instance of an extension, which
+        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.
     
@@ -159,14 +159,14 @@ The following options are available on the `markdown.markdown` function:
     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 
+        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.
+* __`safe_mode`__{: #safe_mode }: Disallow raw HTML.
 
     !!! warning
-        "`safe_mode`" is pending deprecation and should not be used.
+        "`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. 
@@ -178,7 +178,7 @@ The following options are available on the `markdown.markdown` function:
         See the [release notes] for more info.
     
 [Bleach]: https://github.com/jsocol/bleach
-[release notes]: release-2.5.html
+[release notes]: release-2.6.html
     
     The following values are accepted:
 
@@ -215,7 +215,7 @@ The following options are available on the `markdown.markdown` function:
   safe_mode is set to `replace`. Defaults to `[HTML_REMOVED]`.
 
     !!! warning
-        "`html_replacement_text`" is pending deprecation and should not be used.
+        "`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
@@ -277,11 +277,11 @@ Instead, it accepts the following required options:
     * 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.
+  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
+        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.
index c40b471e907aeeaa8fe9830ac77db5bbf2bf08e9..c0b890a916c3d00655c2eb13207a420af4141d70 100644 (file)
@@ -16,7 +16,7 @@ Backwards-incompatible Changes
 ------------------------------
 
 Due to various complications in how Python handles command line scripts in 
-differance systems and with differant installation tools, we were forced to 
-rename the commandline script to ``markdown`` (no ".py"). A matching batch
+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.
+`markdown.py` will need to be altered to call `markdown` instead.
index ec11522226ee489d5532e9937d0e6ecfe6609c64..f44b0c51e57267565537dbf1c3f6bc8414c0b468 100644 (file)
@@ -14,7 +14,7 @@ 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've even 
+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
index 606bad26f5faf40b0c7aecfc566e1cc1c6ef6936..efb3b4af7626008f9c727781bb51302798dad003 100644 (file)
@@ -11,10 +11,10 @@ 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 builtin extensions were added, and HTML5 support was added.
+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 codebase installs on Python 3.1 and 3.2 with no 
+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
@@ -46,7 +46,7 @@ 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 
+* 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. 
@@ -57,9 +57,9 @@ Attribute Lists instead of HeaderId as it did previously.
 in the `markdown` namespace. Extension authors may need to adjust a few
 import statements in their extensions to work with the changes.
 
-* The commandline script name was changed to `markdown_py`. The previous name
+* 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 commandline script
+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.
@@ -69,18 +69,18 @@ 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 
+[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  searializers. That said, `html5` and `xhtml5` have been added as 
-aliases of the `html4` and `xhtml1` searializers respectively.
+new HTML5  serializers. That said, `html5` and `xhtml5` have been added as 
+aliases of the `html4` and `xhtml1` serializers respectively.
 
-An XHTML searializer has been added. Previously, ElementTree's XML searializer 
-was being used for XHTML output. With the new searliazer we are able to avoid
+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.
 
@@ -100,7 +100,7 @@ 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 (licencing permitting).
+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]`. 
@@ -108,13 +108,13 @@ 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 commandline script has been renamed to `markdown_py` which avoids all the 
-various problems we had with previous names.  Also improved the commandline 
-script to accept input on stdin.
+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 builtin extensions and other options available to change the parsing 
+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 
index 59ac78fb90ad5b3fbe60d1c03b5e1434bf6e31ce..c3750fac45e6b884d15aeed86a4555539916fa0e 100644 (file)
@@ -45,7 +45,7 @@ What's New in Python-Markdown 2.1
 \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
-Repo and are generated by the `setup.py build_docs` command.\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
@@ -60,7 +60,7 @@ As long as the provided module contains a compatible extension, the extension
 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 proccessor loop from \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
index d2cef63cf8c5a586f92c16edd86b8488340bd61d..a15e584113133c520ca7c38eae1efb42b5654374 100644 (file)
@@ -26,37 +26,37 @@ longer try to import a third-party installation of ElementTree.
 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 formated links must be
-of a known whitelisted scheme when in "safe_mode" or the url is discarded.
-The whitelisted schemes are: 'http', 'https', 'ftp', 'ftps', 'mailto', and
-'news'. Schemeless urls are also permitted, but are checked in other ways - 
+* "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
+(`:`) 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 outputing XHTML (the default) or HTML4.
+you are outputting XHTML (the default) or HTML4.
 
-* The "force_linenos" config setting of the CodeHilite extension has been
-marked as **Pending Deprecation** and a new setting "linenums" has been added to
+* 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 
+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 recomended), it is 
-archived on [Github](https://gist.github.com/waylan/4773365).
+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 recomended), it is 
-archived on [Github](https://gist.github.com/waylan/5152650). Note that the 
-underlying library, uTidylib, is not Python 3 compatable. Instead, it is 
+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 
-compatability - install from github not PyPI) be used. As the API for that 
+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={...})`).
@@ -68,10 +68,10 @@ 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
+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 
-subdirectory of another project. The various files within the library will 
+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.
 
index af2110b787ffd0788fd0dab98339121a4e5e57a9..c32408cf1fd321b411c2fa67f04bba9952524c95 100644 (file)
@@ -15,12 +15,12 @@ Python-Markdown supports Python versions 2.6, 2.7, 3.1, 3.2, and 3.3.
 Backwards-incompatible Changes
 ------------------------------
 
-* The "force_linenos" config setting of the CodeHilite extension has been
+* 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 
+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 
+`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
@@ -34,7 +34,7 @@ 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 sript. The HTML does not need
+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.
index 207d8762dd8715d829ac19b03afff78a657b0548..0c399ed51ea536ad634cc82818ee9c32b8786a0f 100644 (file)
@@ -1,6 +1,6 @@
 title:      Release Notes for v2.5
-prev_title: Change Log
-prev_url:   change_log.html
+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
 
@@ -16,14 +16,11 @@ 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. While Python-Markdown is no longer tested against
-  Python 2.6, you may be able to get it working if you install a copy of [importlib] 
-  which has been backported for Python 2.6. However, the developers of Python-Markdown 
-  offer no guarentees in that situation.
+  versions 2.7, 3.2, 3.3, or 3.4.
 
 [importlib]: https://pypi.python.org/pypi/importlib
 
-* The `force_linenos` config key on the [CodeHilite Extension] has been **deprecated**
+* 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.
@@ -59,14 +56,14 @@ Backwards-incompatible Changes
 
                html = markdown.markdown(text, extensions=[EscapeHtml()])
 
-       As the HTML would not be parsed with the above Extension, then the searializer will
+       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 previosuly
+  Only keyword arguments should be used. For example, if your code previously
   looked like this:
 
          html = markdown.markdown(text, ['extra'])
@@ -77,14 +74,14 @@ Backwards-incompatible Changes
 
        !!! Note
            This change is being made as a result of deprecating `"safe_mode"` as the 
-               `safe_mode` argumnet was one of the positional arguments. When that argument 
+               `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 recomended that you always use keywords when they are supported
+               position. It is recommended that you always use keywords when they are supported
                for this reason.
 
-* In previous versions of Python-Markdown, the builtin extensions received
-  special status and did not require the full path to be provided. Additionaly,
-  third party extensions whose name started with "mdx_" received the same 
+* 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:
@@ -99,21 +96,21 @@ Backwards-incompatible Changes
 
         $ python -m markdown -x markdown.extensions.extra input.txt
 
-    See the [documentation](reference.html#extensions) for a full explaination
+    See the [documentation](reference.html#extensions) for a full explanation
     of the current behavior.
 
-* The previously documented method of appending the extension configs as 
+* 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 
+  [`extension_configs`](reference.html#extension_configs) keyword should 
   be used instead. See the [documentation](reference.html#extension-configs) 
-  for a full explaination of the current behavior.
+  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 subtitutions to better support
+    added, which allows one to define their own substitutions to better support
     languages other than English. Thanks to [Martin Altmayer] for implementing this 
        feature.
 
@@ -124,10 +121,10 @@ What's New in Python-Markdown 2.5
     `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 
-       submodule). This restriction no longer exists. 
+       sub-module). This restriction no longer exists. 
 
-       Additionaly, 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 seperated by a colon from 
+       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:
@@ -139,37 +136,37 @@ What's New in Python-Markdown 2.5
                "path.to.module:SomeExtensionClass"
        
        This allows multiple extensions to be implemented within the same module and still 
-       accessable when the user isn't able to import the extension directly (perhaps from 
+       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 carfully what is required to
+       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 refactord to make it a little easier 
-    for extension authors to work with config settings. As a result, the 
+*   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 configs and adjust their
+       `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 explaination.
+       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 filename and passes the parsed content of a [YAML] or 
+    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` kerword. Note that 
+       matches the format required by the `extension_configs` keyword. Note that 
        [PyYAML] is required to parse YAML files.
 
 [cli]: cli.html#using-extensions
@@ -177,7 +174,7 @@ What's New in Python-Markdown 2.5
 [JSON]: http://json.org/
 [PyYAML]: http://pyyaml.org/
 
-*   The [amonition extension][ae] is no longer considered "experimental."
+*   The [admonition extension][ae] is no longer considered "experimental."
 
 [ae]: extensions/admonition.html
 
diff --git a/docs/release-2.6.txt b/docs/release-2.6.txt
new file mode 100644 (file)
index 0000000..f79e357
--- /dev/null
@@ -0,0 +1,259 @@
+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.
index 3d1e22e4b8f6a4d1fdfbb2ede0db8ef68e40ca9c..0139bd7c8dae42ccfd934fc4bb4bade1ae0b9064 100644 (file)
@@ -21,9 +21,9 @@ Table of Contents
 * [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)
+        * [`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)
@@ -50,7 +50,7 @@ Table of Contents
     * [Third Party Extensions](extensions/index.html#third-party-extensions)
     * [Extension API](extensions/api.html)
         * [Preprocessors](extensions/api.html#preprocessors)
-        * [InlinePatterns](extensions/api.html#inlinepatterns)
+        * [Inline Patterns](extensions/api.html#inlinepatterns)
         * [Treeprocessors](extensions/api.html#treeprocessors)
         * [Postprocessors](extensions/api.html#postprocessors)
         * [BlockParser](extensions/api.html#blockparser)
@@ -59,13 +59,14 @@ Table of Contents
             * [extendMarkdown](extensions/api.html#extendmarkdown)
             * [OrderedDict](extensions/api.html#ordereddict)
             * [registerExtension](extensions/api.html#registerextension)
-            * [Config Settings](extensions/api.html#configsettings)
+            * [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 Config Settings](test_suite.html#syntax-test-config-settings)
+    * [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)
index 43829e9d2ccd054b3425a0d40e4757dd9e16566e..745ed3b2b89115dc3ba42c019bad1e0ad05fc8a0 100644 (file)
@@ -16,14 +16,14 @@ 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 config options can be passed in on the command
+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
+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.
@@ -34,11 +34,11 @@ 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 txt
-and html files. The txt file contains a snippet of Markdown source text
-formated for a specific syntax feature and the html file contains the expected
-HTML output of that snippet. When the test suite is run, each txt file is run
-through Markdown and the output is compared with the html file as a separate
+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
@@ -80,7 +80,7 @@ insignificant white space differences:
 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 txt file which failed and a unified diff showing
+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.
@@ -90,7 +90,7 @@ 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 Config Settings
+### 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
@@ -99,17 +99,17 @@ 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 txt file named exactly as the file
+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 config options available and the defaults used when they
+Below are the configuration options available and the defaults used when they
 are not explicitly set.
 
-* `normalize`: Switches whitespace normalization of the test output on or off. 
+* `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.  
@@ -118,7 +118,7 @@ are not explicitly set.
   from other implementations.
 * `output_ext`: Extension of output file. Defaults to `.html`. Useful for tests
   from other implementations.
-* Any keyword arguement accepted by the Markdown class. If not set, Markdown's 
+* Any keyword argument accepted by the Markdown class. If not set, Markdown's 
   defaults are used. 
 
 ## Unit Tests
index 3945db6f0e37d17849c4b595990ed3dbcafac804..0ed1dba7d1f12a20ae385026bc828ac9004753be 100644 (file)
--- a/makefile
+++ b/makefile
@@ -1,5 +1,19 @@
 # Python-Markdown makefile
 
+.PHONY : help
+help:
+       @echo 'Usage: make <subcommand>'
+       @echo ''
+       @echo 'Subcommands:'
+       @echo '    install       Install Python-Markdown locally'
+       @echo '    deploy        Register and upload a new release to PyPI'
+       @echo '    build         Build a source distribution'
+       @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
 install:
        python setup.py install
index cbcee58906a7d4c9e543fd410037ddf25df03dbc..107f7027895afaa2dc8a2f13f575a0f1d95d71ea 100644 (file)
@@ -32,7 +32,7 @@ License: BSD (see LICENSE for details).
 
 from __future__ import absolute_import
 from __future__ import unicode_literals
-from .__version__ import version, version_info
+from .__version__ import version, version_info  # noqa
 import codecs
 import sys
 import logging
@@ -49,8 +49,8 @@ from .serializers import to_html_string, to_xhtml_string
 
 __all__ = ['Markdown', 'markdown', 'markdownFromFile']
 
+
 logger = logging.getLogger('MARKDOWN')
-logging.captureWarnings(True)
 
 
 class Markdown(object):
@@ -59,24 +59,24 @@ class Markdown(object):
     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,
+        '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,
+        '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):
         """
@@ -92,15 +92,19 @@ class Markdown(object):
         * 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).
+            * "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).
+            * "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".
+        * 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
@@ -113,13 +117,13 @@ class Markdown(object):
         for c, arg in enumerate(args):
             if pos[c] not in kwargs:
                 kwargs[pos[c]] = arg
-            if c+1 == len(pos): #pragma: no cover
+            if c+1 == len(pos):  # pragma: no cover
                 # ignore any additional args
                 break
         if len(args):
-            warnings.warn('Positional arguments are pending depreacted in Markdown '
-                          'and will be deprecated in version 2.6. Use keyword '
-                          'arguments only.', PendingDeprecationWarning)
+            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():
@@ -131,16 +135,17 @@ class Markdown(object):
             self.enable_attributes = False
 
         if 'safe_mode' in kwargs:
-            warnings.warn('"safe_mode" is pending deprecation in Python-Markdown '
-                          'and will be deprecated in version 2.6. Use an HTML '
-                          'sanitizer (like Bleach http://bleach.readthedocs.org/) '
-                          'if you are parsing untrusted markdown text. See the '
-                          '2.5 release notes for more info', PendingDeprecationWarning)
+            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 pending deprecation '
-                          'in Python-Markdown and will be deprecated in version 2.6 '
-                          'along with "safe_mode".', PendingDeprecationWarning)
+            warnings.warn('The "html_replacement_text" keyword is '
+                          'deprecated along with "safe_mode".',
+                          DeprecationWarning)
 
         self.registeredExtensions = []
         self.docType = ""
@@ -180,8 +185,10 @@ class Markdown(object):
                 ext = self.build_extension(ext, configs.get(ext, {}))
             if isinstance(ext, Extension):
                 ext.extendMarkdown(self, globals())
-                logger.info('Successfully loaded extension "%s.%s".' 
-                            % (ext.__class__.__module__, ext.__class__.__name__))
+                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"'
@@ -196,59 +203,82 @@ class Markdown(object):
         following format: "extname(key1=value1,key2=value2)"
 
         """
-        
+
         configs = dict(configs)
-        
+
         # Parse extensions config params (ignore the order)
-        pos = ext_name.find("(") # find the first "("
+        pos = ext_name.find("(")  # find the first "("
         if pos > 0:
             ext_args = ext_name[pos+1:-1]
             ext_name = ext_name[:pos]
             pairs = [x.split("=") for x in ext_args.split(",")]
             configs.update([(x.strip(), y.strip()) for (x, y) in pairs])
-            warnings.warn('Setting configs in the Named Extension string is pending deprecation. '
-                          'It is recommended that you pass an instance of the extension class to '
-                          'Markdown or use the "extension_configs" keyword. The current behavior '
-                          'will be deprecated in version 2.6 and raise an error in version 2.7. '
-                          'See the Release Notes for Python-Markdown version 2.5 for more info.', 
-                          PendingDeprecationWarning)
+            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, '')
+        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: 
+        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)
+            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: 
+            try:
                 module = importlib.import_module(module_name)
-                logger.debug('Successfuly imported extension module "%s".' % module_name)
-                warnings.warn('Using short names for Markdown\'s builtin extensions is pending deprecation. '
-                              'Use the full path to the extension with Python\'s dot notation '
-                              '(eg: "%s" instead of "%s"). The current behavior will be deprecated in '
-                              'version 2.6 and raise an error in version 2.7. See the Release Notes for '
-                              'Python-Markdown version 2.5 for more info.' % (module_name, ext_name), 
-                              PendingDeprecationWarning) 
+                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: 
+                try:
                     module = importlib.import_module(module_name_old_style)
-                    logger.debug('Successfuly imported extension module "%s".' % module_name_old_style)
-                    warnings.warn('Markdown\'s behavuor of appending "mdx_" to an extension name '
-                                  'is pending deprecation. Use the full path to the extension with '
-                                  'Python\'s dot notation (eg: "%s" instead of "%s"). The '
-                                  'current behavior will be deprecated in version 2.6 and raise an '
-                                  'error in version 2.7. See the Release Notes for Python-Markdown '
-                                  'version 2.5 for more info.' % (module_name_old_style, ext_name),
-                                  PendingDeprecationWarning) 
+                    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)
+                    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
 
@@ -293,8 +323,8 @@ class Markdown(object):
             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) + '"')
+                % (self.output_format,
+                   '"' + '", "'.join(valid_formats) + '"')
             e.args = (message,) + e.args[1:]
             raise
         return self
@@ -350,16 +380,18 @@ class Markdown(object):
         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)
+                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):
+            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())
+                    raise ValueError('Markdown failed to strip top-level '
+                                     'tags. Document=%r' % output.strip())
 
         # Run the text post-processors
         for pp in self.postprocessors.values():
@@ -403,7 +435,7 @@ class Markdown(object):
             if not isinstance(text, util.text_type):
                 text = text.decode(encoding)
 
-        text = text.lstrip('\ufeff') # remove the byte-order mark
+        text = text.lstrip('\ufeff')  # remove the byte-order mark
 
         # Convert
         html = self.convert(text)
@@ -422,7 +454,7 @@ class Markdown(object):
                 output_file.write(html)
                 # Don't close here. User may want to write more.
         else:
-            # Encode manually and write bytes to stdout. 
+            # Encode manually and write bytes to stdout.
             html = html.encode(encoding, "xmlcharrefreplace")
             try:
                 # Write bytes directly to buffer (Python 3).
@@ -442,6 +474,7 @@ 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.
 
@@ -485,12 +518,12 @@ def markdownFromFile(*args, **kwargs):
         if c == len(pos):
             break
     if len(args):
-        warnings.warn('Positional arguments are pending depreacted in Markdown '
-                      'and will be deprecated in version 2.6. Use keyword '
-                      'arguments only.', PendingDeprecationWarning)
+        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))
-
index d085540bce64766652e31ec2ca1f736ecdb2e361..17bfa9f3c494a3f297452a2443e57034dea3ffed 100644 (file)
@@ -4,19 +4,21 @@ COMMAND-LINE SPECIFIC STUFF
 
 """
 
-import markdown
 import sys
 import optparse
 import codecs
-try: 
+import warnings
+import markdown
+try:
     import yaml
-except ImportError: #pragma: no cover
+except ImportError:  # pragma: no cover
     import json as yaml
 
 import logging
-from logging import DEBUG, INFO, CRITICAL
+from logging import DEBUG, WARNING, CRITICAL
+
+logger = logging.getLogger('MARKDOWN')
 
-logger =  logging.getLogger('MARKDOWN')
 
 def parse_options(args=None, values=None):
     """
@@ -27,7 +29,7 @@ def parse_options(args=None, values=None):
     desc = "A Python implementation of John Gruber's Markdown. " \
            "https://pythonhosted.org/Markdown/"
     ver = "%%prog %s" % markdown.version
-    
+
     parser = optparse.OptionParser(usage=usage, description=desc, version=ver)
     parser.add_option("-f", "--file", dest="filename", default=None,
                       help="Write output to OUTPUT_FILE. Defaults to STDOUT.",
@@ -36,28 +38,32 @@ def parse_options(args=None, values=None):
                       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", 
+                      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'.")
-    parser.add_option("-n", "--no_lazy_ol", dest="lazy_ol", 
+    parser.add_option("-n", "--no_lazy_ol", dest="lazy_ol",
                       action='store_false', default=True,
                       help="Observe number of first item of ordered lists.")
     parser.add_option("-x", "--extension", action="append", dest="extensions",
-                      help = "Load extension EXTENSION.", metavar="EXTENSION")
-    parser.add_option("-c", "--extension_configs", dest="configfile", default=None,
+                      help="Load extension EXTENSION.", metavar="EXTENSION")
+    parser.add_option("-c", "--extension_configs",
+                      dest="configfile", default=None,
                       help="Read extension configurations from CONFIG_FILE. "
-                      "CONFIG_FILE must be of JSON or YAML format. YAML format requires "
-                      "that a python YAML library be installed. The parsed JSON or YAML "
-                      "must result in a python dictionary which would be accepted by the "
-                      "'extension_configs' keyword on the markdown.Markdown class. "
-                      "The extensions must also be loaded with the `--extension` option.",
+                      "CONFIG_FILE must be of JSON or YAML format. YAML"
+                      "format requires that a python YAML library be "
+                      "installed. The parsed JSON or YAML must result in a "
+                      "python dictionary which would be accepted by the "
+                      "'extension_configs' keyword on the markdown.Markdown "
+                      "class. The extensions must also be loaded with the "
+                      "`--extension` option.",
                       metavar="CONFIG_FILE")
-    parser.add_option("-q", "--quiet", default = CRITICAL,
+    parser.add_option("-q", "--quiet", default=CRITICAL,
                       action="store_const", const=CRITICAL+10, dest="verbose",
                       help="Suppress all warnings.")
     parser.add_option("-v", "--verbose",
-                      action="store_const", const=INFO, dest="verbose",
+                      action="store_const", const=WARNING, dest="verbose",
                       help="Print all warnings.")
     parser.add_option("--noisy",
                       action="store_const", const=DEBUG, dest="verbose",
@@ -75,37 +81,56 @@ def parse_options(args=None, values=None):
 
     extension_configs = {}
     if options.configfile:
-        with codecs.open(options.configfile, mode="r", encoding=options.encoding) as fp:
+        with codecs.open(
+            options.configfile, mode="r", encoding=options.encoding
+        ) as fp:
             try:
                 extension_configs = yaml.load(fp)
             except Exception as e:
-                message = "Failed parsing extension config file: %s" % options.configfile
+                message = "Failed parsing extension config file: %s" % \
+                          options.configfile
                 e.args = (message,) + e.args[1:]
                 raise
 
-    return {'input': input_file,
-            'output': options.filename,
-            'safe_mode': options.safe,
-            'extensions': options.extensions,
-            'extension_configs': extension_configs,
-            'encoding': options.encoding,
-            'output_format': options.output_format,
-            'lazy_ol': options.lazy_ol}, options.verbose
+    opts = {
+        'input': input_file,
+        'output': options.filename,
+        'extensions': options.extensions,
+        'extension_configs': extension_configs,
+        'encoding': options.encoding,
+        'output_format': options.output_format,
+        'lazy_ol': options.lazy_ol
+    }
 
-def run(): #pragma: no cover
+    if options.safe:
+        # Avoid deprecation warning if user didn't set option
+        opts['safe_mode'] = options.safe
+
+    return opts, options.verbose
+
+
+def run():  # pragma: no cover
     """Run Markdown from the command line."""
 
     # Parse options and adjust logging level if necessary
     options, logging_level = parse_options()
-    if not options: sys.exit(2)
+    if not options:
+        sys.exit(2)
     logger.setLevel(logging_level)
-    logger.addHandler(logging.StreamHandler())
+    console_handler = logging.StreamHandler()
+    logger.addHandler(console_handler)
+    if logging_level <= WARNING:
+        # Ensure deprecation warnings get displayed
+        warnings.filterwarnings('default')
+        logging.captureWarnings(True)
+        warn_logger = logging.getLogger('py.warnings')
+        warn_logger.addHandler(console_handler)
 
     # Run
     markdown.markdownFromFile(**options)
 
-if __name__ == '__main__': #pragma: no cover
-    # Support running module as a commandline command. 
-    # Python 2.5 & 2.6 do: `python -m markdown.__main__ [options] [args]`.
+
+if __name__ == '__main__':  # pragma: no cover
+    # Support running module as a commandline command.
     # Python 2.7 & 3.x do: `python -m markdown [options] [args]`.
     run()
index cabeff37d0fd4b0fb4977db3051a5e1b7833fa2e..0212a4da3bf91f2c5f44a94c31388477ea054ecf 100644 (file)
@@ -1,11 +1,12 @@
 #
 # markdown/__version__.py
 #
-# version_info should conform to PEP 386 
+# 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, 5, 0, 'final', 0)
+version_info = (2, 6, 0, 'final', 0)
+
 
 def _get_version():
     " Returns a PEP 386-compliant version number from version_info. "
index 4504a16f518b2406c85f5fdce1eccfa5ea4eceb5..32d3254cdf832b869a83c0fd1cb6a0607090511e 100644 (file)
@@ -3,16 +3,17 @@ from __future__ import absolute_import
 from . import util
 from . import odict
 
+
 class State(list):
-    """ Track the current and nested state of the parser. 
-    
-    This utility class is used to track the state of the BlockParser and 
+    """ Track the current and nested state of the parser.
+
+    This utility class is used to track the state of the BlockParser and
     support multiple levels if nesting. It's just a simple API wrapped around
     a list. Each time a state is set, that state is appended to the end of the
     list. Each time a state is reset, that state is removed from the end of
     the list.
 
-    Therefore, each time a state is set for a nested block, that state must be 
+    Therefore, each time a state is set for a nested block, that state must be
     reset when we back out of that level of nesting or the state could be
     corrupted.
 
@@ -36,9 +37,10 @@ class State(list):
         else:
             return False
 
+
 class BlockParser:
-    """ Parse Markdown blocks into an ElementTree object. 
-    
+    """ Parse Markdown blocks into an ElementTree object.
+
     A wrapper class that stitches the various BlockProcessors together,
     looping through them and creating an ElementTree object.
     """
@@ -49,12 +51,12 @@ class BlockParser:
         self.markdown = markdown
 
     def parseDocument(self, lines):
-        """ Parse a markdown document into an ElementTree. 
-        
-        Given a list of lines, an ElementTree object (not just a parent Element)
-        is created and the root element is passed to the parser as the parent.
-        The ElementTree object is returned.
-        
+        """ Parse a markdown document into an ElementTree.
+
+        Given a list of lines, an ElementTree object (not just a parent
+        Element) is created and the root element is passed to the parser
+        as the parent. The ElementTree object is returned.
+
         This should only be called on an entire document, not pieces.
 
         """
@@ -64,29 +66,30 @@ class BlockParser:
         return util.etree.ElementTree(self.root)
 
     def parseChunk(self, parent, text):
-        """ Parse a chunk of markdown text and attach to given etree node. 
-        
+        """ Parse a chunk of markdown text and attach to given etree node.
+
         While the ``text`` argument is generally assumed to contain multiple
         blocks which will be split on blank lines, it could contain only one
         block. Generally, this method would be called by extensions when
-        block parsing is required. 
-        
-        The ``parent`` etree Element passed in is altered in place. 
+        block parsing is required.
+
+        The ``parent`` etree Element passed in is altered in place.
         Nothing is returned.
 
         """
         self.parseBlocks(parent, text.split('\n\n'))
 
     def parseBlocks(self, parent, blocks):
-        """ Process blocks of markdown text and attach to given etree node. 
-        
+        """ Process blocks of markdown text and attach to given etree node.
+
         Given a list of ``blocks``, each blockprocessor is stepped through
         until there are no blocks left. While an extension could potentially
-        call this method directly, it's generally expected to be used internally.
+        call this method directly, it's generally expected to be used
+        internally.
 
-        This is a public method as an extension may need to add/alter additional
-        BlockProcessors which call this method to recursively parse a nested
-        block.
+        This is a public method as an extension may need to add/alter
+        additional BlockProcessors which call this method to recursively
+        parse a nested block.
 
         """
         while blocks:
@@ -95,5 +98,3 @@ class BlockParser:
                     if processor.run(parent, blocks) is not False:
                         # run returns True or None
                         break
-
-
index 08fbcf86b309491c0e2a4e6935df1b952f5a864d..29db022cee111b062818853bebaf99d6ffa1dcba 100644 (file)
@@ -2,9 +2,9 @@
 CORE MARKDOWN BLOCKPARSER
 ===========================================================================
 
-This parser handles basic parsing of Markdown blocks.  It doesn't concern itself
-with inline elements such as **bold** or *italics*, but rather just catches
-blocks, lists, quotes, etc.
+This parser handles basic parsing of Markdown blocks.  It doesn't concern
+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
 different type of block. Extensions may add/replace/remove BlockProcessors
@@ -19,7 +19,7 @@ import re
 from . import util
 from .blockparser import BlockParser
 
-logger =  logging.getLogger('MARKDOWN')
+logger = logging.getLogger('MARKDOWN')
 
 
 def build_block_parser(md_instance, **kwargs):
@@ -39,8 +39,8 @@ def build_block_parser(md_instance, **kwargs):
 
 
 class BlockProcessor:
-    """ Base class for block processors. 
-    
+    """ Base class for block processors.
+
     Each subclass will provide the methods below to work with the source and
     tree. Each processor will need to define it's own ``test`` and ``run``
     methods. The ``test`` method should return True or False, to indicate
@@ -82,32 +82,32 @@ class BlockProcessor:
         return '\n'.join(lines)
 
     def test(self, parent, block):
-        """ Test for block type. Must be overridden by subclasses. 
-        
-        As the parser loops through processors, it will call the ``test`` method
-        on each to determine if the given block of text is of that type. This
-        method must return a boolean ``True`` or ``False``. The actual method of
-        testing is left to the needs of that particular block type. It could 
-        be as simple as ``block.startswith(some_string)`` or a complex regular
-        expression. As the block type may be different depending on the parent
-        of the block (i.e. inside a list), the parent etree element is also 
-        provided and may be used as part of the test.
+        """ Test for block type. Must be overridden by subclasses.
+
+        As the parser loops through processors, it will call the ``test``
+        method on each to determine if the given block of text is of that
+        type. This method must return a boolean ``True`` or ``False``. The
+        actual method of testing is left to the needs of that particular
+        block type. It could be as simple as ``block.startswith(some_string)``
+        or a complex regular expression. As the block type may be different
+        depending on the parent of the block (i.e. inside a list), the parent
+        etree element is also provided and may be used as part of the test.
 
         Keywords:
-        
+
         * ``parent``: A etree element which will be the parent of the block.
-        * ``block``: A block of text from the source which has been split at 
+        * ``block``: A block of text from the source which has been split at
             blank lines.
         """
-        pass #pragma: no cover
+        pass  # pragma: no cover
 
     def run(self, parent, blocks):
-        """ Run processor. Must be overridden by subclasses. 
-        
+        """ Run processor. Must be overridden by subclasses.
+
         When the parser determines the appropriate type of a block, the parser
         will call the corresponding processor's ``run`` method. This method
         should parse the individual lines of the block and append them to
-        the etree. 
+        the etree.
 
         Note that both the ``parent`` and ``etree`` keywords are pointers
         to instances of the objects which should be edited in place. Each
@@ -123,12 +123,12 @@ class BlockProcessor:
         * ``parent``: A etree element which is the parent of the current block.
         * ``blocks``: A list of all remaining blocks of the document.
         """
-        pass #pragma: no cover
+        pass  # pragma: no cover
 
 
 class ListIndentProcessor(BlockProcessor):
-    """ Process children of list items. 
-    
+    """ Process children of list items.
+
     Example:
         * a list item
             process this part
@@ -142,16 +142,14 @@ class ListIndentProcessor(BlockProcessor):
 
     def __init__(self, *args):
         BlockProcessor.__init__(self, *args)
-        self.INDENT_RE = re.compile(r'^(([ ]{%s})+)'% self.tab_length)
+        self.INDENT_RE = re.compile(r'^(([ ]{%s})+)' % self.tab_length)
 
     def test(self, parent, block):
         return block.startswith(' '*self.tab_length) and \
-                not self.parser.state.isstate('detabbed') and  \
-                (parent.tag in self.ITEM_TYPES or \
-                    (len(parent) and parent[-1] and \
-                        (parent[-1].tag in self.LIST_TYPES)
-                    )
-                )
+            not self.parser.state.isstate('detabbed') and \
+            (parent.tag in self.ITEM_TYPES or
+                (len(parent) and parent[-1] is not None and
+                    (parent[-1].tag in self.LIST_TYPES)))
 
     def run(self, parent, blocks):
         block = blocks.pop(0)
@@ -162,7 +160,7 @@ class ListIndentProcessor(BlockProcessor):
         if parent.tag in self.ITEM_TYPES:
             # It's possible that this parent has a 'ul' or 'ol' child list
             # with a member.  If that is the case, then that should be the
-            # parent.  This is intended to catch the edge case of an indented 
+            # parent.  This is intended to catch the edge case of an indented
             # list whose first member was parsed previous to this point
             # see OListProcessor
             if len(parent) and parent[-1].tag in self.LIST_TYPES:
@@ -193,7 +191,7 @@ class ListIndentProcessor(BlockProcessor):
         """ Create a new li and parse the block with it as the parent. """
         li = util.etree.SubElement(parent, 'li')
         self.parser.parseBlocks(li, [block])
+
     def get_level(self, parent, block):
         """ Get level of indent based on list level. """
         # Get indent level
@@ -211,7 +209,8 @@ class ListIndentProcessor(BlockProcessor):
         # Step through children of tree to find matching indent level.
         while indent_level > level:
             child = self.lastChild(parent)
-            if child is not None and (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES):
+            if (child is not None and
+               (child.tag in self.LIST_TYPES or child.tag in self.ITEM_TYPES)):
                 if child.tag in self.LIST_TYPES:
                     level += 1
                 parent = child
@@ -227,19 +226,21 @@ class CodeBlockProcessor(BlockProcessor):
 
     def test(self, parent, block):
         return block.startswith(' '*self.tab_length)
-    
+
     def run(self, parent, blocks):
         sibling = self.lastChild(parent)
         block = blocks.pop(0)
         theRest = ''
-        if sibling is not None and sibling.tag == "pre" and len(sibling) \
-                    and sibling[0].tag == "code":
+        if (sibling is not None and sibling.tag == "pre" and
+           len(sibling) and sibling[0].tag == "code"):
             # The previous block was a code block. As blank lines do not start
             # new code blocks, append this block to the previous, adding back
             # linebreaks removed from the split into a list.
             code = sibling[0]
             block, theRest = self.detab(block)
-            code.text = util.AtomicString('%s\n%s\n' % (code.text, block.rstrip()))
+            code.text = util.AtomicString(
+                '%s\n%s\n' % (code.text, block.rstrip())
+            )
         else:
             # This is a new codeblock. Create the elements and insert text.
             pre = util.etree.SubElement(parent, 'pre')
@@ -247,7 +248,7 @@ class CodeBlockProcessor(BlockProcessor):
             block, theRest = self.detab(block)
             code.text = util.AtomicString('%s\n' % block.rstrip())
         if theRest:
-            # This block contained unindented line(s) after the first indented 
+            # This block contained unindented line(s) after the first indented
             # line. Insert these lines as the first block of the master blocks
             # list for future processing.
             blocks.insert(0, theRest)
@@ -264,12 +265,13 @@ class BlockQuoteProcessor(BlockProcessor):
         block = blocks.pop(0)
         m = self.RE.search(block)
         if m:
-            before = block[:m.start()] # Lines before blockquote
+            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.
-            block = '\n'.join([self.clean(line) for line in 
-                            block[m.start():].split('\n')])
+            block = '\n'.join(
+                [self.clean(line) for line in block[m.start():].split('\n')]
+            )
         sibling = self.lastChild(parent)
         if sibling is not None and sibling.tag == "blockquote":
             # Previous block was a blockquote so set that as this blocks parent
@@ -293,6 +295,7 @@ class BlockQuoteProcessor(BlockProcessor):
         else:
             return line
 
+
 class OListProcessor(BlockProcessor):
     """ Process ordered list blocks. """
 
@@ -308,7 +311,7 @@ class OListProcessor(BlockProcessor):
     #   3. Item
     # The ol tag will get starts="3" attribute
     STARTSWITH = '1'
-    # List of allowed sibling tags. 
+    # List of allowed sibling tags.
     SIBLING_TAGS = ['ol', 'ul']
 
     def test(self, parent, block):
@@ -322,12 +325,12 @@ class OListProcessor(BlockProcessor):
         if sibling is not None and sibling.tag in self.SIBLING_TAGS:
             # Previous block was a list item, so set that as parent
             lst = sibling
-            # make sure previous item is in a p- if the item has text, then it
-            # it isn't in a p
-            if lst[-1].text: 
-                # since it's possible there are other children for this sibling,
-                # we can't just SubElement the p, we need to insert it as the 
-                # first item
+            # make sure previous item is in a p- if the item has text,
+            # then it isn't in a p
+            if lst[-1].text:
+                # since it's possible there are other children for this
+                # sibling, we can't just SubElement the p, we need to
+                # insert it as the first item.
                 p = util.etree.Element('p')
                 p.text = lst[-1].text
                 lst[-1].text = ''
@@ -347,7 +350,7 @@ class OListProcessor(BlockProcessor):
             self.parser.parseBlocks(li, [firstitem])
             self.parser.state.reset()
         elif parent.tag in ['ol', 'ul']:
-            # this catches the edge case of a multi-item indented list whose 
+            # this catches the edge case of a multi-item indented list whose
             # first item is in a blank parent-list item:
             # * * subitem1
             #     * subitem2
@@ -357,7 +360,7 @@ class OListProcessor(BlockProcessor):
             # 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.parser.markdown.lazy_ol and self.STARTSWITH != '1':
                 lst.attrib['start'] = self.STARTSWITH
 
         self.parser.state.set('list')
@@ -381,7 +384,7 @@ class OListProcessor(BlockProcessor):
             if m:
                 # This is a new list item
                 # Check first item for the start index
-                if not items and self.TAG=='ol':
+                if not items and self.TAG == 'ol':
                     # Detect the integer value of first list item
                     INTEGER_RE = re.compile('(\d+)')
                     self.STARTSWITH = INTEGER_RE.match(m.group(1)).group()
@@ -420,8 +423,8 @@ class HashHeaderProcessor(BlockProcessor):
         block = blocks.pop(0)
         m = self.RE.search(block)
         if m:
-            before = block[:m.start()] # All lines before header
-            after = block[m.end():]    # All lines after header
+            before = block[:m.start()]  # All lines before header
+            after = block[m.end():]     # All lines after header
             if before:
                 # As the header was not the first line of the block and the
                 # lines before the header must be parsed first,
@@ -433,7 +436,7 @@ class HashHeaderProcessor(BlockProcessor):
             if after:
                 # Insert remaining lines as first block for future parsing.
                 blocks.insert(0, after)
-        else: #pragma: no cover
+        else:  # pragma: no cover
             # This should never happen, but just in case...
             logger.warn("We've got a problem header: %r" % block)
 
@@ -495,7 +498,6 @@ class HRProcessor(BlockProcessor):
             blocks.insert(0, postlines)
 
 
-
 class EmptyBlockProcessor(BlockProcessor):
     """ Process blocks that are empty or start with an empty line. """
 
@@ -515,9 +517,12 @@ class EmptyBlockProcessor(BlockProcessor):
                 # Add remaining lines to master blocks for later.
                 blocks.insert(0, theRest)
         sibling = self.lastChild(parent)
-        if sibling is not None and sibling.tag == 'pre' and len(sibling) and sibling[0].tag == 'code':
+        if (sibling is not None and sibling.tag == 'pre' and
+           len(sibling) and sibling[0].tag == 'code'):
             # Last block is a codeblock. Append to preserve whitespace.
-            sibling[0].text = util.AtomicString('%s%s' % (sibling[0].text, filler))
+            sibling[0].text = util.AtomicString(
+                '%s%s' % (sibling[0].text, filler)
+            )
 
 
 class ParagraphProcessor(BlockProcessor):
@@ -533,7 +538,7 @@ class ParagraphProcessor(BlockProcessor):
             if self.parser.state.isstate('list'):
                 # The parent is a tight-list.
                 #
-                # Check for any children. This will likely only happen in a 
+                # Check for any children. This will likely only happen in a
                 # tight-list when a header isn't followed by a blank line.
                 # For example:
                 #
index 03b2a4cf3247b6b064143be37034ed520f46fec2..6e7a08a1e1770d0d0e01fc0ee73433febfd4f8e1 100644 (file)
@@ -7,9 +7,10 @@ from __future__ import unicode_literals
 from ..util import parseBoolValue
 import warnings
 
+
 class Extension(object):
     """ Base class for extensions to subclass. """
-    
+
     # Default config -- to be overriden by a subclass
     # Must be of the following format:
     #     {
@@ -18,31 +19,36 @@ class Extension(object):
     # Note that Extension.setConfig will raise a KeyError
     # if a default is not set here.
     config = {}
-    
+
     def __init__(self, *args, **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):
-            self.setConfigs(args[0])
-            warnings.warn('Extension classes accepting positional args is pending Deprecation. '
-                          'Each setting should be passed into the Class as a keyword. Positional '
-                          'args will be deprecated in version 2.6 and raise an error in version '
-                          '2.7. See the Release Notes for Python-Markdown version 2.5 for more info.',
-                          PendingDeprecationWarning)
+            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():
-            self.setConfigs(kwargs.pop('configs', {}))
-            warnings.warn('Extension classes accepting a dict on the single keyword "config" is '
-                          'pending Deprecation. Each setting should be passed into the Class as '
-                          'a keyword directly. The "config" keyword will be deprecated in version '
-                          '2.6 and raise an error in version 2.7. See the Release Notes for '
-                          'Python-Markdown version 2.5 for more info.',
-                          PendingDeprecationWarning)
+            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=''):
         """ Return a setting for the given key or an empty string. """
@@ -88,6 +94,7 @@ class Extension(object):
         * md_globals: Global variables in the markdown module namespace.
 
         """
-        raise NotImplementedError('Extension "%s.%s" must define an "extendMarkdown"' \
-            'method.' % (self.__class__.__module__, self.__class__.__name__))
-
+        raise NotImplementedError(
+            'Extension "%s.%s" must define an "extendMarkdown"'
+            'method.' % (self.__class__.__module__, self.__class__.__name__)
+        )
index 58dd0aaad19018c7139106bc21fcd3b78883029f..353d126f6f822987319f75dbfe85fd40c7b158a0 100644 (file)
@@ -4,7 +4,7 @@ Abbreviation Extension for Python-Markdown
 
 This extension adds abbreviation handling to Python-Markdown.
 
-See <https://pythonhosted.org/Markdown/extensions/abbreviations.html> 
+See <https://pythonhosted.org/Markdown/extensions/abbreviations.html>
 for documentation.
 
 Oringinal code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/) and
@@ -12,7 +12,7 @@ Oringinal code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/) and
 
 All changes Copyright 2008-2014 The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 '''
 
@@ -27,14 +27,15 @@ import re
 # Global Vars
 ABBR_REF_RE = re.compile(r'[*]\[(?P<abbr>[^\]]*)\][ ]?:\s*(?P<title>.*)')
 
+
 class AbbrExtension(Extension):
     """ Abbreviation Extension for Python-Markdown. """
 
     def extendMarkdown(self, md, md_globals):
         """ Insert AbbrPreprocessor before ReferencePreprocessor. """
         md.preprocessors.add('abbr', AbbrPreprocessor(md), '<reference')
-        
-           
+
+
 class AbbrPreprocessor(Preprocessor):
     """ Abbreviation Preprocessor - parse text for abbr references. """
 
@@ -42,7 +43,7 @@ class AbbrPreprocessor(Preprocessor):
         '''
         Find and remove all Abbreviation references from the text.
         Each reference is set as a new AbbrPattern in the markdown instance.
-        
+
         '''
         new_text = []
         for line in lines:
@@ -50,19 +51,19 @@ class AbbrPreprocessor(Preprocessor):
             if m:
                 abbr = m.group('abbr').strip()
                 title = m.group('title').strip()
-                self.markdown.inlinePatterns['abbr-%s'%abbr] = \
+                self.markdown.inlinePatterns['abbr-%s' % abbr] = \
                     AbbrPattern(self._generate_pattern(abbr), title)
             else:
                 new_text.append(line)
         return new_text
-    
+
     def _generate_pattern(self, text):
         '''
-        Given a string, returns an regex pattern to match that string. 
-        
-        'HTML' -> r'(?P<abbr>[H][T][M][L])' 
-        
-        Note: we force each char as a literal match (in brackets) as we don't 
+        Given a string, returns an regex pattern to match that string.
+
+        'HTML' -> r'(?P<abbr>[H][T][M][L])'
+
+        Note: we force each char as a literal match (in brackets) as we don't
         know what they will be beforehand.
 
         '''
@@ -85,5 +86,6 @@ class AbbrPattern(Pattern):
         abbr.set('title', self.title)
         return abbr
 
+
 def makeExtension(*args, **kwargs):
     return AbbrExtension(*args, **kwargs)
index 189f2c2dd996a1ab718f6345db911a4d62b90946..76e0fb588c842f80164a8ca118bf13fd753bd568 100644 (file)
@@ -4,16 +4,16 @@ Admonition extension for Python-Markdown
 
 Adds rST-style admonitions. Inspired by [rST][] feature with the same name.
 
-[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions
+[rST]: http://docutils.sourceforge.net/docs/ref/rst/directives.html#specific-admonitions  # noqa
 
-See <https://pythonhosted.org/Markdown/extensions/admonition.html> 
+See <https://pythonhosted.org/Markdown/extensions/admonition.html>
 for documentation.
 
 Original code Copyright [Tiago Serafim](http://www.tiagoserafim.com/).
 
 All changes Copyright The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -46,8 +46,8 @@ class AdmonitionProcessor(BlockProcessor):
     def test(self, parent, block):
         sibling = self.lastChild(parent)
         return self.RE.search(block) or \
-            (block.startswith(' ' * self.tab_length) and sibling and \
-                sibling.get('class', '').find(self.CLASSNAME) != -1)
+            (block.startswith(' ' * self.tab_length) and sibling is not None and
+             sibling.get('class', '').find(self.CLASSNAME) != -1)
 
     def run(self, parent, blocks):
         sibling = self.lastChild(parent)
@@ -82,7 +82,8 @@ class AdmonitionProcessor(BlockProcessor):
         klass, title = match.group(1).lower(), match.group(2)
         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>`
+            # e.g.: `!!! note` will render
+            # `<p class="admonition-title">Note</p>`
             title = klass.capitalize()
         elif title == '':
             # an explicit blank title should not be rendered
@@ -93,4 +94,3 @@ class AdmonitionProcessor(BlockProcessor):
 
 def makeExtension(*args, **kwargs):
     return AdmonitionExtension(*args, **kwargs)
-
index 59da3b42de3740b3a682750f5a4cf76bed6bab77..395259a84804f406ecfb656f12d1ae155876450d 100644 (file)
@@ -2,18 +2,18 @@
 Attribute List Extension for Python-Markdown
 ============================================
 
-Adds attribute list syntax. Inspired by 
+Adds attribute list syntax. Inspired by
 [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://pythonhosted.org/Markdown/extensions/attr_list.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) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -26,21 +26,25 @@ import re
 
 try:
     Scanner = re.Scanner
-except AttributeError: #pragma: no cover
+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('=')
     return k, v.strip('"')
 
+
 def _handle_single_quote(s, t):
     k, v = t.split('=')
     return k, v.strip("'")
 
-def _handle_key_value(s, t): 
+
+def _handle_key_value(s, t):
     return t.split('=')
 
+
 def _handle_word(s, t):
     if t.startswith('.'):
         return '.', t[1:]
@@ -56,22 +60,26 @@ _scanner = Scanner([
     (r' ', None)
 ])
 
+
 def get_attrs(str):
     """ Parse attribute list and return a list of attribute tuples. """
     return _scanner.scan(str)[0]
 
+
 def isheader(elem):
     return elem.tag in ['h1', 'h2', 'h3', 'h4', 'h5', 'h6']
 
+
 class AttrListTreeprocessor(Treeprocessor):
-    
+
     BASE_RE = r'\{\:?([^\}]*)\}'
     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)
-    NAME_RE = re.compile(r'[^A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff\u0370-\u037d'
-                         r'\u037f-\u1fff\u200c-\u200d\u2070-\u218f\u2c00-\u2fef'
-                         r'\u3001-\ud7ff\uf900-\ufdcf\ufdf0-\ufffd'
+    NAME_RE = re.compile(r'[^A-Z_a-z\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02ff'
+                         r'\u0370-\u037d\u037f-\u1fff\u200c-\u200d'
+                         r'\u2070-\u218f\u2c00-\u2fef\u3001-\ud7ff'
+                         r'\uf900-\ufdcf\ufdf0-\ufffd'
                          r'\:\-\.0-9\u00b7\u0300-\u036f\u203f-\u2040]+')
 
     def run(self, doc):
@@ -160,7 +168,9 @@ class AttrListTreeprocessor(Treeprocessor):
 
 class AttrListExtension(Extension):
     def extendMarkdown(self, md, md_globals):
-        md.treeprocessors.add('attr_list', AttrListTreeprocessor(md), '>prettify')
+        md.treeprocessors.add(
+            'attr_list', AttrListTreeprocessor(md), '>prettify'
+        )
 
 
 def makeExtension(*args, **kwargs):
index 7fbf83fb46cdc724f48516ea1172e059c4b1c46f..0657c37681e08c5563b642a6f6bda5be253717e2 100644 (file)
@@ -4,7 +4,7 @@ CodeHilite Extension for Python-Markdown
 
 Adds code/syntax highlighting to standard Python-Markdown code blocks.
 
-See <https://pythonhosted.org/Markdown/extensions/code_hilite.html> 
+See <https://pythonhosted.org/Markdown/extensions/code_hilite.html>
 for documentation.
 
 Original code Copyright 2006-2008 [Waylan Limberg](http://achinghead.com/).
@@ -19,11 +19,11 @@ from __future__ import absolute_import
 from __future__ import unicode_literals
 from . import Extension
 from ..treeprocessors import Treeprocessor
-import warnings
+
 try:
     from pygments import highlight
-    from pygments.lexers import get_lexer_by_name, guess_lexer, TextLexer
-    from pygments.formatters import HtmlFormatter
+    from pygments.lexers import get_lexer_by_name, guess_lexer
+    from pygments.formatters import get_formatter_by_name
     pygments = True
 except ImportError:
     pygments = False
@@ -47,7 +47,7 @@ def parse_hl_lines(expr):
 # ------------------ The Main CodeHilite Class ----------------------
 class CodeHilite(object):
     """
-    Determine language of source code, and pass it into the pygments hilighter.
+    Determine language of source code, and pass it into pygments hilighter.
 
     Basic Usage:
         >>> code = CodeHilite(src = 'some text')
@@ -55,10 +55,11 @@ class CodeHilite(object):
 
     * src: Source string or any object with a .readline attribute.
 
-    * linenums: (Boolean) Set line numbering to 'on' (True), 'off' (False) or 'auto'(None). 
-    Set to 'auto' by default.
+    * linenums: (Boolean) Set line numbering to 'on' (True),
+      'off' (False) or 'auto'(None). Set to 'auto' by default.
 
-    * guess_lang: (Boolean) Turn language auto-detection 'on' or 'off' (on by default).
+    * guess_lang: (Boolean) Turn language auto-detection
+      'on' or 'off' (on by default).
 
     * css_class: Set class name of wrapper div ('codehilite' by default).
 
@@ -67,14 +68,14 @@ class CodeHilite(object):
     Low Level Usage:
         >>> code = CodeHilite()
         >>> code.src = 'some text' # String or anything with a .readline attr.
-        >>> code.linenos = True  # True or False; Turns line numbering on or of.
+        >>> code.linenos = True  # Turns line numbering on or of.
         >>> html = code.hilite()
 
     """
 
     def __init__(self, src=None, linenums=None, guess_lang=True,
-                css_class="codehilite", lang=None, style='default',
-                noclasses=False, tab_length=4, hl_lines=None):
+                 css_class="codehilite", lang=None, style='default',
+                 noclasses=False, tab_length=4, hl_lines=None, use_pygments=True):
         self.src = src
         self.lang = lang
         self.linenums = linenums
@@ -84,6 +85,7 @@ class CodeHilite(object):
         self.noclasses = noclasses
         self.tab_length = tab_length
         self.hl_lines = hl_lines or []
+        self.use_pygments = use_pygments
 
     def hilite(self):
         """
@@ -101,7 +103,7 @@ class CodeHilite(object):
         if self.lang is None:
             self._parseHeader()
 
-        if pygments:
+        if pygments and self.use_pygments:
             try:
                 lexer = get_lexer_by_name(self.lang)
             except ValueError:
@@ -109,14 +111,15 @@ class CodeHilite(object):
                     if self.guess_lang:
                         lexer = guess_lexer(self.src)
                     else:
-                        lexer = TextLexer()
+                        lexer = get_lexer_by_name('text')
                 except ValueError:
-                    lexer = TextLexer()
-            formatter = HtmlFormatter(linenos=self.linenums,
-                                      cssclass=self.css_class,
-                                      style=self.style,
-                                      noclasses=self.noclasses,
-                                      hl_lines=self.hl_lines)
+                    lexer = get_lexer_by_name('text')
+            formatter = get_formatter_by_name('html',
+                                              linenos=self.linenums,
+                                              cssclass=self.css_class,
+                                              style=self.style,
+                                              noclasses=self.noclasses,
+                                              hl_lines=self.hl_lines)
             return highlight(self.src, lexer, formatter)
         else:
             # just escape and build markup usable by JS highlighting libs
@@ -131,9 +134,9 @@ class CodeHilite(object):
                 classes.append('linenums')
             class_str = ''
             if classes:
-                class_str = ' class="%s"' % ' '.join(classes) 
-            return '<pre class="%s"><code%s>%s</code></pre>\n'% \
-                        (self.css_class, class_str, txt)
+                class_str = ' class="%s"' % ' '.join(classes)
+            return '<pre class="%s"><code%s>%s</code></pre>\n' % \
+                   (self.css_class, class_str, txt)
 
     def _parseHeader(self):
         """
@@ -141,8 +144,8 @@ class CodeHilite(object):
         line should be removed or left in place. If the sheband line contains a
         path (even a single /) then it is assumed to be a real shebang line and
         left alone. However, if no path is given (e.i.: #!python or :::python)
-        then it is assumed to be a mock shebang for language identifitation of a
-        code fragment and removed from the code block prior to processing for
+        then it is assumed to be a mock shebang for language identifitation of
+        code fragment and removed from the code block prior to processing for
         code highlighting. When a mock shebang (e.i: #!python) is found, line
         numbering is turned on. When colons are found in place of a shebang
         (e.i.: :::python), line numbering is left in the current state - off
@@ -155,9 +158,9 @@ class CodeHilite(object):
 
         import re
 
-        #split text into lines
+        # split text into lines
         lines = self.src.split("\n")
-        #pull first line to examine
+        # pull first line to examine
         fl = lines.pop(0)
 
         c = re.compile(r'''
@@ -191,24 +194,27 @@ class CodeHilite(object):
         self.src = "\n".join(lines).strip("\n")
 
 
-
 # ------------------ The Markdown Extension -------------------------------
+
+
 class HiliteTreeprocessor(Treeprocessor):
     """ Hilight source code in code blocks. """
 
     def run(self, root):
         """ Find code blocks and store in htmlStash. """
-        blocks = root.getiterator('pre')
+        blocks = root.iter('pre')
         for block in blocks:
-            children = block.getchildren()
-            if len(children) == 1 and children[0].tag == 'code':
-                code = CodeHilite(children[0].text,
-                            linenums=self.config['linenums'],
-                            guess_lang=self.config['guess_lang'],
-                            css_class=self.config['css_class'],
-                            style=self.config['pygments_style'],
-                            noclasses=self.config['noclasses'],
-                            tab_length=self.markdown.tab_length)
+            if len(block) == 1 and block[0].tag == 'code':
+                code = CodeHilite(
+                    block[0].text,
+                    linenums=self.config['linenums'],
+                    guess_lang=self.config['guess_lang'],
+                    css_class=self.config['css_class'],
+                    style=self.config['pygments_style'],
+                    noclasses=self.config['noclasses'],
+                    tab_length=self.markdown.tab_length,
+                    use_pygments=self.config['use_pygments']
+                )
                 placeholder = self.markdown.htmlStash.store(code.hilite(),
                                                             safe=True)
                 # Clear codeblock in etree instance
@@ -225,13 +231,23 @@ class CodeHiliteExtension(Extension):
     def __init__(self, *args, **kwargs):
         # define default configs
         self.config = {
-            'linenums': [None, "Use lines numbers. True=yes, False=no, None=auto"],
-            'force_linenos' : [False, "Depreciated! Use 'linenums' instead. Force line numbers - Default: False"],
-            'guess_lang' : [True, "Automatic language detection - Default: True"],
-            'css_class' : ["codehilite",
-                           "Set class name for wrapper <div> - Default: codehilite"],
-            'pygments_style' : ['default', 'Pygments HTML Formatter Style (Colorscheme) - Default: default'],
-            'noclasses': [False, 'Use inline styles instead of CSS classes - Default false']
+            'linenums': [None,
+                         "Use lines numbers. True=yes, False=no, None=auto"],
+            'guess_lang': [True,
+                           "Automatic language detection - Default: True"],
+            'css_class': ["codehilite",
+                          "Set class name for wrapper <div> - "
+                          "Default: codehilite"],
+            'pygments_style': ['default',
+                               'Pygments HTML Formatter Style '
+                               '(Colorscheme) - Default: default'],
+            'noclasses': [False,
+                          'Use inline styles instead of CSS classes - '
+                          'Default false'],
+            'use_pygments': [True,
+                             'Use Pygments to Highlight code blocks. '
+                             'Disable if using a JavaScript library. '
+                             'Default: True']
             }
 
         super(CodeHiliteExtension, self).__init__(*args, **kwargs)
@@ -246,5 +262,4 @@ class CodeHiliteExtension(Extension):
 
 
 def makeExtension(*args, **kwargs):
-  return CodeHiliteExtension(*args, **kwargs)
-
+    return CodeHiliteExtension(*args, **kwargs)
index 22e2491a66a1904fa99fce4784933e8b2d5e3843..77cca6eb8b4a84848aa63cf5b03968a00ef56bcd 100644 (file)
@@ -4,14 +4,14 @@ Definition List Extension for Python-Markdown
 
 Adds parsing of Definition Lists to Python-Markdown.
 
-See <https://pythonhosted.org/Markdown/extensions/definition_lists.html> 
+See <https://pythonhosted.org/Markdown/extensions/definition_lists.html>
 for documentation.
 
 Original code Copyright 2008 [Waylan Limberg](http://achinghead.com)
 
 All changes Copyright 2008-2014 The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -36,7 +36,8 @@ class DefListProcessor(BlockProcessor):
 
         raw_block = blocks.pop(0)
         m = self.RE.search(raw_block)
-        terms = [l.strip() for l in raw_block[:m.start()].split('\n') if l.strip()]
+        terms = [l.strip() for l in
+                 raw_block[:m.start()].split('\n') if l.strip()]
         block = raw_block[m.end():]
         no_indent = self.NO_INDENT_RE.match(block)
         if no_indent:
@@ -49,7 +50,7 @@ class DefListProcessor(BlockProcessor):
             d = m.group(2)
         sibling = self.lastChild(parent)
         if not terms and sibling is None:
-            # This is not a definition item. Most likely a paragraph that 
+            # This is not a definition item. Most likely a paragraph that
             # starts with a colon at the begining of a document or list.
             blocks.insert(0, raw_block)
             return False
@@ -63,7 +64,7 @@ class DefListProcessor(BlockProcessor):
         else:
             state = 'list'
 
-        if sibling and sibling.tag == 'dl':
+        if sibling is not None and sibling.tag == 'dl':
             # This is another item on an existing list
             dl = sibling
             if not terms and len(dl) and dl[-1].tag == 'dd' and len(dl[-1]):
@@ -84,6 +85,7 @@ class DefListProcessor(BlockProcessor):
         if theRest:
             blocks.insert(0, theRest)
 
+
 class DefListIndentProcessor(ListIndentProcessor):
     """ Process indented children of definition list items. """
 
@@ -94,7 +96,6 @@ class DefListIndentProcessor(ListIndentProcessor):
         """ Create a new dd and parse the block with it as the parent. """
         dd = etree.SubElement(parent, 'dd')
         self.parser.parseBlocks(dd, [block])
 
 
 class DefListExtension(Extension):
@@ -105,11 +106,10 @@ class DefListExtension(Extension):
         md.parser.blockprocessors.add('defindent',
                                       DefListIndentProcessor(md.parser),
                                       '>indent')
-        md.parser.blockprocessors.add('deflist', 
+        md.parser.blockprocessors.add('deflist',
                                       DefListProcessor(md.parser),
                                       '>ulist')
 
 
 def makeExtension(*args, **kwargs):
     return DefListExtension(*args, **kwargs)
-
index 4044a874e450e4b14d9b9b61bfa8308ac249fc7a..de5db03cd6b1cfdf21a21cbffa9631242a153d91 100644 (file)
@@ -20,12 +20,12 @@ under a differant name. You could also edit the `extensions` global
 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://pythonhosted.org/Markdown/extensions/extra.html>
 for documentation.
 
 Copyright The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -51,7 +51,7 @@ class ExtraExtension(Extension):
     """ Add various extensions to Markdown class."""
 
     def __init__(self, *args, **kwargs):
-        """ config is just a dumb holder which gets passed to actual ext later. """
+        """ config is a dumb holder which gets passed to actual ext later. """
         self.config = kwargs.pop('configs', {})
         self.config.update(kwargs)
 
index 2aacca639b85ca801a3c100352e64c1e6bc8e7f0..4af8891a8ba18b7a9e4ac255fc2321728924f9db 100644 (file)
@@ -4,7 +4,7 @@ Fenced Code Extension for Python Markdown
 
 This extension adds Fenced Code Blocks to Python-Markdown.
 
-See <https://pythonhosted.org/Markdown/extensions/fenced_code_blocks.html> 
+See <https://pythonhosted.org/Markdown/extensions/fenced_code_blocks.html>
 for documentation.
 
 Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/).
@@ -12,7 +12,7 @@ Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com/).
 
 All changes Copyright 2008-2014 The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 """
 
 from __future__ import absolute_import
@@ -30,8 +30,8 @@ class FencedCodeExtension(Extension):
         md.registerExtension(self)
 
         md.preprocessors.add('fenced_code_block',
-                                 FencedBlockPreprocessor(md),
-                                 ">normalize_whitespace")
+                             FencedBlockPreprocessor(md),
+                             ">normalize_whitespace")
 
 
 class FencedBlockPreprocessor(Preprocessor):
@@ -75,21 +75,26 @@ class FencedBlockPreprocessor(Preprocessor):
                 # If config is not empty, then the codehighlite extension
                 # is enabled, so we call it to highlight the code
                 if self.codehilite_conf:
-                    highliter = CodeHilite(m.group('code'),
-                            linenums=self.codehilite_conf['linenums'][0],
-                            guess_lang=self.codehilite_conf['guess_lang'][0],
-                            css_class=self.codehilite_conf['css_class'][0],
-                            style=self.codehilite_conf['pygments_style'][0],
-                            lang=(m.group('lang') or None),
-                            noclasses=self.codehilite_conf['noclasses'][0],
-                            hl_lines=parse_hl_lines(m.group('hl_lines')))
+                    highliter = CodeHilite(
+                        m.group('code'),
+                        linenums=self.codehilite_conf['linenums'][0],
+                        guess_lang=self.codehilite_conf['guess_lang'][0],
+                        css_class=self.codehilite_conf['css_class'][0],
+                        style=self.codehilite_conf['pygments_style'][0],
+                        lang=(m.group('lang') or None),
+                        noclasses=self.codehilite_conf['noclasses'][0],
+                        hl_lines=parse_hl_lines(m.group('hl_lines'))
+                    )
 
                     code = highliter.hilite()
                 else:
-                    code = self.CODE_WRAP % (lang, self._escape(m.group('code')))
+                    code = self.CODE_WRAP % (lang,
+                                             self._escape(m.group('code')))
 
                 placeholder = self.markdown.htmlStash.store(code, safe=True)
-                text = '%s\n%s\n%s'% (text[:m.start()], placeholder, text[m.end():])
+                text = '%s\n%s\n%s' % (text[:m.start()],
+                                       placeholder,
+                                       text[m.end():])
             else:
                 break
         return text.split("\n")
@@ -105,4 +110,3 @@ class FencedBlockPreprocessor(Preprocessor):
 
 def makeExtension(*args, **kwargs):
     return FencedCodeExtension(*args, **kwargs)
-
index a59de970b41b2256bf40df4c1a52cde4a449ea1e..d8caae27cd977061c9e287c757b859a013bbeefb 100644 (file)
@@ -4,12 +4,12 @@ Footnotes Extension for Python-Markdown
 
 Adds footnote handling to Python-Markdown.
 
-See <https://pythonhosted.org/Markdown/extensions/footnotes.html> 
+See <https://pythonhosted.org/Markdown/extensions/footnotes.html>
 for documentation.
 
 Copyright The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -25,30 +25,32 @@ from ..odict import OrderedDict
 import re
 
 FN_BACKLINK_TEXT = "zz1337820767766393qq"
-NBSP_PLACEHOLDER =  "qq3936677670287331zz"
+NBSP_PLACEHOLDER = "qq3936677670287331zz"
 DEF_RE = re.compile(r'[ ]{0,3}\[\^([^\]]*)\]:\s*(.*)')
 TABBED_RE = re.compile(r'((\t)|(    ))(.*)')
 
+
 class FootnoteExtension(Extension):
     """ Footnote Extension. """
 
-    def __init__ (self, *args, **kwargs):
+    def __init__(self, *args, **kwargs):
         """ Setup configs. """
 
         self.config = {
             'PLACE_MARKER':
-                 ["///Footnotes Go Here///",
-                  "The text string that marks where the footnotes go"],
+                ["///Footnotes Go Here///",
+                 "The text string that marks where the footnotes go"],
             'UNIQUE_IDS':
-                 [False,
-                  "Avoid name collisions across "
-                  "multiple calls to reset()."],
+                [False,
+                 "Avoid name collisions across "
+                 "multiple calls to reset()."],
             "BACKLINK_TEXT":
-                 ["&#8617;",
-                  "The text string that links from the footnote to the reader's place."]
+                ["&#8617;",
+                 "The text string that links from the footnote "
+                 "to the reader's place."]
         }
         super(FootnoteExtension, self).__init__(*args, **kwargs)
-        
+
         # In multiple invocations, emit links that don't get tangled.
         self.unique_prefix = 0
 
@@ -60,23 +62,27 @@ class FootnoteExtension(Extension):
         self.parser = md.parser
         self.md = md
         # Insert a preprocessor before ReferencePreprocessor
-        md.preprocessors.add("footnote", FootnotePreprocessor(self),
-                             "<reference")
+        md.preprocessors.add(
+            "footnote", FootnotePreprocessor(self), "<reference"
+        )
         # Insert an inline pattern before ImageReferencePattern
-        FOOTNOTE_RE = r'\[\^([^\]]*)\]' # blah blah [^1] blah
-        md.inlinePatterns.add("footnote", FootnotePattern(FOOTNOTE_RE, self),
-                              "<reference")
+        FOOTNOTE_RE = r'\[\^([^\]]*)\]'  # blah blah [^1] blah
+        md.inlinePatterns.add(
+            "footnote", FootnotePattern(FOOTNOTE_RE, self), "<reference"
+        )
         # Insert a tree-processor that would actually add the footnote div
-        # This must be before all other treeprocessors (i.e., inline and 
+        # 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")
+        md.treeprocessors.add(
+            "footnote", FootnoteTreeprocessor(self), "_begin"
+        )
         # Insert a postprocessor after amp_substitute oricessor
-        md.postprocessors.add("footnote", FootnotePostprocessor(self),
-                                  ">amp_substitute")
+        md.postprocessors.add(
+            "footnote", FootnotePostprocessor(self), ">amp_substitute"
+        )
 
     def reset(self):
-        """ Clear the footnotes on reset, and prepare for a distinct document. """
+        """ Clear footnotes on reset, and prepare for distinct document. """
         self.footnotes = OrderedDict()
         self.unique_prefix += 1
 
@@ -92,7 +98,7 @@ class FootnoteExtension(Extension):
                         return child, element, False
                 finder(child)
             return None
-                
+
         res = finder(root)
         return res
 
@@ -115,7 +121,8 @@ class FootnoteExtension(Extension):
     def makeFootnoteRefId(self, id):
         """ Return footnote back-link id. """
         if self.getConfig("UNIQUE_IDS"):
-            return 'fnref%s%d-%s' % (self.get_separator(), self.unique_prefix, id)
+            return 'fnref%s%d-%s' % (self.get_separator(),
+                                     self.unique_prefix, id)
         else:
             return 'fnref%s%s' % (self.get_separator(), id)
 
@@ -137,10 +144,13 @@ class FootnoteExtension(Extension):
             backlink = 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("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))
+            backlink.set(
+                "title",
+                "Jump back to footnote %d in the text" %
+                (self.footnotes.index(id)+1)
+            )
             backlink.text = FN_BACKLINK_TEXT
 
             if li.getchildren():
@@ -157,7 +167,7 @@ class FootnoteExtension(Extension):
 class FootnotePreprocessor(Preprocessor):
     """ Find all footnote references and store for later use. """
 
-    def __init__ (self, footnotes):
+    def __init__(self, footnotes):
         self.footnotes = footnotes
 
     def run(self, lines):
@@ -178,7 +188,7 @@ class FootnotePreprocessor(Preprocessor):
             if m:
                 fn, _i = self.detectTabbed(lines[i+1:])
                 fn.insert(0, m.group(2))
-                i += _i-1 # skip past footnote
+                i += _i-1  # skip past footnote
                 self.footnotes.setFootnote(m.group(1), "\n".join(fn))
             else:
                 newlines.append(lines[i])
@@ -199,16 +209,16 @@ class FootnotePreprocessor(Preprocessor):
 
         """
         items = []
-        blank_line = False # have we encountered a blank line yet?
-        i = 0 # to keep track of where we are
+        blank_line = False  # have we encountered a blank line yet?
+        i = 0  # to keep track of where we are
 
         def detab(line):
             match = TABBED_RE.match(line)
             if match:
-               return match.group(4)
+                return match.group(4)
 
         for line in lines:
-            if line.strip(): # Non-blank line
+            if line.strip():  # Non-blank line
                 detabbed_line = detab(line)
                 if detabbed_line:
                     items.append(detabbed_line)
@@ -222,23 +232,24 @@ class FootnotePreprocessor(Preprocessor):
                 else:
                     return items, i+1
 
-            else: # Blank line: _maybe_ we are done.
+            else:  # Blank line: _maybe_ we are done.
                 blank_line = True
-                i += 1 # advance
+                i += 1  # advance
 
                 # Find the next non-blank line
                 for j in range(i, len(lines)):
                     if lines[j].strip():
-                        next_line = lines[j]; break
+                        next_line = lines[j]
+                        break
                 else:
-                    break # There is no more text; we are done.
+                    break  # There is no more text; we are done.
 
                 # Check if the next non-blank line is tabbed
-                if detab(next_line): # Yes, more work to do.
+                if detab(next_line):  # Yes, more work to do.
                     items.append("")
                     continue
                 else:
-                    break # No, we are done.
+                    break  # No, we are done.
         else:
             i += 1
 
@@ -260,7 +271,7 @@ class FootnotePattern(Pattern):
             sup.set('id', self.footnotes.makeFootnoteRefId(id))
             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('rel', 'footnote')  # invalid in HTML5
             a.set('class', 'footnote-ref')
             a.text = text_type(self.footnotes.footnotes.index(id) + 1)
             return sup
@@ -271,12 +282,12 @@ class FootnotePattern(Pattern):
 class FootnoteTreeprocessor(Treeprocessor):
     """ Build and append footnote div to end of document. """
 
-    def __init__ (self, footnotes):
+    def __init__(self, footnotes):
         self.footnotes = footnotes
 
     def run(self, root):
         footnotesDiv = self.footnotes.makeFootnotesDiv(root)
-        if footnotesDiv:
+        if footnotesDiv is not None:
             result = self.footnotes.findFootnotesPlaceholder(root)
             if result:
                 child, parent, isText = result
@@ -290,16 +301,19 @@ class FootnoteTreeprocessor(Treeprocessor):
             else:
                 root.append(footnotesDiv)
 
+
 class FootnotePostprocessor(Postprocessor):
     """ Replace placeholders with html entities. """
     def __init__(self, footnotes):
         self.footnotes = footnotes
 
     def run(self, text):
-        text = text.replace(FN_BACKLINK_TEXT, self.footnotes.getConfig("BACKLINK_TEXT"))
+        text = text.replace(
+            FN_BACKLINK_TEXT, self.footnotes.getConfig("BACKLINK_TEXT")
+        )
         return text.replace(NBSP_PLACEHOLDER, "&#160;")
 
+
 def makeExtension(*args, **kwargs):
     """ Return an instance of the FootnoteExtension """
     return FootnoteExtension(*args, **kwargs)
-
index f7b7805bf8ccc18ff628c3a1453fcb36788e73e2..2cb20b97ab6c4901bc3626643e869236d2961e6b 100644 (file)
@@ -4,14 +4,14 @@ HeaderID Extension for Python-Markdown
 
 Auto-generate id attributes for HTML headers.
 
-See <https://pythonhosted.org/Markdown/extensions/header_id.html> 
+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) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -19,64 +19,9 @@ from __future__ import absolute_import
 from __future__ import unicode_literals
 from . import Extension
 from ..treeprocessors import Treeprocessor
-from ..util import HTML_PLACEHOLDER_RE, parseBoolValue
-import re
-import logging
-import unicodedata
-
-logger = logging.getLogger('MARKDOWN')
-
-IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
-
-
-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)
-
-
-def unique(id, ids):
-    """ Ensure id is unique in set of ids. Append '_1', '_2'... if not """
-    while id in ids or not id:
-        m = IDCOUNT_RE.match(id)
-        if m:
-            id = '%s_%d'% (m.group(1), int(m.group(2))+1)
-        else:
-            id = '%s_%d'% (id, 1)
-    ids.add(id)
-    return id
-
-
-def itertext(elem):
-    """ Loop through all children and return text only. 
-    
-    Reimplements method of same name added to ElementTree in Python 2.7
-    
-    """
-    if elem.text:
-        yield elem.text
-    for e in elem:
-        for s in itertext(e):
-            yield s
-        if e.tail:
-            yield e.tail
-
-
-def stashedHTML2text(text, md):
-    """ Extract raw HTML, reduce to plain text and swap with placeholder. """
-    def _html_sub(m):
-        """ Substitute raw html with plain text. """
-        try:
-            raw, safe = md.htmlStash.rawHtmlBlocks[int(m.group(1))]
-        except (IndexError, TypeError):
-            return m.group(0)
-        if md.safeMode and not safe:
-            return ''
-        # Strip out tags and entities - leaveing text
-        return re.sub(r'(<[^>]+>)|(&[\#a-zA-Z0-9]+;)', '', raw)
-
-    return HTML_PLACEHOLDER_RE.sub(_html_sub, text)
+from ..util import parseBoolValue
+from .toc import slugify, unique, stashedHTML2text
+import warnings
 
 
 class HeaderIdTreeprocessor(Treeprocessor):
@@ -88,13 +33,13 @@ class HeaderIdTreeprocessor(Treeprocessor):
         start_level, force_id = self._get_meta()
         slugify = self.config['slugify']
         sep = self.config['separator']
-        for elem in doc.getiterator():
+        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(itertext(elem)), self.md)
+                        id = stashedHTML2text(''.join(elem.itertext()), self.md)
                         id = slugify(id, sep)
                     elem.set('id', unique(id, self.IDs))
                 if start_level:
@@ -103,7 +48,6 @@ class HeaderIdTreeprocessor(Treeprocessor):
                         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
@@ -111,7 +55,7 @@ class HeaderIdTreeprocessor(Treeprocessor):
         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: 
+            if 'header_forceid' in self.md.Meta:
                 force = parseBoolValue(self.md.Meta['header_forceid'][0])
         return level, force
 
@@ -120,14 +64,19 @@ 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'], 
-            }
+            '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()
@@ -146,4 +95,3 @@ class HeaderIdExtension(Extension):
 
 def makeExtension(*args, **kwargs):
     return HeaderIdExtension(*args, **kwargs)
-
index bb81a867c0a56cd21723c57f35934a1291abd794..cf8074f20884768157d9e235ff26fb0659c18250 100644 (file)
@@ -4,14 +4,14 @@ Meta Data Extension for Python-Markdown
 
 This extension adds Meta Data handling to markdown.
 
-See <https://pythonhosted.org/Markdown/extensions/meta_data.html> 
+See <https://pythonhosted.org/Markdown/extensions/meta_data.html>
 for documentation.
 
 Original code Copyright 2007-2008 [Waylan Limberg](http://achinghead.com).
 
 All changes Copyright 2008-2014 The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -20,33 +20,68 @@ from __future__ import unicode_literals
 from . import Extension
 from ..preprocessors import Preprocessor
 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.*)?')
+
 
 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):
         """ Add MetaPreprocessor to Markdown instance. """
-
-        md.preprocessors.add("meta", MetaPreprocessor(md), "_begin")
+        md.preprocessors.add("meta",
+                             MetaPreprocessor(md, self.getConfigs()),
+                             ">normalize_whitespace")
 
 
 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
+            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)
-            if line.strip() == '':
-                break # blank line - done
             m1 = META_RE.match(line)
-            if m1:
+            if line.strip() == '' or have_yaml and YAML_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:
                 key = m1.group('key').lower().strip()
                 value = m1.group('value').strip()
                 try:
@@ -60,11 +95,12 @@ class MetaPreprocessor(Preprocessor):
                     meta[key].append(m2.group('value').strip())
                 else:
                     lines.insert(0, line)
-                    break # no meta data - done
+                    break  # no meta data - done
+        if yaml_block:
+            meta = yaml.load('\n'.join(yaml_block), SafeLoader)
         self.markdown.Meta = meta
         return lines
-        
+
 
 def makeExtension(*args, **kwargs):
     return MetaExtension(*args, **kwargs)
-
index 062a7e6e2ec7d152a3029f16074b4c5269609143..8acd60c2e1cf4ee921197ca2c08d3548b7c68d60 100644 (file)
@@ -5,14 +5,14 @@ NL2BR Extension
 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://pythonhosted.org/Markdown/extensions/nl2br.html>
 for documentation.
 
 Oringinal code Copyright 2011 [Brian Neal](http://deathofagremmie.com/)
 
 All changes Copyright 2011-2014 The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -23,6 +23,7 @@ from ..inlinepatterns import SubstituteTagPattern
 
 BR_RE = r'\n'
 
+
 class Nl2BrExtension(Extension):
 
     def extendMarkdown(self, md, md_globals):
@@ -32,4 +33,3 @@ class Nl2BrExtension(Extension):
 
 def makeExtension(*args, **kwargs):
     return Nl2BrExtension(*args, **kwargs)
-
index 9eb3a11f8809dcf73692b6c984d4f9b42a40dcd2..213c8a6fb8f5e7516d07dc3e54fa05739ca56e78 100644 (file)
@@ -4,14 +4,14 @@ Sane List Extension for Python-Markdown
 
 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://pythonhosted.org/Markdown/extensions/sane_lists.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) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -23,13 +23,13 @@ import re
 
 
 class SaneOListProcessor(OListProcessor):
-    
+
     CHILD_RE = re.compile(r'^[ ]{0,3}((\d+\.))[ ]+(.*)')
     SIBLING_TAGS = ['ol']
 
 
 class SaneUListProcessor(UListProcessor):
-    
+
     CHILD_RE = re.compile(r'^[ ]{0,3}(([*+-]))[ ]+(.*)')
     SIBLING_TAGS = ['ul']
 
@@ -45,4 +45,3 @@ class SaneListExtension(Extension):
 
 def makeExtension(*args, **kwargs):
     return SaneListExtension(*args, **kwargs)
-
index 331dae8aeb7c72e76bf8bf0f6c509c4eded43336..58570bb55e766e326ee3d21246e04109f4a3c657 100644 (file)
@@ -4,14 +4,14 @@ 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> 
+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) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 '''
 
@@ -23,13 +23,19 @@ 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')
+        md.inlinePatterns.add(
+            'strong2',
+            SimpleTagPattern(SMART_STRONG_RE, 'strong'),
+            '>emphasis2'
+        )
+
 
 def makeExtension(*args, **kwargs):
     return SmartEmphasisExtension(*args, **kwargs)
index 00c330f1f05f0a8cc155fb716d6ea59027820da3..3d790612764806cfc62e220255f9b6ac833cacd6 100644 (file)
@@ -3,17 +3,17 @@
 Smarty extension for Python-Markdown
 ====================================
 
-Adds conversion of ASCII dashes, quotes and ellipses to their HTML 
+Adds conversion of ASCII dashes, quotes and ellipses to their HTML
 entity equivalents.
 
-See <https://pythonhosted.org/Markdown/extensions/smarty.html> 
+See <https://pythonhosted.org/Markdown/extensions/smarty.html>
 for documentation.
 
 Author: 2013, Dmitry Shachnev <mitya57@gmail.com>
 
 All changes Copyright 2013-2014 The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 SmartyPants license:
 
@@ -32,7 +32,7 @@ SmartyPants license:
       the documentation and/or other materials provided with the
       distribution.
 
-   *  Neither the name "SmartyPants" nor the names of its contributors 
+   *  Neither the name "SmartyPants" nor the names of its contributors
       may be used to endorse or promote products derived from this
       software without specific prior written permission.
 
@@ -86,7 +86,7 @@ from . import Extension
 from ..inlinepatterns import HtmlPattern
 from ..odict import OrderedDict
 from ..treeprocessors import InlineProcessor
-from ..util import parseBoolValue
+
 
 # Constants for quote education.
 punctClass = r"""[!"#\$\%'()*+,-.\/:;<=>?\@\[\\\]\^_`{|}~]"""
@@ -94,14 +94,14 @@ endOfWordClass = r"[\s.,;:!?)]"
 closeClass = "[^\ \t\r\n\[\{\(\-\u0002\u0003]"
 
 openingQuotesBase = (
-   '(\s'              # a  whitespace char
-   '|&nbsp;'          # or a non-breaking space entity
-   '|--'              # or dashes
-   '|–|—'             # or unicode
-   '|&[mn]dash;'      # or named dash entities
-   '|&#8211;|&#8212;' # or decimal entities
-   ')'
-) 
+    '(\s'               # a  whitespace char
+    '|&nbsp;'           # or a non-breaking space entity
+    '|--'               # or dashes
+    '|–|—'              # or unicode
+    '|&[mn]dash;'       # or named dash entities
+    '|&#8211;|&#8212;'  # or decimal entities
+    ')'
+)
 
 substitutions = {
     'mdash': '&mdash;',
@@ -137,13 +137,14 @@ closingDoubleQuotesRegex2 = '(?<=%s)"' % closeClass
 openingSingleQuotesRegex = r"%s'(?=\w)" % openingQuotesBase
 
 # Single closing quotes:
-closingSingleQuotesRegex  = r"(?<=%s)'(?!\s|s\b|\d)" % closeClass
+closingSingleQuotesRegex = r"(?<=%s)'(?!\s|s\b|\d)" % closeClass
 closingSingleQuotesRegex2 = r"(?<=%s)'(\s|s\b)" % closeClass
 
 # All remaining quotes should be opening ones
 remainingSingleQuotesRegex = "'"
 remainingDoubleQuotesRegex = '"'
 
+
 class SubstituteTextPattern(HtmlPattern):
     def __init__(self, pattern, replace, markdown_instance):
         """ Replaces matches with some text. """
@@ -160,6 +161,7 @@ class SubstituteTextPattern(HtmlPattern):
                 result += self.markdown.htmlStash.store(part, safe=True)
         return result
 
+
 class SmartyExtension(Extension):
     def __init__(self, *args, **kwargs):
         self.config = {
@@ -167,7 +169,7 @@ class SmartyExtension(Extension):
             'smart_angled_quotes': [False, 'Educate angled quotes'],
             'smart_dashes': [True, 'Educate dashes'],
             'smart_ellipses': [True, 'Educate ellipses'],
-            'substitutions' : [{}, 'Overwrite default substitutions'],
+            'substitutions': [{}, 'Overwrite default substitutions'],
         }
         super(SmartyExtension, self).__init__(*args, **kwargs)
         self.substitutions = dict(substitutions)
@@ -182,31 +184,40 @@ class SmartyExtension(Extension):
             self.inlinePatterns.add(name, pattern, after)
 
     def educateDashes(self, md):
-        emDashesPattern = SubstituteTextPattern(r'(?<!-)---(?!-)',
-                                            (self.substitutions['mdash'],), md)
-        enDashesPattern = SubstituteTextPattern(r'(?<!-)--(?!-)',
-                                            (self.substitutions['ndash'],), md)
+        emDashesPattern = SubstituteTextPattern(
+            r'(?<!-)---(?!-)', (self.substitutions['mdash'],), md
+        )
+        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.add(
+            'smarty-en-dashes', enDashesPattern, '>smarty-em-dashes'
+        )
 
     def educateEllipses(self, md):
-        ellipsesPattern = SubstituteTextPattern(r'(?<!\.)\.{3}(?!\.)',
-                                         (self.substitutions['ellipsis'],), md)
+        ellipsesPattern = SubstituteTextPattern(
+            r'(?<!\.)\.{3}(?!\.)', (self.substitutions['ellipsis'],), md
+        )
         self.inlinePatterns.add('smarty-ellipses', ellipsesPattern, '_begin')
 
     def educateAngledQuotes(self, md):
-        leftAngledQuotePattern = SubstituteTextPattern(r'\<\<',
-                                 (self.substitutions['left-angle-quote'],), md)
-        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')
+        leftAngledQuotePattern = SubstituteTextPattern(
+            r'\<\<', (self.substitutions['left-angle-quote'],), md
+        )
+        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'
+        )
 
     def educateQuotes(self, md):
-        configs = self.getConfigs()
         lsquo = self.substitutions['left-single-quote']
         rsquo = self.substitutions['right-single-quote']
         ldquo = self.substitutions['left-double-quote']
@@ -243,5 +254,6 @@ class SmartyExtension(Extension):
         md.treeprocessors.add('smarty', inlineProcessor, '_end')
         md.ESCAPED_CHARS.extend(['"', "'"])
 
+
 def makeExtension(*args, **kwargs):
     return SmartyExtension(*args, **kwargs)
index 57507e96d8c32e79446931f62490ea10f0553295..cbf711ae56c26e0b4410ef675bf46f5b0102f90e 100644 (file)
@@ -4,7 +4,7 @@ Tables Extension for Python-Markdown
 
 Added parsing of tables to Python-Markdown.
 
-See <https://pythonhosted.org/Markdown/extensions/tables.html> 
+See <https://pythonhosted.org/Markdown/extensions/tables.html>
 for documentation.
 
 Original code Copyright 2009 [Waylan Limberg](http://achinghead.com)
@@ -21,13 +21,14 @@ from . import Extension
 from ..blockprocessors import BlockProcessor
 from ..util import etree
 
+
 class TableProcessor(BlockProcessor):
     """ Process Tables. """
 
     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 
+        return (len(rows) > 2 and '|' in rows[0] and
+                '|' in rows[1] and '-' in rows[1] and
                 rows[1].strip()[0] in ['|', ':', '-'])
 
     def run(self, parent, blocks):
@@ -66,13 +67,13 @@ class TableProcessor(BlockProcessor):
         if parent.tag == 'thead':
             tag = 'th'
         cells = self._split_row(row, border)
-        # We use align here rather than cells to ensure every 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()
-            except IndexError: #pragma: no cover
+            except IndexError:  # pragma: no cover
                 c.text = ""
             if a:
                 c.set('align', a)
@@ -92,11 +93,10 @@ class TableExtension(Extension):
 
     def extendMarkdown(self, md, md_globals):
         """ Add an instance of TableProcessor to BlockParser. """
-        md.parser.blockprocessors.add('table', 
+        md.parser.blockprocessors.add('table',
                                       TableProcessor(md.parser),
                                       '<hashheader')
 
 
 def makeExtension(*args, **kwargs):
     return TableExtension(*args, **kwargs)
-
index 22cf063b8ae58ff03e8cea4047ac6c287e6733ca..fdfa6879e658c8a288c513e2adc87a160b7f621a 100644 (file)
@@ -2,14 +2,14 @@
 Table of Contents Extension for Python-Markdown
 ===============================================
 
-See <https://pythonhosted.org/Markdown/extensions/toc.html> 
+See <https://pythonhosted.org/Markdown/extensions/toc.html>
 for documentation.
 
 Oringinal code Copyright 2008 [Jack Miller](http://codezen.org)
 
 All changes Copyright 2008-2014 The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 """
 
@@ -17,88 +17,168 @@ from __future__ import absolute_import
 from __future__ import unicode_literals
 from . import Extension
 from ..treeprocessors import Treeprocessor
-from ..util import etree, parseBoolValue, AMP_SUBSTITUTE
-from .headerid import slugify, unique, itertext, stashedHTML2text
+from ..util import etree, parseBoolValue, AMP_SUBSTITUTE, HTML_PLACEHOLDER_RE
 import re
+import unicodedata
 
 
-def order_toc_list(toc_list):
+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)
+
+
+IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$')
+
+
+def unique(id, ids):
+    """ Ensure id is unique in set of ids. Append '_1', '_2'... if not """
+    while id in ids or not id:
+        m = IDCOUNT_RE.match(id)
+        if m:
+            id = '%s_%d' % (m.group(1), int(m.group(2))+1)
+        else:
+            id = '%s_%d' % (id, 1)
+    ids.add(id)
+    return id
+
+
+def stashedHTML2text(text, md):
+    """ Extract raw HTML from stash, reduce to plain text and swap with placeholder. """
+    def _html_sub(m):
+        """ Substitute raw html with plain text. """
+        try:
+            raw, safe = 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)
+
+    return HTML_PLACEHOLDER_RE.sub(_html_sub, text)
+
+
+def nest_toc_tokens(toc_list):
     """Given an unsorted list with errors and skips, return a nested one.
     [{'level': 1}, {'level': 2}]
     =>
     [{'level': 1, 'children': [{'level': 2, 'children': []}]}]
-    
+
     A wrong list is also converted:
     [{'level': 2}, {'level': 1}]
     =>
     [{'level': 2, 'children': []}, {'level': 1, 'children': []}]
     """
-    
-    def build_correct(remaining_list, prev_elements=[{'level': 1000}]):
-        
-        if not remaining_list:
-            return [], []
-        
-        current = remaining_list.pop(0)
-        if not 'children' in current.keys():
-            current['children'] = []
-        
-        if not prev_elements:
-            # This happens for instance with [8, 1, 1], ie. when some
-            # header level is outside a scope. We treat it as a
-            # top-level
-            next_elements, children = build_correct(remaining_list, [current])
-            current['children'].append(children)
-            return [current] + next_elements, []
-        
-        prev_element = prev_elements.pop()
-        children = []
-        next_elements = []
-        # Is current part of the child list or next list?
-        if current['level'] > prev_element['level']:
-            #print "%d is a child of %d" % (current['level'], prev_element['level'])
-            prev_elements.append(prev_element)
-            prev_elements.append(current)
-            prev_element['children'].append(current)
-            next_elements2, children2 = build_correct(remaining_list, prev_elements)
-            children += children2
-            next_elements += next_elements2
-        else:
-            #print "%d is ancestor of %d" % (current['level'], prev_element['level'])
-            if not prev_elements:
-                #print "No previous elements, so appending to the next set"
-                next_elements.append(current)
-                prev_elements = [current]
-                next_elements2, children2 = build_correct(remaining_list, prev_elements)
-                current['children'].extend(children2)
+
+    ordered_list = []
+    if len(toc_list):
+        # Initialize everything by processing the first entry
+        last = toc_list.pop(0)
+        last['children'] = []
+        levels = [last['level']]
+        ordered_list.append(last)
+        parents = []
+
+        # Walk the rest nesting the entries properly
+        while toc_list:
+            t = toc_list.pop(0)
+            current_level = t['level']
+            t['children'] = []
+
+            # Reduce depth if current level < last item's level
+            if current_level < levels[-1]:
+                # Pop last level since we know we are less than it
+                levels.pop()
+
+                # Pop parents and levels we are less than or equal to
+                to_pop = 0
+                for p in reversed(parents):
+                    if current_level <= p['level']:
+                        to_pop += 1
+                    else:  # pragma: no cover
+                        break
+                if to_pop:
+                    levels = levels[:-to_pop]
+                    parents = parents[:-to_pop]
+
+                # Note current level as last
+                levels.append(current_level)
+
+            # Level is the same, so append to
+            # the current parent (if available)
+            if current_level == levels[-1]:
+                (parents[-1]['children'] if parents
+                 else ordered_list).append(t)
+
+            # Current level is > last item's level,
+            # So make last item a parent and append current as child
             else:
-                #print "Previous elements, comparing to those first"
-                remaining_list.insert(0, current)
-                next_elements2, children2 = build_correct(remaining_list, prev_elements)
-                children.extend(children2)
-            next_elements += next_elements2
-        
-        return next_elements, children
-    
-    ordered_list, __ = build_correct(toc_list)
+                last['children'].append(t)
+                parents.append(last)
+                levels.append(current_level)
+            last = t
+
     return ordered_list
 
 
 class TocTreeprocessor(Treeprocessor):
-    
-    # Iterator wrapper to get parent and child all at once
+    def __init__(self, md, config):
+        super(TocTreeprocessor, self).__init__(md)
+
+        self.marker = config["marker"]
+        self.title = config["title"]
+        self.base_level = int(config["baselevel"]) - 1
+        self.slugify = config["slugify"]
+        self.sep = config["separator"]
+        self.use_anchors = parseBoolValue(config["anchorlink"])
+        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]")
+
     def iterparent(self, root):
-        for parent in root.getiterator():
+        ''' Iterator wrapper to get parent and child all at once. '''
+        for parent in root.iter():
             for child in parent:
                 yield parent, child
-    
-    def add_anchor(self, c, elem_id): #@ReservedAssignment
+
+    def replace_marker(self, root, elem):
+        ''' Replace marker with elem. '''
+        for (p, c) in self.iterparent(root):
+            text = ''.join(c.itertext()).strip()
+            if not text:
+                continue
+
+            # 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']:
+                for i in range(len(p)):
+                    if p[i] == c:
+                        p[i] = elem
+                        break
+
+    def set_level(self, elem):
+        ''' Adjust header level according to base level. '''
+        level = int(elem.tag[-1]) + self.base_level
+        if level > 6:
+            level = 6
+        elem.tag = 'h%d' % level
+
+    def add_anchor(self, c, elem_id):  # @ReservedAssignment
         anchor = etree.Element("a")
         anchor.text = c.text
         anchor.attrib["href"] = "#" + elem_id
         anchor.attrib["class"] = "toclink"
         c.text = ""
-        for elem in c.getchildren():
+        for elem in c:
             anchor.append(elem)
             c.remove(elem)
         c.append(anchor)
@@ -106,18 +186,23 @@ class TocTreeprocessor(Treeprocessor):
     def add_permalink(self, c, elem_id):
         permalink = etree.Element("a")
         permalink.text = ("%spara;" % AMP_SUBSTITUTE
-            if self.use_permalinks is True else self.use_permalinks)
+                          if self.use_permalinks is True
+                          else self.use_permalinks)
         permalink.attrib["href"] = "#" + elem_id
         permalink.attrib["class"] = "headerlink"
         permalink.attrib["title"] = "Permanent link"
         c.append(permalink)
-    
-    def build_toc_etree(self, div, toc_list):
+
+    def build_toc_div(self, toc_list):
+        """ Return a string div given a toc list. """
+        div = etree.Element("div")
+        div.attrib["class"] = "toc"
+
         # Add title to the div
-        if self.config["title"]:
+        if self.title:
             header = etree.SubElement(div, "span")
             header.attrib["class"] = "toctitle"
-            header.text = self.config["title"]
+            header.text = self.title
 
         def build_etree_ul(toc_list, parent):
             ul = etree.SubElement(parent, "ul")
@@ -130,115 +215,95 @@ class TocTreeprocessor(Treeprocessor):
                 if item['children']:
                     build_etree_ul(item['children'], li)
             return ul
-        
-        return build_etree_ul(toc_list, div)
-        
-    def run(self, doc):
 
-        div = etree.Element("div")
-        div.attrib["class"] = "toc"
-        header_rgx = re.compile("[Hh][123456]")
-        
-        self.use_anchors = parseBoolValue(self.config["anchorlink"])
-        self.use_permalinks = parseBoolValue(self.config["permalink"], False)
-        if self.use_permalinks is None:
-            self.use_permalinks = self.config["permalink"]
-        
+        build_etree_ul(toc_list, div)
+        prettify = self.markdown.treeprocessors.get('prettify')
+        if prettify:
+            prettify.run(div)
+        return div
+
+    def run(self, doc):
         # Get a list of id attributes
         used_ids = set()
-        for c in doc.getiterator():
-            if "id" in c.attrib:
-                used_ids.add(c.attrib["id"])
-
-        toc_list = []
-        marker_found = False
-        for (p, c) in self.iterparent(doc):
-            text = ''.join(itertext(c)).strip()
-            if not text:
-                continue
+        for el in doc.iter():
+            if "id" in el.attrib:
+                used_ids.add(el.attrib["id"])
 
-            # 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.config["marker"] and \
-               not header_rgx.match(c.tag) and c.tag not in ['pre', 'code']:
-                for i in range(len(p)):
-                    if p[i] == c:
-                        p[i] = div
-                        break
-                marker_found = True
-                            
-            if header_rgx.match(c.tag):
-                
-                # Do not override pre-existing ids 
-                if not "id" in c.attrib:
-                    elem_id = stashedHTML2text(text, self.markdown)
-                    elem_id = unique(self.config["slugify"](elem_id, '-'), used_ids)
-                    c.attrib["id"] = elem_id
-                else:
-                    elem_id = c.attrib["id"]
-
-                tag_level = int(c.tag[-1])
-                
-                toc_list.append({'level': tag_level,
-                    'id': elem_id,
-                    'name': text})
+        toc_tokens = []
+        for el in doc.iter():
+            if self.header_rgx.match(el.tag):
+                self.set_level(el)
+                text = ''.join(el.itertext()).strip()
+
+                # Do not override pre-existing ids
+                if "id" not in el.attrib:
+                    innertext = stashedHTML2text(text, self.markdown)
+                    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
+                })
 
                 if self.use_anchors:
-                    self.add_anchor(c, elem_id)
+                    self.add_anchor(el, el.attrib["id"])
                 if self.use_permalinks:
-                    self.add_permalink(c, elem_id)
-                
-        toc_list_nested = order_toc_list(toc_list)
-        self.build_toc_etree(div, toc_list_nested)
-        prettify = self.markdown.treeprocessors.get('prettify')
-        if prettify: prettify.run(div)
-        if not marker_found:
-            # serialize and attach to markdown instance.
-            toc = self.markdown.serializer(div)
-            for pp in self.markdown.postprocessors.values():
-                toc = pp.run(toc)
-            self.markdown.toc = toc
+                    self.add_permalink(el, el.attrib["id"])
+
+        div = self.build_toc_div(nest_toc_tokens(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 = pp.run(toc)
+        self.markdown.toc = toc
 
 
 class TocExtension(Extension):
-    
+
     TreeProcessorClass = TocTreeprocessor
-    
+
     def __init__(self, *args, **kwargs):
-        self.config = { 
-            "marker" : ["[TOC]", 
-                "Text to find and replace with Table of Contents - "
-                "Defaults to \"[TOC]\""],
-            "slugify" : [slugify,
-                "Function to generate anchors based on header text - "
-                "Defaults to the headerid ext's slugify function."],
-            "title" : [None,
-                "Title to insert into TOC <div> - "
-                "Defaults to None"],
-            "anchorlink" : [0,
-                "1 if header should be a self link - "
-                "Defaults to 0"],
-            "permalink" : [0,
-                "1 or link text if a Sphinx-style permalink should be added - "
-                "Defaults to 0"]
+        self.config = {
+            "marker": ['[TOC]',
+                       'Text to find and replace with Table of Contents - '
+                       'Set to an empty string to disable. Defaults to "[TOC]"'],
+            "title": ["",
+                      "Title to insert into TOC <div> - "
+                      "Defaults to an empty string"],
+            "anchorlink": [False,
+                           "True if header should be a self link - "
+                           "Defaults to False"],
+            "permalink": [0,
+                          "True or link text if a Sphinx-style permalink should "
+                          "be added - Defaults to False"],
+            "baselevel": ['1', 'Base level for headers.'],
+            "slugify": [slugify,
+                        "Function to generate anchors based on header text - "
+                        "Defaults to the headerid ext's slugify function."],
+            'separator': ['-', 'Word separator. Defaults to "-".']
         }
 
         super(TocExtension, self).__init__(*args, **kwargs)
 
     def extendMarkdown(self, md, md_globals):
-        tocext = self.TreeProcessorClass(md)
-        tocext.config = self.getConfigs()
+        md.registerExtension(self)
+        self.md = md
+        self.reset()
+        tocext = self.TreeProcessorClass(md, self.getConfigs())
         # Headerid ext is set to '>prettify'. With this set to '_end',
-        # it should always come after headerid ext (and honor ids assinged 
-        # by the header id extension) if both are used. Same goes for 
+        # it should always come after headerid ext (and honor ids assinged
+        # 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")
 
+    def reset(self):
+        self.md.toc = ''
+
 
 def makeExtension(*args, **kwargs):
     return TocExtension(*args, **kwargs)
index 64377cf3e237e5c9aa226f8bae38af083a1d9fe6..94e1b6794853a534a68786236ea28df54a2b5298 100644 (file)
@@ -4,14 +4,14 @@ WikiLinks Extension for Python-Markdown
 
 Converts [[WikiLinks]] to relative links.
 
-See <https://pythonhosted.org/Markdown/extensions/wikilinks.html> 
+See <https://pythonhosted.org/Markdown/extensions/wikilinks.html>
 for documentation.
 
 Original code Copyright [Waylan Limberg](http://achinghead.com/).
 
 All changes Copyright The Python Markdown Project
 
-License: [BSD](http://www.opensource.org/licenses/bsd-license.php) 
+License: [BSD](http://www.opensource.org/licenses/bsd-license.php)
 
 '''
 
@@ -22,27 +22,28 @@ from ..inlinepatterns import Pattern
 from ..util import etree
 import re
 
+
 def build_url(label, base, end):
     """ Build a url from the label, a base, and an end. """
     clean_label = re.sub(r'([ ]+_)|(_[ ]+)|([ ]+)', '_', label)
-    return '%s%s%s'% (base, clean_label, end)
+    return '%s%s%s' % (base, clean_label, end)
 
 
 class WikiLinkExtension(Extension):
 
-    def __init__ (self, *args, **kwargs):
+    def __init__(self, *args, **kwargs):
         self.config = {
-            'base_url' : ['/', 'String to append to beginning or URL.'],
-            'end_url' : ['/', 'String to append to end of URL.'],
-            'html_class' : ['wikilink', 'CSS hook. Leave blank for none.'],
-            'build_url' : [build_url, 'Callable formats URL from label.'],
+            'base_url': ['/', 'String to append to beginning or URL.'],
+            'end_url': ['/', 'String to append to end of URL.'],
+            'html_class': ['wikilink', 'CSS hook. Leave blank for none.'],
+            'build_url': [build_url, 'Callable formats URL from label.'],
         }
-        
+
         super(WikiLinkExtension, self).__init__(*args, **kwargs)
-    
+
     def extendMarkdown(self, md, md_globals):
         self.md = md
-    
+
         # append to end of inline patterns
         WIKILINK_RE = r'\[\[([\w0-9_ -]+)\]\]'
         wikilinkPattern = WikiLinks(WIKILINK_RE, self.getConfigs())
@@ -54,14 +55,14 @@ class WikiLinks(Pattern):
     def __init__(self, pattern, config):
         super(WikiLinks, self).__init__(pattern)
         self.config = config
-  
+
     def handleMatch(self, m):
         if m.group(2).strip():
             base_url, end_url, html_class = self._getMeta()
             label = m.group(2).strip()
             url = self.config['build_url'](label, base_url, end_url)
             a = etree.Element('a')
-            a.text = label 
+            a.text = label
             a.set('href', url)
             if html_class:
                 a.set('class', html_class)
@@ -82,7 +83,7 @@ class WikiLinks(Pattern):
             if 'wiki_html_class' in self.md.Meta:
                 html_class = self.md.Meta['wiki_html_class'][0]
         return base_url, end_url, html_class
-    
 
-def makeExtension(*args, **kwargs) :
+
+def makeExtension(*args, **kwargs):
     return WikiLinkExtension(*args, **kwargs)
index eaf4040a8e0772462eb5e08f149efb384adb50c7..95d358d7156ccd341f5c5f33a2a48d1255d122dd 100644 (file)
@@ -46,13 +46,13 @@ from __future__ import unicode_literals
 from . import util
 from . import odict
 import re
-try: #pragma: no cover
+try:  # pragma: no cover
     from urllib.parse import urlparse, urlunparse
-except ImportError: #pragma: no cover
+except ImportError:  # pragma: no cover
     from urlparse import urlparse, urlunparse
-try: #pragma: no cover
+try:  # pragma: no cover
     from html import entities
-except ImportError: #pragma: no cover
+except ImportError:  # pragma: no cover
     import htmlentitydefs as entities
 
 
@@ -64,10 +64,12 @@ def build_inlinepatterns(md_instance, **kwargs):
     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["short_reference"] = \
-            ReferencePattern(SHORT_REF_RE, md_instance)
+    inlinePatterns["image_reference"] = ImageReferencePattern(
+        IMAGE_REFERENCE_RE, md_instance
+    )
+    inlinePatterns["short_reference"] = ReferencePattern(
+        SHORT_REF_RE, md_instance
+    )
     inlinePatterns["autolink"] = AutolinkPattern(AUTOLINK_RE, md_instance)
     inlinePatterns["automail"] = AutomailPattern(AUTOMAIL_RE, md_instance)
     inlinePatterns["linebreak"] = SubstituteTagPattern(LINE_BREAK_RE, 'br')
@@ -75,7 +77,8 @@ def build_inlinepatterns(md_instance, **kwargs):
         inlinePatterns["html"] = HtmlPattern(HTML_RE, md_instance)
     inlinePatterns["entity"] = HtmlPattern(ENTITY_RE, md_instance)
     inlinePatterns["not_strong"] = SimpleTextPattern(NOT_STRONG_RE)
-    inlinePatterns["strong_em"] = DoubleTagPattern(STRONG_EM_RE, 'strong,em')
+    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:
@@ -90,46 +93,84 @@ The actual regular expressions for patterns
 """
 
 NOBRACKET = r'[^\]\[]*'
-BRK = ( r'\[('
-        + (NOBRACKET + r'(\[')*6
-        + (NOBRACKET+ r'\])*')*6
-        + NOBRACKET + r')\]' )
+BRK = (
+    r'\[(' +
+    (NOBRACKET + r'(\[')*6 +
+    (NOBRACKET + r'\])*')*6 +
+    NOBRACKET + r')\]'
+)
 NOIMG = r'(?<!\!)'
 
-BACKTICK_RE = r'(?<!\\)(`+)(.+?)(?<!`)\2(?!`)' # `e=f()` or ``e=f("`")``
-ESCAPE_RE = r'\\(.)'                             # \<
-EMPHASIS_RE = r'(\*)([^\*]+)\2'                    # *emphasis*
-STRONG_RE = r'(\*{2}|_{2})(.+?)\2'                      # **strong**
-STRONG_EM_RE = r'(\*{3}|_{3})(.+?)\2'            # ***strong***
-SMART_EMPHASIS_RE = r'(?<!\w)(_)(?!_)(.+?)(?<!_)\2(?!\w)'  # _smart_emphasis_
-EMPHASIS_2_RE = r'(_)(.+?)\2'                 # _emphasis_
-LINK_RE = NOIMG + BRK + \
-r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)'''
+# `e=f()` or ``e=f("`")``
+BACKTICK_RE = r'(?<!\\)(`+)(.+?)(?<!`)\2(?!`)'
+
+# \<
+ESCAPE_RE = r'\\(.)'
+
+# *emphasis*
+EMPHASIS_RE = r'(\*)([^\*]+)\2'
+
+# **strong**
+STRONG_RE = r'(\*{2}|_{2})(.+?)\2'
+
+# ***strongem*** or ***em*strong**
+EM_STRONG_RE = r'(\*|_)\2{2}(.+?)\2(.*?)\2{2}'
+
+# ***strong**em*
+STRONG_EM_RE = r'(\*|_)\2{2}(.+?)\2{2}(.*?)\2'
+
+# _smart_emphasis_
+SMART_EMPHASIS_RE = r'(?<!\w)(_)(?!_)(.+?)(?<!_)\2(?!\w)'
+
+# _emphasis_
+EMPHASIS_2_RE = r'(_)(.+?)\2'
+
 # [text](url) or [text](<url>) or [text](url "title")
+LINK_RE = NOIMG + BRK + \
+    r'''\(\s*(<.*?>|((?:(?:\(.*?\))|[^\(\)]))*?)\s*((['"])(.*?)\12\s*)?\)'''
 
-IMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^")]+"[^"]*"|[^\)]*))\)'
 # ![alttxt](http://x.com/) or ![alttxt](<http://x.com/>)
-REFERENCE_RE = NOIMG + BRK+ r'\s?\[([^\]]*)\]'           # [Google][3]
-SHORT_REF_RE = NOIMG + r'\[([^\]]+)\]'                   # [Google]
-IMAGE_REFERENCE_RE = r'\!' + BRK + '\s?\[([^\]]*)\]' # ![alt text][2]
-NOT_STRONG_RE = r'((^| )(\*|_)( |$))'                        # stand-alone * or _
-AUTOLINK_RE = r'<((?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^>]*)>' # <http://www.123.com>
-AUTOMAIL_RE = r'<([^> \!]*@[^> ]*)>'               # <me@example.com>
+IMAGE_LINK_RE = r'\!' + BRK + r'\s*\((<.*?>|([^")]+"[^"]*"|[^\)]*))\)'
+
+# [Google][3]
+REFERENCE_RE = NOIMG + BRK + r'\s?\[([^\]]*)\]'
+
+# [Google]
+SHORT_REF_RE = NOIMG + r'\[([^\]]+)\]'
+
+# ![alt text][2]
+IMAGE_REFERENCE_RE = r'\!' + BRK + '\s?\[([^\]]*)\]'
+
+# stand-alone * or _
+NOT_STRONG_RE = r'((^| )(\*|_)( |$))'
+
+# <http://www.123.com>
+AUTOLINK_RE = r'<((?:[Ff]|[Hh][Tt])[Tt][Pp][Ss]?://[^>]*)>'
+
+# <me@example.com>
+AUTOMAIL_RE = r'<([^> \!]*@[^> ]*)>'
+
+# <...>
+HTML_RE = r'(\<([a-zA-Z/][^\>]*?|\!--.*?--)\>)'
 
-HTML_RE = r'(\<([a-zA-Z/][^\>]*?|\!--.*?--)\>)'               # <...>
-ENTITY_RE = r'(&[\#a-zA-Z0-9]*;)'               # &amp;
-LINE_BREAK_RE = r'  \n'                     # two spaces at end of line
+# &amp;
+ENTITY_RE = r'(&[\#a-zA-Z0-9]*;)'
+
+# two spaces at end of line
+LINE_BREAK_RE = r'  \n'
 
 
 def dequote(string):
     """Remove quotes from around a string."""
-    if ( ( string.startswith('"') and string.endswith('"'))
-         or (string.startswith("'") and string.endswith("'")) ):
+    if ((string.startswith('"') and string.endswith('"')) or
+       (string.startswith("'") and string.endswith("'"))):
         return string[1:-1]
     else:
         return string
 
-ATTR_RE = re.compile("\{@([^\}]*)=([^\}]*)}") # {@id=123}
+
+ATTR_RE = re.compile("\{@([^\}]*)=([^\}]*)}")  # {@id=123}
+
 
 def handleAttributes(text, parent):
     """Set values of an element based on attribute definitions ({@id=123})."""
@@ -143,6 +184,7 @@ The pattern classes
 -----------------------------------------------------------------------------
 """
 
+
 class Pattern(object):
     """Base class that inline patterns subclass. """
 
@@ -156,7 +198,7 @@ class Pattern(object):
 
         """
         self.pattern = pattern
-        self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern, 
+        self.compiled_re = re.compile("^(.*?)%s(.*?)$" % pattern,
                                       re.DOTALL | re.UNICODE)
 
         # Api for Markdown to pass safe_mode into instance
@@ -178,7 +220,7 @@ class Pattern(object):
         * m: A re match object containing a match of the pattern.
 
         """
-        pass #pragma: no cover
+        pass  # pragma: no cover
 
     def type(self):
         """ Return class name, to define pattern type """
@@ -188,9 +230,10 @@ class Pattern(object):
         """ Return unescaped text given text with an inline placeholder. """
         try:
             stash = self.markdown.treeprocessors['inline'].stashed_nodes
-        except KeyError: #pragma: no cover
+        except KeyError:  # pragma: no cover
             return text
-        def itertext(el): #pragma: no cover
+
+        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:
@@ -202,6 +245,7 @@ class Pattern(object):
                     yield s
                 if e.tail:
                     yield e.tail
+
         def get_stash(m):
             id = m.group(1)
             if id in stash:
@@ -210,7 +254,7 @@ class Pattern(object):
                     return value
                 else:
                     # An etree Element - return text content only
-                    return ''.join(itertext(value)) 
+                    return ''.join(itertext(value))
         return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
 
 
@@ -228,7 +272,7 @@ class EscapePattern(Pattern):
         if char in self.markdown.ESCAPED_CHARS:
             return '%s%s%s' % (util.STX, ord(char), util.ETX)
         else:
-            return None 
+            return None
 
 
 class SimpleTagPattern(Pattern):
@@ -237,7 +281,7 @@ class SimpleTagPattern(Pattern):
     of a Pattern.
 
     """
-    def __init__ (self, pattern, tag):
+    def __init__(self, pattern, tag):
         Pattern.__init__(self, pattern)
         self.tag = tag
 
@@ -249,13 +293,13 @@ class SimpleTagPattern(Pattern):
 
 class SubstituteTagPattern(SimpleTagPattern):
     """ Return an element of type `tag` with no children. """
-    def handleMatch (self, m):
+    def handleMatch(self, m):
         return util.etree.Element(self.tag)
 
 
 class BacktickPattern(Pattern):
     """ Return a `<code>` element containing the matching text. """
-    def __init__ (self, pattern):
+    def __init__(self, pattern):
         Pattern.__init__(self, pattern)
         self.tag = "code"
 
@@ -276,12 +320,14 @@ class DoubleTagPattern(SimpleTagPattern):
         el1 = util.etree.Element(tag1)
         el2 = util.etree.SubElement(el1, tag2)
         el2.text = m.group(3)
+        if len(m.groups()) == 5:
+            el2.tail = m.group(4)
         return el1
 
 
 class HtmlPattern(Pattern):
     """ Store raw inline html and return a placeholder. """
-    def handleMatch (self, m):
+    def handleMatch(self, m):
         rawhtml = self.unescape(m.group(2))
         place_holder = self.markdown.htmlStash.store(rawhtml)
         return place_holder
@@ -290,8 +336,9 @@ class HtmlPattern(Pattern):
         """ Return unescaped text given text with an inline placeholder. """
         try:
             stash = self.markdown.treeprocessors['inline'].stashed_nodes
-        except KeyError: #pragma: no cover
+        except KeyError:  # pragma: no cover
             return text
+
         def get_stash(m):
             id = m.group(1)
             value = stash.get(id)
@@ -300,7 +347,7 @@ class HtmlPattern(Pattern):
                     return self.markdown.serializer(value)
                 except:
                     return '\%s' % value
-            
+
         return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
 
 
@@ -320,7 +367,7 @@ class LinkPattern(Pattern):
             el.set("href", "")
 
         if title:
-            title = dequote(self.unescape(title)) 
+            title = dequote(self.unescape(title))
             el.set("title", title)
         return el
 
@@ -344,31 +391,33 @@ class LinkPattern(Pattern):
         if not self.markdown.safeMode:
             # Return immediately bipassing parsing.
             return url
-        
+
         try:
             scheme, netloc, path, params, query, fragment = url = urlparse(url)
-        except ValueError: #pragma: no cover
+        except ValueError:  # pragma: no cover
             # Bad url - so bad it couldn't be parsed.
             return ''
-        
+
         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 netloc == '' and scheme not in locless_schemes: #pragma: no cover
+
+        if netloc == '' and scheme not in locless_schemes:  # pragma: no cover
             # This should not happen. Treat as suspect.
             return ''
 
         for part in url[2:]:
             if ":" in part:
-                # A colon in "path", "parameters", "query" or "fragment" is suspect.
+                # A colon in "path", "parameters", "query"
+                # or "fragment" is suspect.
                 return ''
 
         # Url passes all tests. Return url as-is.
         return urlunparse(url)
 
+
 class ImagePattern(LinkPattern):
     """ Return a img element from the given match. """
     def handleMatch(self, m):
@@ -392,6 +441,7 @@ class ImagePattern(LinkPattern):
         el.set('alt', self.unescape(truealt))
         return el
 
+
 class ReferencePattern(LinkPattern):
     """ Match to a stored reference and return link element. """
 
@@ -409,7 +459,7 @@ class ReferencePattern(LinkPattern):
 
         # Clean up linebreaks in id
         id = self.NEWLINE_CLEANUP_RE.sub(' ', id)
-        if not id in self.markdown.references: # ignore undefined refs
+        if id not in self.markdown.references:  # ignore undefined refs
             return None
         href, title = self.markdown.references[id]
 
@@ -450,6 +500,7 @@ class AutolinkPattern(Pattern):
         el.text = util.AtomicString(m.group(2))
         return el
 
+
 class AutomailPattern(Pattern):
     """
     Return a mailto link Element given an automail link (`<foo@example.com>`).
@@ -476,4 +527,3 @@ class AutomailPattern(Pattern):
                           ord(letter) for letter in mailto])
         el.set('href', mailto)
         return el
-
index b158e06a24a6e619a89a3e9111baf70d1c7cfef4..584ad7c173f45cba6a41dc491d9a3f0a4bd0ae04 100644 (file)
@@ -1,13 +1,13 @@
 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.
 
     """
@@ -82,11 +82,11 @@ class OrderedDict(dict):
         for key in self.keyOrder:
             yield self[key]
 
-    if util.PY3: #pragma: no cover
+    if util.PY3:  # pragma: no cover
         items = _iteritems
         keys = _iterkeys
         values = _itervalues
-    else: #pragma: no cover
+    else:  # pragma: no cover
         iteritems = _iteritems
         iterkeys = _iterkeys
         itervalues = _itervalues
@@ -133,7 +133,9 @@ class OrderedDict(dict):
         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()])
+        return '{%s}' % ', '.join(
+            ['%r: %r' % (k, v) for k, v in self._iteritems()]
+        )
 
     def clear(self):
         super(OrderedDict, self).clear()
index 7b568adac55432fd7699abb4870ec65fac4b1e94..2d4dcb589ee0cfb1f580b55c4a6eab16f9d9f339 100644 (file)
@@ -42,7 +42,7 @@ class Postprocessor(util.Processor):
         (possibly modified) string.
 
         """
-        pass #pragma: no cover
+        pass  # pragma: no cover
 
 
 class RawHtmlPostprocessor(Postprocessor):
@@ -51,7 +51,7 @@ class RawHtmlPostprocessor(Postprocessor):
     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]
+            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)
@@ -59,12 +59,16 @@ class RawHtmlPostprocessor(Postprocessor):
                     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)),
-                            html + "\n")
-            text =  text.replace(self.markdown.htmlStash.get_placeholder(i), 
-                                 html)
+            if (self.isblocklevel(html) and
+               (safe or not self.markdown.safeMode)):
+                text = text.replace(
+                    "<p>%s</p>" %
+                    (self.markdown.htmlStash.get_placeholder(i)),
+                    html + "\n"
+                )
+            text = text.replace(
+                self.markdown.htmlStash.get_placeholder(i), html
+            )
         return text
 
     def escape(self, html):
@@ -88,7 +92,7 @@ class AndSubstitutePostprocessor(Postprocessor):
     """ Restore valid entities """
 
     def run(self, text):
-        text =  text.replace(util.AMP_SUBSTITUTE, "&")
+        text = text.replace(util.AMP_SUBSTITUTE, "&")
         return text
 
 
index 4a1fac5f696af66179a02751793cc524817901cd..7fd38d331fb5685a4c06f23e646c9d4d40e69b8b 100644 (file)
@@ -41,7 +41,7 @@ class Preprocessor(util.Processor):
         the (possibly modified) list of lines.
 
         """
-        pass #pragma: no cover
+        pass  # pragma: no cover
 
 
 class NormalizeWhitespace(Preprocessor):
@@ -61,13 +61,14 @@ class HtmlBlockPreprocessor(Preprocessor):
 
     right_tag_patterns = ["</%s>", "%s>"]
     attrs_pattern = r"""
-        \s+(?P<attr>[^>"'/= ]+)=(?P<q>['"])(?P<value>.*?)(?P=q)   # attr="value"
-        |                                                         # OR
-        \s+(?P<attr1>[^>"'/= ]+)=(?P<value1>[^> ]+)               # attr=value
-        |                                                         # OR
-        \s+(?P<attr2>[^>"'/= ]+)                                  # attr
+        \s+(?P<attr>[^>"'/= ]+)=(?P<q>['"])(?P<value>.*?)(?P=q) # attr="value"
+        |                                                       # OR
+        \s+(?P<attr1>[^>"'/= ]+)=(?P<value1>[^> ]+)             # attr=value
+        |                                                       # OR
+        \s+(?P<attr2>[^>"'/= ]+)                                # attr
         """
-    left_tag_pattern = r'^\<(?P<tag>[^> ]+)(?P<attrs>(%s)*)\s*\/?\>?' % attrs_pattern
+    left_tag_pattern = r'^\<(?P<tag>[^> ]+)(?P<attrs>(%s)*)\s*\/?\>?' % \
+                       attrs_pattern
     attrs_re = re.compile(attrs_pattern, re.VERBOSE)
     left_tag_re = re.compile(left_tag_pattern, re.VERBOSE)
     markdown_in_raw = False
@@ -87,7 +88,9 @@ class HtmlBlockPreprocessor(Preprocessor):
                             attrs[ma.group('attr').strip()] = ""
                     elif ma.group('attr1'):
                         if ma.group('value1'):
-                            attrs[ma.group('attr1').strip()] = ma.group('value1')
+                            attrs[ma.group('attr1').strip()] = ma.group(
+                                'value1'
+                            )
                         else:
                             attrs[ma.group('attr1').strip()] = ""
                     elif ma.group('attr2'):
@@ -118,20 +121,21 @@ class HtmlBlockPreprocessor(Preprocessor):
     def _get_right_tag(self, left_tag, left_index, block):
         for p in self.right_tag_patterns:
             tag = p % left_tag
-            i = self._recursive_tagfind("<%s" % left_tag, tag, left_index, block)
+            i = self._recursive_tagfind(
+                "<%s" % left_tag, tag, left_index, block
+            )
             if i > 2:
                 return tag.lstrip("<").rstrip(">"), i
         return block.rstrip()[-left_index:-1].lower(), len(block)
 
     def _equal_tags(self, left_tag, right_tag):
-        if left_tag[0] in ['?', '@', '%']: # handle PHP, etc.
+        if left_tag[0] in ['?', '@', '%']:  # handle PHP, etc.
             return True
         if ("/" + left_tag) == right_tag:
             return True
         if (right_tag == "--" and left_tag == "--"):
             return True
-        elif left_tag == right_tag[1:] \
-            and right_tag[0] == "/":
+        elif left_tag == right_tag[1:] and right_tag[0] == "/":
             return True
         else:
             return False
@@ -174,9 +178,10 @@ class HtmlBlockPreprocessor(Preprocessor):
                 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 + 1]))
-                    del items[i:right_listindex + 1]
+                        items[i:right_listindex + offset]))
+                    del items[i:right_listindex + offset]
                     items.insert(i, placeholder)
         return items
 
@@ -187,7 +192,7 @@ class HtmlBlockPreprocessor(Preprocessor):
         items = []
         left_tag = ''
         right_tag = ''
-        in_tag = False # flag
+        in_tag = False  # flag
 
         while text:
             block = text[0]
@@ -203,7 +208,7 @@ class HtmlBlockPreprocessor(Preprocessor):
 
                     if block[1:4] == "!--":
                         # is a comment block
-                        left_tag, left_index, attrs  = "--", 2, {}
+                        left_tag, left_index, attrs = "--", 2, {}
                     else:
                         left_tag, left_index, attrs = self._get_left_tag(block)
                     right_tag, data_index = self._get_right_tag(left_tag,
@@ -211,14 +216,11 @@ class HtmlBlockPreprocessor(Preprocessor):
                                                                 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 (util.isBlockLevel(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 (util.isBlockLevel(left_tag) or block[1] in ["!", "?", "@", "%"]):
                         new_blocks.append(block)
                         continue
 
@@ -239,14 +241,14 @@ class HtmlBlockPreprocessor(Preprocessor):
                         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 == "--"):
+                        if (not self._equal_tags(left_tag, right_tag)) and \
+                           (util.isBlockLevel(left_tag) or left_tag == "--"):
                             items.append(block.strip())
                             in_tag = True
                         else:
                             new_blocks.append(
-                            self.markdown.htmlStash.store(block.strip()))
-
+                                self.markdown.htmlStash.store(block.strip())
+                            )
                         continue
 
                 else:
@@ -316,11 +318,13 @@ class ReferencePreprocessor(Preprocessor):
     """ Remove reference definitions from text and store for later use. """
 
     TITLE = r'[ ]*(\"(.*)\"|\'(.*)\'|\((.*)\))[ ]*'
-    RE = re.compile(r'^[ ]{0,3}\[([^\]]*)\]:\s*([^ ]*)[ ]*(%s)?$' % TITLE, re.DOTALL)
+    RE = re.compile(
+        r'^[ ]{0,3}\[([^\]]*)\]:\s*([^ ]*)[ ]*(%s)?$' % TITLE, re.DOTALL
+    )
     TITLE_RE = re.compile(r'^%s$' % TITLE)
 
-    def run (self, lines):
-        new_text = [];
+    def run(self, lines):
+        new_text = []
         while lines:
             line = lines.pop(0)
             m = self.RE.match(line)
@@ -338,4 +342,4 @@ class ReferencePreprocessor(Preprocessor):
             else:
                 new_text.append(line)
 
-        return new_text #+ "\n"
+        return new_text  # + "\n"
index f53ae31db8457e518a0bcbb5e7496b891e4c02e1..1e8d9dd288f00ea0d5e001a22eb5004001cbcbf5 100644 (file)
@@ -42,9 +42,9 @@ from __future__ import unicode_literals
 from . import util
 ElementTree = util.etree.ElementTree
 QName = util.etree.QName
-if hasattr(util.etree, 'test_comment'): #pragma: no cover
+if hasattr(util.etree, 'test_comment'):  # pragma: no cover
     Comment = util.etree.test_comment
-else: #pragma: no cover
+else:  # pragma: no cover
     Comment = util.etree.Comment
 PI = util.etree.PI
 ProcessingInstruction = util.etree.ProcessingInstruction
@@ -56,7 +56,7 @@ HTML_EMPTY = ("area", "base", "basefont", "br", "col", "frame", "hr",
 
 try:
     HTML_EMPTY = set(HTML_EMPTY)
-except NameError: #pragma: no cover
+except NameError:  # pragma: no cover
     pass
 
 _namespace_map = {
@@ -73,17 +73,19 @@ _namespace_map = {
 }
 
 
-def _raise_serialization_error(text): #pragma: no cover
+def _raise_serialization_error(text):  # pragma: no cover
     raise TypeError(
         "cannot serialize %r (type %s)" % (text, type(text).__name__)
         )
 
+
 def _encode(text, encoding):
     try:
         return text.encode(encoding, "xmlcharrefreplace")
-    except (TypeError, AttributeError): #pragma: no cover
+    except (TypeError, AttributeError):  # pragma: no cover
         _raise_serialization_error(text)
 
+
 def _escape_cdata(text):
     # escape character data
     try:
@@ -97,7 +99,7 @@ def _escape_cdata(text):
         if ">" in text:
             text = text.replace(">", "&gt;")
         return text
-    except (TypeError, AttributeError): #pragma: no cover
+    except (TypeError, AttributeError):  # pragma: no cover
         _raise_serialization_error(text)
 
 
@@ -115,9 +117,10 @@ def _escape_attrib(text):
         if "\n" in text:
             text = text.replace("\n", "&#10;")
         return text
-    except (TypeError, AttributeError): #pragma: no cover
+    except (TypeError, AttributeError):  # pragma: no cover
         _raise_serialization_error(text)
 
+
 def _escape_attrib_html(text):
     # escape attribute value
     try:
@@ -130,7 +133,7 @@ def _escape_attrib_html(text):
         if "\"" in text:
             text = text.replace("\"", "&quot;")
         return text
-    except (TypeError, AttributeError): #pragma: no cover
+    except (TypeError, AttributeError):  # pragma: no cover
         _raise_serialization_error(text)
 
 
@@ -152,7 +155,7 @@ def _serialize_html(write, elem, qnames, namespaces, format):
             write("<" + tag)
             items = elem.items()
             if items or namespaces:
-                items = sorted(items) # lexical order
+                items = sorted(items)  # lexical order
                 for k, v in items:
                     if isinstance(k, QName):
                         k = k.text
@@ -167,7 +170,7 @@ def _serialize_html(write, elem, qnames, namespaces, format):
                         write(" %s=\"%s\"" % (qnames[k], v))
                 if namespaces:
                     items = namespaces.items()
-                    items.sort(key=lambda x: x[1]) # sort on prefix
+                    items.sort(key=lambda x: x[1])  # sort on prefix
                     for v, k in items:
                         if k:
                             k = ":" + k
@@ -188,6 +191,7 @@ def _serialize_html(write, elem, qnames, namespaces, format):
     if elem.tail:
         write(_escape_cdata(elem.tail))
 
+
 def _write_html(root,
                 encoding=None,
                 default_namespace=None,
@@ -232,7 +236,7 @@ def _namespaces(elem, default_namespace=None):
                 if prefix:
                     qnames[qname] = "%s:%s" % (prefix, tag)
                 else:
-                    qnames[qname] = tag # default element
+                    qnames[qname] = tag  # default element
             else:
                 if default_namespace:
                     raise ValueError(
@@ -240,14 +244,14 @@ def _namespaces(elem, default_namespace=None):
                         "default_namespace option"
                         )
                 qnames[qname] = qname
-        except TypeError: #pragma: no cover
+        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
+        iterate = elem.getiterator  # cET compatibility
     for elem in iterate():
         tag = elem.tag
         if isinstance(tag, QName) and tag.text not in qnames:
@@ -269,8 +273,10 @@ def _namespaces(elem, default_namespace=None):
             add_qname(text.text)
     return qnames, namespaces
 
+
 def to_html_string(element):
     return _write_html(ElementTree(element).getroot(), format="html")
 
+
 def to_xhtml_string(element):
     return _write_html(ElementTree(element).getroot(), format="xhtml")
index a82141ada2c76c040d3e612be8cb3eb3d02688e0..d06f192885f18ea3154025cb7b2c89841500f446 100644 (file)
@@ -34,11 +34,11 @@ class Treeprocessor(util.Processor):
     def run(self, root):
         """
         Subclasses of Treeprocessor should implement a `run` method, which
-        takes a root ElementTree. This method can return another ElementTree 
-        object, and the existing root ElementTree will be replaced, or it can 
+        takes a root ElementTree. This method can return another ElementTree
+        object, and the existing root ElementTree will be replaced, or it can
         modify the current tree and return None.
         """
-        pass #pragma: no cover
+        pass  # pragma: no cover
 
 
 class InlineProcessor(Treeprocessor):
@@ -71,7 +71,7 @@ class InlineProcessor(Treeprocessor):
         * index: index, from which we start search
 
         Returns: placeholder id and string index, after the found placeholder.
-        
+
         """
         m = self.__placeholder_re.search(data, index)
         if m:
@@ -129,11 +129,10 @@ class InlineProcessor(Treeprocessor):
             text = subnode.tail
             subnode.tail = None
 
-        childResult = self.__processPlaceholders(text, subnode)
+        childResult = self.__processPlaceholders(text, subnode, isText)
 
         if not isText and node is not subnode:
-            pos = list(node).index(subnode)
-            node.remove(subnode)
+            pos = list(node).index(subnode) + 1
         else:
             pos = 0
 
@@ -141,7 +140,7 @@ class InlineProcessor(Treeprocessor):
         for newChild in childResult:
             node.insert(pos, newChild)
 
-    def __processPlaceholders(self, data, parent):
+    def __processPlaceholders(self, data, parent, isText=True):
         """
         Process string with placeholders and generate ElementTree tree.
 
@@ -151,7 +150,7 @@ class InlineProcessor(Treeprocessor):
         * parent: Element, which contains processing inline data
 
         Returns: list with ElementTree elements with applied inline patterns.
-        
+
         """
         def linkText(text):
             if text:
@@ -160,6 +159,11 @@ class InlineProcessor(Treeprocessor):
                         result[-1].tail += text
                     else:
                         result[-1].tail = text
+                elif not isText:
+                    if parent.tail:
+                        parent.tail += text
+                    else:
+                        parent.tail = text
                 else:
                     if parent.text:
                         parent.text += text
@@ -179,15 +183,17 @@ class InlineProcessor(Treeprocessor):
                         text = data[strartIndex:index]
                         linkText(text)
 
-                    if not isString(node): # it's Element
+                    if not isString(node):  # it's Element
                         for child in [node] + list(node):
                             if child.tail:
                                 if child.tail.strip():
-                                    self.__processElementText(node, child,False)
+                                    self.__processElementText(
+                                        node, child, False
+                                    )
                             if child.text:
                                 if child.text.strip():
                                     self.__processElementText(child, child)
-                    else: # it's just a string
+                    else:  # it's just a string
                         linkText(node)
                         strartIndex = phEndIndex
                         continue
@@ -195,7 +201,7 @@ class InlineProcessor(Treeprocessor):
                     strartIndex = phEndIndex
                     result.append(node)
 
-                else: # wrong placeholder
+                else:  # wrong placeholder
                     end = index + len(self.__placeholder_prefix)
                     linkText(data[strartIndex:end])
                     strartIndex = end
@@ -240,12 +246,14 @@ class InlineProcessor(Treeprocessor):
                 # We need to process current node too
                 for child in [node] + list(node):
                     if not isString(node):
-                        if child.text: 
-                            child.text = self.__handleInline(child.text,
-                                                            patternIndex + 1)
+                        if child.text:
+                            child.text = self.__handleInline(
+                                child.text, patternIndex + 1
+                            )
                         if child.tail:
-                            child.tail = self.__handleInline(child.tail,
-                                                            patternIndex)
+                            child.tail = self.__handleInline(
+                                child.tail, patternIndex
+                            )
 
         placeholder = self.__stashNode(node, pattern.type())
 
@@ -258,8 +266,8 @@ class InlineProcessor(Treeprocessor):
 
         Iterate over ElementTree, find elements with inline tag, apply inline
         patterns and append newly created Elements to tree.  If you don't
-        want to process your data with inline paterns, instead of normal string,
-        use subclass AtomicString:
+        want to process your data with inline paterns, instead of normal
+        string, use subclass AtomicString:
 
             node.text = markdown.AtomicString("This will not be processed.")
 
@@ -278,21 +286,23 @@ class InlineProcessor(Treeprocessor):
             currElement = stack.pop()
             insertQueue = []
             for child in currElement:
-                if child.text and not isinstance(child.text, util.AtomicString):
+                if child.text and not isinstance(
+                    child.text, util.AtomicString
+                ):
                     text = child.text
                     child.text = None
-                    lst = self.__processPlaceholders(self.__handleInline(
-                                                    text), child)
+                    lst = self.__processPlaceholders(
+                        self.__handleInline(text), child
+                    )
                     stack += lst
                     insertQueue.append((child, lst))
                 if child.tail:
                     tail = self.__handleInline(child.tail)
                     dumby = util.etree.Element('d')
-                    tailResult = self.__processPlaceholders(tail, dumby)
-                    if dumby.text:
-                        child.tail = dumby.text
-                    else:
-                        child.tail = None
+                    child.tail = None
+                    tailResult = self.__processPlaceholders(tail, dumby, False)
+                    if dumby.tail:
+                        child.tail = dumby.tail
                     pos = list(currElement).index(child) + 1
                     tailResult.reverse()
                     for newChild in tailResult:
@@ -303,21 +313,21 @@ class InlineProcessor(Treeprocessor):
             for element, lst in insertQueue:
                 if self.markdown.enable_attributes:
                     if element.text and isString(element.text):
-                        element.text = \
-                            inlinepatterns.handleAttributes(element.text, 
-                                                                    element)
+                        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)
+                            newChild.tail = inlinepatterns.handleAttributes(
+                                newChild.tail, element
+                            )
                         if newChild.text and isString(newChild.text):
-                            newChild.text = \
-                                inlinepatterns.handleAttributes(newChild.text,
-                                                                    newChild)
+                            newChild.text = inlinepatterns.handleAttributes(
+                                newChild.text, newChild
+                            )
                     element.insert(i, newChild)
                     i += 1
         return tree
index 0541e7b40917f9c95871c55b15b874b7ea7bbb54..d3d48f099940521431520bbc42eb466fd099e1fb 100644 (file)
@@ -10,14 +10,14 @@ Python 3 Stuff
 """
 PY3 = sys.version_info[0] == 3
 
-if PY3: #pragma: no cover
+if PY3:  # pragma: no cover
     string_type = str
     text_type = str
     int2str = chr
-else: #pragma: no cover
-    string_type = basestring
-    text_type = unicode
-    int2str = unichr
+else:  # pragma: no cover
+    string_type = basestring   # noqa
+    text_type = unicode        # noqa
+    int2str = unichr           # noqa
 
 
 """
@@ -25,12 +25,16 @@ Constants you might want to modify
 -----------------------------------------------------------------------------
 """
 
-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 = 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
+)
 # Placeholders
 STX = '\u0002'  # Use STX ("Start of text") for start-of-placeholder
 ETX = '\u0003'  # Use ETX ("End of text") for end-of-placeholder
@@ -48,17 +52,18 @@ Constants you probably do not need to change
 -----------------------------------------------------------------------------
 """
 
-RTL_BIDI_RANGES = ( ('\u0590', '\u07FF'),
-                     # Hebrew (0590-05FF), Arabic (0600-06FF),
-                     # Syriac (0700-074F), Arabic supplement (0750-077F),
-                     # Thaana (0780-07BF), Nko (07C0-07FF).
-                    ('\u2D30', '\u2D7F'), # Tifinagh
-                    )
+RTL_BIDI_RANGES = (
+    ('\u0590', '\u07FF'),
+    # Hebrew (0590-05FF), Arabic (0600-06FF),
+    # Syriac (0700-074F), Arabic supplement (0750-077F),
+    # Thaana (0780-07BF), Nko (07C0-07FF).
+    ('\u2D30', '\u2D7F')  # Tifinagh
+)
 
 # Extensions should use "markdown.util.etree" instead of "etree" (or do `from
 # markdown.util import etree`).  Do not import it by yourself.
 
-try: #pragma: no cover
+try:  # pragma: no cover
     # Is the C implementation of ElementTree available?
     import xml.etree.cElementTree as etree
     from xml.etree.ElementTree import Comment
@@ -66,7 +71,7 @@ try: #pragma: no cover
     etree.test_comment = Comment
     if etree.VERSION < "1.0.5":
         raise RuntimeError("cElementTree version 1.0.5 or higher is required.")
-except (ImportError, RuntimeError): #pragma: no cover
+except (ImportError, RuntimeError):  # pragma: no cover
     # Use the Python implementation of ElementTree?
     import xml.etree.ElementTree as etree
     if etree.VERSION < "1.1":
@@ -86,14 +91,15 @@ def isBlockLevel(tag):
     # Some ElementTree tags are not strings, so return False.
     return False
 
+
 def parseBoolValue(value, fail_on_errors=True, preserve_none=False):
     """Parses a string representing bool value. If parsing was successful,
-       returns True or False. If preserve_none=True, returns True, False, 
-       or None. If parsing was not successful, raises  ValueError, or, if 
+       returns True or False. If preserve_none=True, returns True, False,
+       or None. If parsing was not successful, raises  ValueError, or, if
        fail_on_errors=False, returns None."""
     if not isinstance(value, string_type):
         if preserve_none and value is None:
-            return  value
+            return value
         return bool(value)
     elif preserve_none and value.lower() == 'none':
         return None
@@ -104,11 +110,13 @@ def parseBoolValue(value, fail_on_errors=True, preserve_none=False):
     elif fail_on_errors:
         raise ValueError('Cannot parse bool value: %r' % value)
 
+
 """
 MISC AUXILIARY CLASSES
 =============================================================================
 """
 
+
 class AtomicString(text_type):
     """A string which should not be further processed."""
     pass
index 2727a201452bffae617f4019bc0a7690a7176d1c..57489537717a1f30990bc9612bc9ac01c6e1a02b 100755 (executable)
@@ -1,16 +1,22 @@
 #!/usr/bin/env python
 
 import tests
-import os, sys
+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'):
+        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.')
+            print(
+                sys.argv[2],
+                'does not have a valid file extension. Check config.'
+            )
     else:
         tests.generate_all()
 else:
index f635b48fda98d93b17021f8ba8ec926056007cc4..fec2dcb2f0cdb0bfd51c5312c7976953d5907a17 100755 (executable)
--- a/setup.py
+++ b/setup.py
@@ -1,7 +1,8 @@
 #!/usr/bin/env python
 
 from __future__ import with_statement
-import sys, os
+import sys
+import os
 from distutils.core import setup
 from distutils.command.install_scripts import install_scripts
 from distutils.command.build import build
@@ -10,6 +11,7 @@ from distutils.util import change_root, newer
 import codecs
 import imp
 
+
 def get_version():
     " Get version & version_info without importing markdown.__init__ "
     path = os.path.join(os.path.dirname(__file__), 'markdown')
@@ -25,8 +27,8 @@ version, version_info = get_version()
 # Get development Status for classifiers
 dev_status_map = {
     'alpha': '3 - Alpha',
-    'beta' : '4 - Beta',
-    'rc'   : '4 - Beta',
+    'beta' '4 - Beta',
+    'rc':    '4 - Beta',
     'final': '5 - Production/Stable'
 }
 if version_info[3] == 'alpha' and version_info[4] == 0:
@@ -40,8 +42,11 @@ else:
 # try to import itself rather than the library which will raise an error.
 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)
 
@@ -50,17 +55,20 @@ class md_install_scripts(install_scripts):
                 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)
+                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)
+                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))
+                _, 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)'
@@ -68,7 +76,7 @@ class build_docs(Command):
     user_options = [
         ('build-base=', 'd', 'directory to "build" to'),
         ('force', 'f', 'forcibly build everything (ignore file timestamps)'),
-        ]
+    ]
 
     boolean_options = ['force']
 
@@ -79,9 +87,11 @@ class build_docs(Command):
         self.sitemap = ''
 
     def finalize_options(self):
-        self.set_undefined_options('build',
-                                    ('build_base', 'build_base'),
-                                    ('force', 'force'))
+        self.set_undefined_options(
+            'build',
+            ('build_base', 'build_base'),
+            ('force', 'force')
+        )
         self.docs = self._get_docs()
 
     def _get_docs(self):
@@ -95,13 +105,13 @@ class build_docs(Command):
         """ Build and return context to pass to template. """
         # set defaults
         c = {
-            'title'      : '',
-            'prev_url'   : '',
-            'prev_title' : '',
-            'next_url'   : '',
-            'next_title' : '',
-            'crumb'      : '',
-            'version'    : version,
+            'title':      '',
+            'prev_url':   '',
+            'prev_title': '',
+            'next_url':   '',
+            'next_title': '',
+            'crumb':      '',
+            'version':    version,
         }
         c['body'] = self.md.convert(src)
         c['toc'] = self.md.toc
@@ -114,7 +124,7 @@ class build_docs(Command):
         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)
+        c['base'] = '../' * len(parts)
         # Build page title
         if name.lower() != 'index' or parts:
             c['page_title'] = '%s &#8212; Python Markdown' % c['title']
@@ -124,7 +134,7 @@ class build_docs(Command):
         crumbs = []
         ctemp = '<li><a href="%s">%s</a> &raquo;</li>'
         for n, part in enumerate(parts):
-            href = ('../'*n) + 'index.html'
+            href = ('../' * n) + 'index.html'
             label = part.replace('_', ' ').capitalize()
             crumbs.append(ctemp % (href, label))
         if c['title'] and name.lower() != 'index':
@@ -140,12 +150,19 @@ class build_docs(Command):
         try:
             import markdown
         except ImportError:
-            print ('skipping build_docs: Markdown "import" failed!')
+            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'])
+                extensions=[
+                    'extra',
+                    'toc(permalink=true)',
+                    'meta',
+                    'admonition',
+                    'smarty'
+                ]
+            )
             for infile in self.docs:
                 outfile, ext = os.path.splitext(infile)
                 if ext == '.txt':
@@ -160,7 +177,7 @@ class build_docs(Command):
                     self.mkpath(os.path.split(outfile)[0])
                     if self.force or newer(infile, outfile):
                         if self.verbose:
-                            print ('Converting %s -> %s' % (infile, outfile))
+                            print('Converting %s -> %s' % (infile, outfile))
                         if not self.dry_run:
                             with codecs.open(infile, encoding='utf-8') as f:
                                 src = f.read()
@@ -175,11 +192,12 @@ class build_docs(Command):
 
 
 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']
 
@@ -192,11 +210,11 @@ class md_build(build):
 
     sub_commands = build.sub_commands + [('build_docs', has_docs)]
 
-long_description = \
-'''This is a Python implementation of John Gruber's Markdown_. 
+long_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 
+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/
@@ -214,38 +232,41 @@ You may ask for help and discuss various other issues on the
 '''
 
 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,
-    description =   'Python implementation of Markdown.',
-    long_description = long_description,
-    author =        'Manfred Stienstra, Yuri takhteyev and Waylan limberg',
-    author_email =  'markdown [at] freewisdom.org',
-    maintainer =    'Waylan Limberg',
-    maintainer_email = 'waylan [at] gmail.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},
-    classifiers =   ['Development Status :: %s' % DEVSTATUS,
-                     'License :: OSI Approved :: BSD License',
-                     'Operating System :: OS Independent',
-                     'Programming Language :: Python',
-                     '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',
-                     'Topic :: Communications :: Email :: Filters',
-                     'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
-                     'Topic :: Internet :: WWW/HTTP :: Site Management',
-                     'Topic :: Software Development :: Documentation',
-                     'Topic :: Software Development :: Libraries :: Python Modules',
-                     'Topic :: Text Processing :: Filters',
-                     'Topic :: Text Processing :: Markup :: HTML',
-                    ],
-    )
+    name='Markdown',
+    version=version,
+    url='https://pythonhosted.org/Markdown/',
+    download_url='http://pypi.python.org/packages/source/M/Markdown/Markdown-%s.tar.gz' % 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',
+    maintainer='Waylan Limberg',
+    maintainer_email='waylan.limberg [at] 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
+    },
+    classifiers=[
+        'Development Status :: %s' % DEVSTATUS,
+        'License :: OSI Approved :: BSD License',
+        'Operating System :: OS Independent',
+        'Programming Language :: Python',
+        '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',
+        'Topic :: Communications :: Email :: Filters',
+        'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
+        'Topic :: Internet :: WWW/HTTP :: Site Management',
+        'Topic :: Software Development :: Documentation',
+        'Topic :: Software Development :: Libraries :: Python Modules',
+        'Topic :: Text Processing :: Filters',
+        'Topic :: Text Processing :: Markup :: HTML'
+    ]
+)
index 15710ac2e495eb23ef1bfccce291d82b6f6892ab..f759ceb74c438cb9d58e0bce5863dfcb059badef 100644 (file)
@@ -7,7 +7,7 @@ try:
 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."
+        "Markdown tests. Run `pip install nose` to install the latest version."
     e.args = (msg,) + e.args[1:]
     raise
 from .plugins import HtmlOutput, Markdown, MarkdownSyntaxError
@@ -20,12 +20,13 @@ try:
 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."
+        "tests. Run `pip install pyyaml` to install the latest version."
     e.args = (msg,) + e.args[1:]
     raise
 
 test_dir = os.path.abspath(os.path.dirname(__file__))
 
+
 class YamlConfig():
     def __init__(self, defaults, filename):
         """ Set defaults and load config file if it exists. """
@@ -73,21 +74,24 @@ def get_config(dir_name):
     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'})
+        '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:
@@ -101,9 +105,10 @@ class CheckSyntax(object):
         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') 
+        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).
+            # 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'):
@@ -112,15 +117,22 @@ class CheckSyntax(object):
             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)]
+            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)))
+            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):
@@ -131,9 +143,12 @@ def TestSyntax():
             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))
+                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)
@@ -141,15 +156,16 @@ def generate(file, config):
         print('Skipping:', file)
         return None
     input_file = file + config.get(cfg_section, 'input_ext')
-    output_file = file + config.get(cfg_section, 'output_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, 
+        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):
@@ -164,4 +180,3 @@ def generate_all():
 
 def run():
     nose.main(addplugins=[HtmlOutput(), Markdown()])
-
index f8874c388556ec9304436e81e5a1ec9ad01eb0cb..7acb2eeb79794ec692913b76649eeafd45fbbc9d 100644 (file)
@@ -27,11 +27,18 @@ Note: Subelements are not required to have tail text.</div>
 Raw html blocks may also be nested.
 </div>
 
-
-
 </div>
 <p>This text is after the markdown in html.</p>
 <div name="issue308">
 <p><span>1</span>
 <span>2</span></p>
-</div>
\ No newline at end of file
+</div>
+<div name="issue368">
+<p>Markdown is <em>active</em> here.</p>
+<div name="RawHtml">
+Raw html blocks may also be nested.
+</div>
+
+<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
index 0a82ccf70586d7329b77969453dbc4af4b34e6b9..72f530b996d932a7de0ab1e815da4adb90aaf9fb 100644 (file)
@@ -51,3 +51,17 @@ This text is after the markdown in html.
 <span>2</span>
 
 </div>
+
+<div markdown="1" name="issue368">
+
+Markdown is *active* here.
+
+<div name="RawHtml">
+Raw html blocks may also be nested.
+</div>
+
+Markdown is *still* active here.
+
+</div>
+
+Markdown is *active again* here.
index 5605d3f392494a6851b35b5a0686d226218860ee..7e27b29663ebff1f405ab46005abd179566dd40d 100644 (file)
@@ -36,6 +36,10 @@ toc_nested2:
         markdown.extensions.toc:
             permalink: "[link]"
 
+toc_nested_list:
+    extensions:
+        - markdown.extensions.toc
+
 wikilinks:
     extensions:
         - markdown.extensions.wikilinks
diff --git a/tests/extensions/toc_nested_list.html b/tests/extensions/toc_nested_list.html
new file mode 100644 (file)
index 0000000..6912411
--- /dev/null
@@ -0,0 +1,30 @@
+<h1 id="title">Title</h1>
+<div class="toc">
+<ul>
+<li><a href="#title">Title</a><ul>
+<li><a href="#section-1">Section 1</a><ul>
+<li><a href="#subsection-1">Subsection 1</a></li>
+<li><a href="#subsection-2">Subsection 2</a></li>
+</ul>
+</li>
+<li><a href="#section-2">Section 2</a></li>
+<li><a href="#section-3">Section 3</a></li>
+</ul>
+</li>
+</ul>
+</div>
+<h2 id="section-1">Section 1</h2>
+<ol>
+<li>
+<p>List Item 1</p>
+<h3 id="subsection-1">Subsection 1</h3>
+<p>Explanation 1</p>
+</li>
+<li>
+<p>List Item 2</p>
+<h3 id="subsection-2">Subsection 2</h3>
+<p>Explanation 2</p>
+</li>
+</ol>
+<h2 id="section-2">Section 2</h2>
+<h2 id="section-3">Section 3</h2>
\ No newline at end of file
diff --git a/tests/extensions/toc_nested_list.txt b/tests/extensions/toc_nested_list.txt
new file mode 100644 (file)
index 0000000..d83e96f
--- /dev/null
@@ -0,0 +1,19 @@
+# Title
+
+[TOC]
+
+## Section 1
+
+1. List Item 1
+
+    ### Subsection 1
+    Explanation 1
+
+2. List Item 2
+
+    ### Subsection 2
+    Explanation 2
+
+## Section 2
+
+## Section 3
\ No newline at end of file
diff --git a/tests/misc/em_strong_complex.html b/tests/misc/em_strong_complex.html
new file mode 100644 (file)
index 0000000..3befa70
--- /dev/null
@@ -0,0 +1,14 @@
+<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><em>test</em> test</strong>_</p>
+<p><strong><em>test</em> test</strong></p>
+<p><em>test_test test_test</em></p>
+<p><em><strong>test test</strong> test test</em></p>
+<p><strong><em>test test</em> test test</strong></p>
+<p>*<em>test</em></p>
+<p><strong><em>test</em></strong></p>
+<p><strong>test</strong>*</p>
+<p><strong><em>test</em> test</strong></p>
+<p><em>test</em>test test<em>test</em></p>
\ No newline at end of file
diff --git a/tests/misc/em_strong_complex.txt b/tests/misc/em_strong_complex.txt
new file mode 100644 (file)
index 0000000..66f4ff1
--- /dev/null
@@ -0,0 +1,27 @@
+___test test__ test test_
+
+___test test_ test test__
+
+___test___
+
+__test___
+
+___test_ test___
+
+___test_ test__
+
+_test_test test_test_
+
+***test test** test test*
+
+***test test* test test**
+
+**test*
+
+***test***
+
+**test***
+
+***test* test**
+
+*test*test test*test*
\ No newline at end of file
index 1ac98ee1a050fa19b04116703b980fe575b797e4..1c7bb43c683aaf0b94f7f6813353ebe5dca4c4b5 100644 (file)
@@ -4,4 +4,7 @@
 <strong><a href="http://example.com"><em>link</em></a></strong>
 <strong><a href="http://example.com"><em>link</em></a></strong>
 <strong><a href="http://example.com"><em>link</em></a></strong>
-<a href="http://example.com"><strong><em>link</em></strong></a></p>
\ No newline at end of file
+<a href="http://example.com"><strong><em>link</em></strong></a></p>
+<p><strong><em>I am <strong><em>italic</em> and</strong> bold</em> I am <code>just</code> bold</strong></p>
+<p>Example <strong><em>bold italic</em></strong> on the same line <strong><em>bold italic</em></strong>.</p>
+<p>Example <strong><em>bold italic</em></strong> on the same line <strong><em>bold italic</em></strong>.</p>
\ No newline at end of file
index c651b8682b23b067813813dd7ff239f61dda076e..9032cf13191e9e9420dfee0a2a62fd333028b185 100644 (file)
@@ -5,3 +5,9 @@ __[_link_](http://example.com)__
 __[*link*](http://example.com)__
 **[_link_](http://example.com)**
 [***link***](http://example.com)
+
+***I am ___italic_ and__ bold* I am `just` bold**
+
+Example __*bold italic*__ on the same line __*bold italic*__.
+
+Example **_bold italic_** on the same line **_bold italic_**.
index 12bac5541315922f37292514bac8fd2a5b36757a..90c5c0d9928a468fd32229e3429a357d8b908a3b 100644 (file)
@@ -9,9 +9,11 @@ class MarkdownSyntaxError(Exception):
 
 class Markdown(ErrorClassPlugin):
     """ Add MarkdownSyntaxError and ensure proper formatting. """
-    mdsyntax = ErrorClass(MarkdownSyntaxError, 
-                          label='MarkdownSyntaxError', 
-                          isfailure=True)
+    mdsyntax = ErrorClass(
+        MarkdownSyntaxError,
+        label='MarkdownSyntaxError',
+        isfailure=True
+    )
     enabled = True
 
     def configure(self, options, conf):
@@ -39,28 +41,30 @@ def escape(html):
 
 class HtmlOutput(Plugin):
     """Output test results as ugly, unstyled html. """
-    
+
     name = 'html-output'
-    score = 2 # run late
+    score = 2  # run late
     enabled = True
-    
+
     def __init__(self):
         super(HtmlOutput, self).__init__()
-        self.html = [ '<html><head>',
-                      '<title>Test output</title>',
-                      '</head><body>' ]
-   
+        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>')
@@ -68,9 +72,10 @@ class HtmlOutput(Plugin):
 
     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(
+            "Ran %d test%s" %
+            (result.testsRun, result.testsRun != 1 and "s" or "")
+        )
         self.html.append('</div>')
         self.html.append('<div>')
         if not result.wasSuccessful():
@@ -93,7 +98,7 @@ or ""))
     def formatErr(self, err):
         exctype, value, tb = err
         return ''.join(traceback.format_exception(exctype, value, tb))
-    
+
     def startContext(self, ctx):
         try:
             n = ctx.__name__
@@ -108,12 +113,13 @@ or ""))
 
     def stopContext(self, ctx):
         self.html.append('</fieldset>')
-    
+
     def startTest(self, test):
-        self.html.extend([ '<div><span>',
-                           test.shortDescription() or str(test),
-                           '</span>' ])
-        
+        self.html.extend([
+            '<div><span>',
+            test.shortDescription() or str(test),
+            '</span>'
+        ])
+
     def stopTest(self, test):
         self.html.append('</div>')
-
index b19db6276e4ba171747185412f5884b658f5eef1..e3de779dd379e6074e9e4d8a605810d95248b1b7 100644 (file)
@@ -15,12 +15,13 @@ import types
 import markdown
 import warnings
 from markdown.__main__ import parse_options
-from logging import DEBUG, INFO, CRITICAL
+from logging import DEBUG, WARNING, CRITICAL
 import yaml
 import tempfile
 
 PY3 = sys.version_info[0] == 3
 
+
 class TestMarkdownBasics(unittest.TestCase):
     """ Tests basics of the Markdown class. """
 
@@ -53,6 +54,7 @@ class TestMarkdownBasics(unittest.TestCase):
         """ Test Extension loading with class name (`path.to.module:Class`). """
         markdown.Markdown(extensions=['markdown.extensions.footnotes:FootnoteExtension'])
 
+
 class TestBlockParser(unittest.TestCase):
     """ Tests of the BlockParser class. """
 
@@ -65,8 +67,10 @@ class TestBlockParser(unittest.TestCase):
         root = markdown.util.etree.Element("div")
         text = 'foo'
         self.parser.parseChunk(root, text)
-        self.assertEqual(markdown.serializers.to_xhtml_string(root), 
-                         "<div><p>foo</p></div>")
+        self.assertEqual(
+            markdown.serializers.to_xhtml_string(root),
+            "<div><p>foo</p></div>"
+        )
 
     def testParseDocument(self):
         """ Test BlockParser.parseDocument. """
@@ -74,8 +78,10 @@ class TestBlockParser(unittest.TestCase):
         tree = self.parser.parseDocument(lines)
         self.assertTrue(isinstance(tree, markdown.util.etree.ElementTree))
         self.assertTrue(markdown.util.etree.iselement(tree.getroot()))
-        self.assertEqual(markdown.serializers.to_xhtml_string(tree.getroot()),
-            "<div><h1>foo</h1><p>bar</p><pre><code>baz\n</code></pre></div>")
+        self.assertEqual(
+            markdown.serializers.to_xhtml_string(tree.getroot()),
+            "<div><h1>foo</h1><p>bar</p><pre><code>baz\n</code></pre></div>"
+        )
 
 
 class TestBlockParserState(unittest.TestCase):
@@ -115,9 +121,10 @@ class TestBlockParserState(unittest.TestCase):
         self.state.reset()
         self.assertEqual(self.state, ['state1'])
 
+
 class TestHtmlStash(unittest.TestCase):
     """ Test Markdown's HtmlStash. """
-    
+
     def setUp(self):
         self.stash = markdown.util.HtmlStash()
         self.placeholder = self.stash.store('foo')
@@ -133,14 +140,18 @@ class TestHtmlStash(unittest.TestCase):
         placeholder = self.stash.store('bar')
         self.assertEqual(placeholder, self.stash.get_placeholder(1))
         self.assertEqual(self.stash.html_counter, 2)
-        self.assertEqual(self.stash.rawHtmlBlocks, 
-                        [('foo', False), ('bar', False)])
+        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)])
+        self.assertEqual(
+            self.stash.rawHtmlBlocks,
+            [('foo', False), ('bar', True)]
+        )
 
     def testReset(self):
         """ Test HtmlStash.reset. """
@@ -184,49 +195,86 @@ class TestOrderedDict(unittest.TestCase):
 
     def testKeys(self):
         """ Test output of OrderedDict.keys(). """
-        self.assertEqual(list(self.odict.keys()),
-                    ['first', 'third', 'fourth', 'fifth'])
+        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')])
+        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')])
+        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')])
+        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', '.')])
+        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')])
+        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', '.')])
+        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(). """
@@ -236,30 +284,48 @@ class TestOrderedDict(unittest.TestCase):
     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')])
+        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')])
+        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')])
+        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')])
+        self.assertEqual(
+            list(self.odict.items()), [
+                ('first', 'This'),
+                ('third', 'a'),
+                ('fourth', 'self'),
+                ('fifth', 'test')
+            ]
+        )
+
 
 class TestErrors(unittest.TestCase):
     """ Test Error Reporting. """
@@ -275,7 +341,7 @@ class TestErrors(unittest.TestCase):
     def testNonUnicodeSource(self):
         """ Test falure on non-unicode source text. """
         if sys.version_info < (3, 0):
-            source = "foo".encode('utf-16') 
+            source = "foo".encode('utf-16')
             self.assertRaises(UnicodeDecodeError, markdown.markdown, source)
 
     def testBadOutputFormat(self):
@@ -284,40 +350,47 @@ class TestErrors(unittest.TestCase):
 
     def testLoadExtensionFailure(self):
         """ Test failure of an extension to load. """
-        self.assertRaises(ImportError, 
-                        markdown.Markdown, extensions=['non_existant_ext']) 
+        self.assertRaises(
+            ImportError,
+            markdown.Markdown, extensions=['non_existant_ext']
+        )
 
     def testLoadBadExtension(self):
         """ Test loading of an Extension with no makeExtension function. """
-        _create_fake_extension(name='fake_a', has_factory_func=False)
-        self.assertRaises(AttributeError, markdown.Markdown, extensions=['fake_a'])
+        self.assertRaises(AttributeError, markdown.Markdown, extensions=['markdown.util'])
 
     def testNonExtension(self):
         """ Test loading a non Extension object as an extension. """
-        _create_fake_extension(name='fake_b', is_wrong_type=True)
-        self.assertRaises(TypeError, markdown.Markdown, extensions=['fake_b'])
+        self.assertRaises(TypeError, markdown.Markdown, extensions=[object])
 
     def testBaseExtention(self):
         """ Test that the base Extension class will raise NotImplemented. """
-        _create_fake_extension(name='fake_c')
-        self.assertRaises(NotImplementedError, 
-                        markdown.Markdown, extensions=['fake_c'])
+        self.assertRaises(
+            NotImplementedError,
+            markdown.Markdown, extensions=[markdown.extensions.Extension()]
+        )
 
     def testMdxExtention(self):
-        """ Test that appending mdx_ raises a PendingDeprecationWarning. """
-        _create_fake_extension(name='fake_d', use_old_style=True)
-        self.assertRaises(PendingDeprecationWarning, 
-                        markdown.Markdown, extensions=['fake_d'])
+        """ 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 PendingDeprecationWarning. """
-        self.assertRaises(PendingDeprecationWarning, 
-                        markdown.Markdown, extensions=['footnotes'])
+        """ 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 PendingDeprecationWarning. """
-        self.assertRaises(PendingDeprecationWarning, 
-                        markdown.Markdown, extensions=['markdown.extension.footnotes(PLACE_MARKER=FOO)'])
+        """ 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):
@@ -330,21 +403,23 @@ def _create_fake_extension(name, has_factory_func=True, is_wrong_type=False, use
         # 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 
+    # 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
+    sys.modules[mod_name] = ext_mod
 
 
 class testETreeComments(unittest.TestCase):
-    """ 
+    """
     Test that ElementTree Comments work.
 
     These tests should only be a concern when using cElementTree with third
@@ -372,15 +447,33 @@ class testETreeComments(unittest.TestCase):
 
     def testCommentSerialization(self):
         """ Test that an ElementTree Comment serializes properly. """
-        self.assertEqual(markdown.serializers.to_html_string(self.comment),
-                    '<!--foo-->')
+        self.assertEqual(
+            markdown.serializers.to_html_string(self.comment),
+            '<!--foo-->'
+        )
 
     def testCommentPrettify(self):
         """ Test that an ElementTree Comment is prettified properly. """
         pretty = markdown.treeprocessors.PrettifyTreeprocessor()
         pretty.run(self.comment)
-        self.assertEqual(markdown.serializers.to_html_string(self.comment),
-                    '<!--foo-->\n')
+        self.assertEqual(
+            markdown.serializers.to_html_string(self.comment),
+            '<!--foo-->\n'
+        )
+
+
+class testElementTailTests(unittest.TestCase):
+    """ Element Tail Tests """
+    def setUp(self):
+        self.pretty = markdown.treeprocessors.PrettifyTreeprocessor()
+
+    def testBrTailNoNewline(self):
+        """ Test that last <br> in tree has a new line tail """
+        root = markdown.util.etree.Element('root')
+        br = markdown.util.etree.SubElement(root, 'br')
+        self.assertEqual(br.tail, None)
+        self.pretty.run(root)
+        self.assertEqual(br.tail, "\n")
 
 
 class testSerializers(unittest.TestCase):
@@ -391,18 +484,22 @@ class testSerializers(unittest.TestCase):
         el = markdown.util.etree.Element('div')
         p = markdown.util.etree.SubElement(el, 'p')
         p.text = 'foo'
-        hr = markdown.util.etree.SubElement(el, 'hr')
-        self.assertEqual(markdown.serializers.to_html_string(el),
-                    '<div><p>foo</p><hr></div>')
+        markdown.util.etree.SubElement(el, 'hr')
+        self.assertEqual(
+            markdown.serializers.to_html_string(el),
+            '<div><p>foo</p><hr></div>'
+        )
 
     def testXhtml(self):
         """" Test XHTML serialization. """
         el = markdown.util.etree.Element('div')
         p = markdown.util.etree.SubElement(el, 'p')
         p.text = 'foo'
-        hr = markdown.util.etree.SubElement(el, 'hr')
-        self.assertEqual(markdown.serializers.to_xhtml_string(el),
-                    '<div><p>foo</p><hr /></div>')
+        markdown.util.etree.SubElement(el, 'hr')
+        self.assertEqual(
+            markdown.serializers.to_xhtml_string(el),
+            '<div><p>foo</p><hr /></div>'
+        )
 
     def testMixedCaseTags(self):
         """" Test preservation of tag case. """
@@ -410,10 +507,11 @@ class testSerializers(unittest.TestCase):
         el.text = 'not valid '
         em = markdown.util.etree.SubElement(el, 'EMPHASIS')
         em.text = 'html'
-        hr = markdown.util.etree.SubElement(el, 'HR')
-        self.assertEqual(markdown.serializers.to_xhtml_string(el),
-                    '<MixedCase>not valid <EMPHASIS>html</EMPHASIS><HR /></MixedCase>')
-
+        markdown.util.etree.SubElement(el, 'HR')
+        self.assertEqual(
+            markdown.serializers.to_xhtml_string(el),
+            '<MixedCase>not valid <EMPHASIS>html</EMPHASIS><HR /></MixedCase>'
+        )
 
     def buildExtension(self):
         """ Build an extension which registers fakeSerializer. """
@@ -428,9 +526,12 @@ class testSerializers(unittest.TestCase):
         return registerFakeSerializer()
 
     def testRegisterSerializer(self):
-        self.assertEqual(markdown.markdown('baz', 
-                extensions=[self.buildExtension()], output_format='fake'),
-                    '<p>foo</p>')
+        self.assertEqual(
+            markdown.markdown(
+                'baz', extensions=[self.buildExtension()], output_format='fake'
+            ),
+            '<p>foo</p>'
+        )
 
 
 class testAtomicString(unittest.TestCase):
@@ -446,8 +547,10 @@ class testAtomicString(unittest.TestCase):
         p = markdown.util.etree.SubElement(tree, 'p')
         p.text = 'some *text*'
         new = self.inlineprocessor.run(tree)
-        self.assertEqual(markdown.serializers.to_html_string(new), 
-                    '<div><p>some <em>text</em></p></div>')
+        self.assertEqual(
+            markdown.serializers.to_html_string(new),
+            '<div><p>some <em>text</em></p></div>'
+        )
 
     def testSimpleAtomicString(self):
         """ Test that a simple AtomicString is not parsed. """
@@ -455,8 +558,10 @@ class testAtomicString(unittest.TestCase):
         p = markdown.util.etree.SubElement(tree, 'p')
         p.text = markdown.util.AtomicString('some *text*')
         new = self.inlineprocessor.run(tree)
-        self.assertEqual(markdown.serializers.to_html_string(new), 
-                    '<div><p>some *text*</p></div>')
+        self.assertEqual(
+            markdown.serializers.to_html_string(new),
+            '<div><p>some *text*</p></div>'
+        )
 
     def testNestedAtomicString(self):
         """ Test that a nested AtomicString is not parsed. """
@@ -473,9 +578,12 @@ class testAtomicString(unittest.TestCase):
         span2.tail = markdown.util.AtomicString(' *test*')
         span1.tail = markdown.util.AtomicString(' *with*')
         new = self.inlineprocessor.run(tree)
-        self.assertEqual(markdown.serializers.to_html_string(new), 
+        self.assertEqual(
+            markdown.serializers.to_html_string(new),
             '<div><p>*some* <span>*more* <span>*text* <span>*here*</span> '
-            '*to*</span> *test*</span> *with*</p></div>')
+            '*to*</span> *test*</span> *with*</p></div>'
+        )
+
 
 class TestConfigParsing(unittest.TestCase):
     def assertParses(self, value, result):
@@ -496,6 +604,7 @@ class TestConfigParsing(unittest.TestCase):
     def testInvalidBooleansParsing(self):
         self.assertRaises(ValueError, markdown.util.parseBoolValue, 'novalue')
 
+
 class TestCliOptionParsing(unittest.TestCase):
     """ Test parsing of Command Line Interface Options. """
 
@@ -504,11 +613,10 @@ class TestCliOptionParsing(unittest.TestCase):
             'input': None,
             'output': None,
             'encoding': None,
-            'safe_mode': False,
             'output_format': 'xhtml1',
             'lazy_ol': True,
             'extensions': [],
-            'extension_configs': {},  
+            'extension_configs': {},
         }
         self.tempfile = ''
 
@@ -520,14 +628,14 @@ class TestCliOptionParsing(unittest.TestCase):
         options, logging_level = parse_options([])
         self.assertEqual(options, self.default_options)
         self.assertEqual(logging_level, CRITICAL)
-    
+
     def testQuietOption(self):
         options, logging_level = parse_options(['-q'])
         self.assertTrue(logging_level > CRITICAL)
 
     def testVerboseOption(self):
         options, logging_level = parse_options(['-v'])
-        self.assertEqual(logging_level, INFO)
+        self.assertEqual(logging_level, WARNING)
 
     def testNoisyOption(self):
         options, logging_level = parse_options(['--noisy'])
@@ -575,10 +683,14 @@ class TestCliOptionParsing(unittest.TestCase):
         self.assertEqual(options, self.default_options)
 
     def testMultipleExtensionOptions(self):
-        options, logging_level = parse_options(['-x', 'markdown.extensions.footnotes', 
-                                                '-x', 'markdown.extensions.smarty'])
-        self.default_options['extensions'] = ['markdown.extensions.footnotes', 
-                                              'markdown.extensions.smarty']
+        options, logging_level = parse_options([
+            '-x', 'markdown.extensions.footnotes',
+            '-x', 'markdown.extensions.smarty'
+        ])
+        self.default_options['extensions'] = [
+            'markdown.extensions.footnotes',
+            'markdown.extensions.smarty'
+        ]
         self.assertEqual(options, self.default_options)
 
     def create_config_file(self, config):
@@ -590,15 +702,28 @@ class TestCliOptionParsing(unittest.TestCase):
         with os.fdopen(fd, 'w') as fp:
             fp.write(config)
 
-    def testExtensonConfigOption(self):
+    def testExtensionConfigOption(self):
         config = {
-        'markdown.extensions.wikilinks': {
-            'base_url': 'http://example.com/',
-            'end_url': '.html',
-            'html_class': 'test',
+            'markdown.extensions.wikilinks': {
+                'base_url': 'http://example.com/',
+                'end_url': '.html',
+                'html_class': 'test',
             },
-        'markdown.extensions.footnotes:FootnotesExtension': {
-            'PLACE_MARKER': '~~~footnotes~~~'
+            'markdown.extensions.footnotes:FootnotesExtension': {
+                'PLACE_MARKER': '~~~footnotes~~~'
+            }
+        }
+        self.create_config_file(config)
+        options, logging_level = parse_options(['-c', self.tempfile])
+        self.default_options['extension_configs'] = config
+        self.assertEqual(options, self.default_options)
+
+    def textBoolExtensionConfigOption(self):
+        config = {
+            'markdown.extensions.toc': {
+                'title': 'Some Title',
+                'anchorlink': True,
+                'permalink': True
             }
         }
         self.create_config_file(config)
@@ -608,13 +733,13 @@ class TestCliOptionParsing(unittest.TestCase):
 
     def testExtensonConfigOptionAsJSON(self):
         config = {
-        'markdown.extensions.wikilinks': {
-            'base_url': 'http://example.com/',
-            'end_url': '.html',
-            'html_class': 'test',
+            'markdown.extensions.wikilinks': {
+                'base_url': 'http://example.com/',
+                'end_url': '.html',
+                'html_class': 'test',
             },
-        'markdown.extensions.footnotes:FootnotesExtension': {
-            'PLACE_MARKER': '~~~footnotes~~~'
+            'markdown.extensions.footnotes:FootnotesExtension': {
+                'PLACE_MARKER': '~~~footnotes~~~'
             }
         }
         import json
@@ -628,8 +753,8 @@ class TestCliOptionParsing(unittest.TestCase):
 
     def testExtensonConfigOptionBadFormat(self):
         config = """
-[footnotes] 
+[footnotes]
 PLACE_MARKER= ~~~footnotes~~~
 """
         self.create_config_file(config)
-        self.assertRaises(yaml.YAMLError, parse_options, ['-c', self.tempfile])
\ No newline at end of file
+        self.assertRaises(yaml.YAMLError, parse_options, ['-c', self.tempfile])
index a21fd94d2f9a74db81f823d7310603f57f996b35..6a57878dee1003aa5b183dba6439f02c5eabac30 100644 (file)
@@ -8,9 +8,11 @@ continue to work as advertised. This used to be accomplished by doctests.
 """
 
 from __future__ import unicode_literals
+import datetime
 import unittest
 import markdown
 
+
 class TestExtensionClass(unittest.TestCase):
     """ Test markdown.extensions.Extension. """
 
@@ -35,9 +37,13 @@ class TestExtensionClass(unittest.TestCase):
         self.assertEqual(self.ext.getConfigs(), {'foo': 'bar', 'bar': 'baz'})
 
     def testGetConfigInfo(self):
-        self.assertEqual(dict(self.ext.getConfigInfo()), 
-                         dict([('foo', 'Description of foo'),
-                               ('bar', 'Description of bar')]))
+        self.assertEqual(
+            dict(self.ext.getConfigInfo()),
+            dict([
+                ('foo', 'Description of foo'),
+                ('bar', 'Description of bar')
+            ])
+        )
 
     def testSetConfig(self):
         self.ext.setConfig('foo', 'baz')
@@ -63,18 +69,22 @@ class TestAbbr(unittest.TestCase):
         text = 'Some text with an ABBR and a REF. Ignore REFERENCE and ref.' + \
                '\n\n*[ABBR]: Abbreviation\n' + \
                '*[REF]: Abbreviation Reference'
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<p>Some text with an <abbr title="Abbreviation">ABBR</abbr> '
             'and a <abbr title="Abbreviation Reference">REF</abbr>. Ignore '
-            'REFERENCE and ref.</p>')
+            'REFERENCE and ref.</p>'
+        )
 
     def testNestedAbbr(self):
         """ Test Nested Abbreviations. """
         text = '[ABBR](/foo) and _ABBR_\n\n' + \
                '*[ABBR]: Abreviation'
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<p><a href="/foo"><abbr title="Abreviation">ABBR</abbr></a> '
-            'and <em><abbr title="Abreviation">ABBR</abbr></em></p>')
+            'and <em><abbr title="Abreviation">ABBR</abbr></em></p>'
+        )
 
 
 class TestCodeHilite(unittest.TestCase):
@@ -83,7 +93,7 @@ class TestCodeHilite(unittest.TestCase):
     def setUp(self):
         self.has_pygments = True
         try:
-            import pygments
+            import pygments  # noqa
         except ImportError:
             self.has_pygments = False
 
@@ -91,57 +101,66 @@ class TestCodeHilite(unittest.TestCase):
         text = '\t# A Code Comment'
         md = markdown.Markdown(extensions=['markdown.extensions.codehilite'])
         if self.has_pygments:
-            self.assertEqual(md.convert(text),
-                '<div class="codehilite">'
-                '<pre><span class="c"># A Code Comment</span>\n'
-                '</pre></div>')
+            # Pygments can use random lexer here as we did not specify the language
+            self.assertTrue(md.convert(text).startswith('<div class="codehilite"><pre>'))
         else:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<pre class="codehilite"><code># A Code Comment'
-                '</code></pre>')
-    
+                '</code></pre>'
+            )
+
     def testLinenumsTrue(self):
         text = '\t# A Code Comment'
         md = markdown.Markdown(
             extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=True)])
         if self.has_pygments:
-            # Differant versions of pygments output slightly different markup.
-            # So we use 'startwith' and test just enough to confirm that 
+            # 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.assertTrue(
+                md.convert(text).startswith(
+                    '<table class="codehilitetable"><tr><td class="linenos">'
+                )
+            )
         else:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<pre class="codehilite"><code class="linenums"># A Code Comment'
-                '</code></pre>')
+                '</code></pre>'
+            )
 
     def testLinenumsFalse(self):
         text = '\t#!Python\n\t# A Code Comment'
         md = markdown.Markdown(
             extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=False)])
         if self.has_pygments:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<div class="codehilite">'
                 '<pre><span class="c"># A Code Comment</span>\n'
-                '</pre></div>')
+                '</pre></div>'
+            )
         else:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<pre class="codehilite"><code class="language-python"># A Code Comment'
-                '</code></pre>')
+                '</code></pre>'
+            )
 
     def testLinenumsNone(self):
         text = '\t# A Code Comment'
         md = markdown.Markdown(
             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>')
+            # Pygments can use random lexer here as we did not specify the language
+            self.assertTrue(md.convert(text).startswith('<div class="codehilite"><pre>'))
         else:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<pre class="codehilite"><code># A Code Comment'
-                '</code></pre>')
+                '</code></pre>'
+            )
 
     def testLinenumsNoneWithShebang(self):
         text = '\t#!Python\n\t# A Code Comment'
@@ -149,28 +168,38 @@ class TestCodeHilite(unittest.TestCase):
             extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)])
         if self.has_pygments:
             # Differant versions of pygments output slightly different markup.
-            # So we use 'startwith' and test just enough to confirm that 
+            # 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.assertTrue(
+                md.convert(text).startswith(
+                    '<table class="codehilitetable"><tr><td class="linenos">'
+                )
+            )
         else:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<pre class="codehilite"><code class="language-python linenums"># A Code Comment'
-                '</code></pre>')
+                '</code></pre>'
+            )
 
     def testLinenumsNoneWithColon(self):
         text = '\t:::Python\n\t# A Code Comment'
         md = markdown.Markdown(
-            extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)])
+            extensions=[markdown.extensions.codehilite.CodeHiliteExtension(linenums=None)]
+        )
         if self.has_pygments:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<div class="codehilite">'
                 '<pre><span class="c"># A Code Comment</span>\n'
-                '</pre></div>')
+                '</pre></div>'
+            )
         else:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<pre class="codehilite"><code class="language-python"># A Code Comment'
-                '</code></pre>')
+                '</code></pre>'
+            )
 
     def testHighlightLinesWithColon(self):
         # Test with hl_lines delimited by single or double quotes.
@@ -180,18 +209,34 @@ class TestCodeHilite(unittest.TestCase):
         for text in (text0, text1):
             md = markdown.Markdown(extensions=['markdown.extensions.codehilite'])
             if self.has_pygments:
-                self.assertEqual(md.convert(text),
+                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>')
+                    '</pre></div>'
+                )
             else:
-                self.assertEqual(md.convert(text),
+                self.assertEqual(
+                    md.convert(text),
                     '<pre class="codehilite">'
                     '<code class="language-python">#line 1\n'
                     '#line 2\n'
-                    '#line 3</code></pre>')
+                    '#line 3</code></pre>'
+                )
+
+    def testUsePygmentsFalse(self):
+        text = '\t:::Python\n\t# A Code Comment'
+        md = markdown.Markdown(
+            extensions=[markdown.extensions.codehilite.CodeHiliteExtension(use_pygments=False)]
+        )
+        self.assertEqual(
+            md.convert(text),
+            '<pre class="codehilite"><code class="language-python"># A Code Comment'
+            '</code></pre>'
+        )
+
 
 class TestFencedCode(unittest.TestCase):
     """ Test fenced_code extension. """
@@ -200,7 +245,7 @@ class TestFencedCode(unittest.TestCase):
         self.md = markdown.Markdown(extensions=['markdown.extensions.fenced_code'])
         self.has_pygments = True
         try:
-            import pygments
+            import pygments  # noqa
         except ImportError:
             self.has_pygments = False
 
@@ -212,18 +257,22 @@ A paragraph before a fenced code block:
 ~~~
 Fenced code block
 ~~~'''
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<p>A paragraph before a fenced code block:</p>\n'
             '<pre><code>Fenced code block\n'
-            '</code></pre>')
+            '</code></pre>'
+        )
 
     def testSafeFence(self):
         """ Test Fenced Code with safe_mode. """
         text = '~~~\nCode\n~~~'
         self.md.safeMode = 'replace'
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<pre><code>Code\n'
-            '</code></pre>')
+            '</code></pre>'
+        )
 
     def testNestedFence(self):
         """ Test nested fence. """
@@ -233,10 +282,12 @@ Fenced code block
 
 ~~~~
 ~~~~~~~~'''
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<pre><code>\n'
             '~~~~\n'
-            '</code></pre>')
+            '</code></pre>'
+        )
 
     def testFencedLanguage(self):
         """ Test Language Tags. """
@@ -245,9 +296,11 @@ Fenced code block
 ~~~~{.python}
 # Some python code
 ~~~~'''
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<pre><code class="python"># Some python code\n'
-            '</code></pre>')
+            '</code></pre>'
+        )
 
     def testFencedBackticks(self):
         """ Test Code Fenced with Backticks. """
@@ -257,10 +310,12 @@ Fenced code block
 # Arbitrary code
 ~~~~~ # these tildes will not close the block
 `````'''
-        self.assertEqual(self.md.convert(text),
-        '<pre><code># Arbitrary code\n'
-        '~~~~~ # these tildes will not close the block\n'
-        '</code></pre>')
+        self.assertEqual(
+            self.md.convert(text),
+            '<pre><code># Arbitrary code\n'
+            '~~~~~ # these tildes will not close the block\n'
+            '</code></pre>'
+        )
 
     def testFencedCodeWithHighlightLines(self):
         """ Test Fenced Code with Highlighted Lines. """
@@ -271,22 +326,29 @@ line 1
 line 2
 line 3
 ```'''
-        md = markdown.Markdown(extensions=[
-             markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
-            'markdown.extensions.fenced_code'])
+        md = markdown.Markdown(
+            extensions=[
+                markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
+                'markdown.extensions.fenced_code'
+            ]
+        )
 
         if self.has_pygments:
-            self.assertEqual(md.convert(text),
+            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>')
+                '</pre></div>'
+            )
         else:
-            self.assertEqual(md.convert(text),
+            self.assertEqual(
+                md.convert(text),
                 '<pre class="codehilite"><code>line 1\n'
                 'line 2\n'
-                'line 3</code></pre>')
+                'line 3</code></pre>'
+            )
 
     def testFencedLanguageAndHighlightLines(self):
         """ Test Fenced Code with Highlighted Lines. """
@@ -304,22 +366,29 @@ line 3
 #line 3
 ~~~'''
         for text in (text0, text1):
-            md = markdown.Markdown(extensions=[
-                 markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
-                'markdown.extensions.fenced_code'])
-
+            md = markdown.Markdown(
+                extensions=[
+                    markdown.extensions.codehilite.CodeHiliteExtension(linenums=None, guess_lang=False),
+                    'markdown.extensions.fenced_code'
+                ]
+            )
             if self.has_pygments:
-                self.assertEqual(md.convert(text),
+                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>')
+                    '</pre></div>'
+                )
             else:
-                self.assertEqual(md.convert(text),
+                self.assertEqual(
+                    md.convert(text),
                     '<pre class="codehilite"><code class="language-python">#line 1\n'
                     '#line 2\n'
-                    '#line 3</code></pre>')
+                    '#line 3</code></pre>'
+                )
+
 
 class TestHeaderId(unittest.TestCase):
     """ Test HeaderId Extension. """
@@ -331,53 +400,10 @@ class TestHeaderId(unittest.TestCase):
         """ Test Basic HeaderID """
 
         text = "# Some Header #"
-        self.assertEqual(self.md.convert(text),
-            '<h1 id="some-header">Some Header</h1>')
-
-    def testUniqueFunc(self):
-        """ Test 'unique' function. """
-        from markdown.extensions.headerid import unique
-        ids = set(['foo'])
-        self.assertEqual(unique('foo', ids), 'foo_1')
-        self.assertEqual(ids, set(['foo', 'foo_1']))
-
-    def testUniqueIds(self):
-        """ Test Unique IDs. """
-
-        text = '#Header\n#Header\n#Header'
-        self.assertEqual(self.md.convert(text),
-            '<h1 id="header">Header</h1>\n'
-            '<h1 id="header_1">Header</h1>\n'
-            '<h1 id="header_2">Header</h1>')
-
-    def testBaseLevel(self):
-        """ Test Header Base Level. """
-
-        text = '#Some Header\n## Next Level'
         self.assertEqual(
-            markdown.markdown(text, [markdown.extensions.headerid.HeaderIdExtension(level=3)]),
-            '<h3 id="some-header">Some Header</h3>\n'
-            '<h4 id="next-level">Next Level</h4>')
-
-    def testHeaderInlineMarkup(self):
-        """ Test Header IDs with inline markup. """
-
-        text = '#Some *Header* with [markup](http://example.com).'
-        self.assertEqual(self.md.convert(text),
-            '<h1 id="some-header-with-markup">Some <em>Header</em> with '
-            '<a href="http://example.com">markup</a>.</h1>')
-
-    def testHtmlEntities(self):
-        """ Test HeaderIDs with HTML Entities. """
-        text = '# Foo &amp; bar'
-        self.assertEqual(self.md.convert(text),
-            '<h1 id="foo-bar">Foo &amp; bar</h1>')
-
-    def testRawHtml(self):
-        """ Test HeaderIDs with raw HTML. """
-        text = '# Foo <b>Bar</b> Baz.'
-        self.assertEqual(self.md.convert(text),
-            '<h1 id="foo-bar-baz">Foo <b>Bar</b> Baz.</h1>')
+            self.md.convert(text),
+            '<h1 id="some-header">Some Header</h1>'
+        )
 
     def testNoAutoIds(self):
         """ Test HeaderIDs with no auto generated IDs. """
@@ -386,7 +412,8 @@ class TestHeaderId(unittest.TestCase):
         self.assertEqual(
             markdown.markdown(text, [markdown.extensions.headerid.HeaderIdExtension(forceid=False)]),
             '<h1>Some Header</h1>\n'
-            '<h1>Another Header</h1>')
+            '<h1>Another Header</h1>'
+        )
 
     def testHeaderIdWithMetaData(self):
         """ Test Header IDs with MetaData extension. """
@@ -395,20 +422,27 @@ class TestHeaderId(unittest.TestCase):
 header_forceid: Off
 
 # A Header'''
-        self.assertEqual(markdown.markdown(text, ['markdown.extensions.headerid', 'markdown.extensions.meta']),
-            '<h2>A Header</h2>')
+        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']),
+        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>')
+            '<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']),
+        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>')
+            '<h1 class="bar" id="header2">Header2</h1>'
+        )
+
 
 class TestMetaData(unittest.TestCase):
     """ Test MetaData extension. """
@@ -425,20 +459,49 @@ Author: Waylan Limberg
 Blank_Data:
 
 The body. This is paragraph one.'''
-        self.assertEqual(self.md.convert(text),
-            '<p>The body. This is paragraph one.</p>')
-        self.assertEqual(self.md.Meta,
-            {'author': ['Waylan Limberg', 'John Doe'],
-             'blank_data': [''],
-             'title': ['A Test Doc.']})
+        self.assertEqual(
+            self.md.convert(text),
+            '<p>The body. This is paragraph one.</p>'
+        )
+        self.assertEqual(
+            self.md.Meta, {
+                'author': ['Waylan Limberg', 'John Doe'],
+                'blank_data': [''],
+                'title': ['A Test Doc.']
+            }
+        )
+
+    def testYamlMetaData(self):
+        """ Test metadata specified as simple YAML. """
+
+        text = '''---
+Title: A Test Doc.
+Author: [Waylan Limberg, John Doe]
+Blank_Data:
+---
+
+The body. This is paragraph one.'''
+        self.assertEqual(
+            self.md.convert(text),
+            '<p>The body. This is paragraph one.</p>'
+        )
+        self.assertEqual(
+            self.md.Meta, {
+                'author': ['[Waylan Limberg, John Doe]'],
+                'blank_data': [''],
+                'title': ['A Test Doc.']
+            }
+        )
 
     def testMissingMetaData(self):
         """ Test document without Meta Data. """
 
         text = '    Some Code - not extra lines of meta data.'
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<pre><code>Some Code - not extra lines of meta data.\n'
-            '</code></pre>')
+            '</code></pre>'
+        )
         self.assertEqual(self.md.Meta, {})
 
     def testMetaDataWithoutNewline(self):
@@ -447,6 +510,25 @@ The body. This is paragraph one.'''
         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
+---
+
+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
+            }
+        )
+
 
 class TestWikiLinks(unittest.TestCase):
     """ Test Wikilinks Extension. """
@@ -458,24 +540,34 @@ class TestWikiLinks(unittest.TestCase):
     def testBasicWikilinks(self):
         """ Test [[wikilinks]]. """
 
-        self.assertEqual(self.md.convert(self.text),
+        self.assertEqual(
+            self.md.convert(self.text),
             '<p>Some text with a '
-            '<a class="wikilink" href="/WikiLink/">WikiLink</a>.</p>')
+            '<a class="wikilink" href="/WikiLink/">WikiLink</a>.</p>'
+        )
 
     def testWikilinkWhitespace(self):
         """ Test whitespace in wikilinks. """
-        self.assertEqual(self.md.convert('[[ foo bar_baz ]]'),
-            '<p><a class="wikilink" href="/foo_bar_baz/">foo bar_baz</a></p>')
-        self.assertEqual(self.md.convert('foo [[ ]] bar'),
-            '<p>foo  bar</p>')
+        self.assertEqual(
+            self.md.convert('[[ foo bar_baz ]]'),
+            '<p><a class="wikilink" href="/foo_bar_baz/">foo bar_baz</a></p>'
+        )
+        self.assertEqual(
+            self.md.convert('foo [[ ]] bar'),
+            '<p>foo  bar</p>'
+        )
 
     def testSimpleSettings(self):
         """ Test Simple Settings. """
 
-        self.assertEqual(markdown.markdown(self.text,
-            [markdown.extensions.wikilinks.WikiLinkExtension(base_url='/wiki/', 
-                                                             end_url='.html', 
-                                                             html_class='foo')]),
+        self.assertEqual(markdown.markdown(
+            self.text, [
+                markdown.extensions.wikilinks.WikiLinkExtension(
+                    base_url='/wiki/',
+                    end_url='.html',
+                    html_class='foo')
+                ]
+            ),
             '<p>Some text with a '
             '<a class="foo" href="/wiki/WikiLink.html">WikiLink</a>.</p>')
 
@@ -483,15 +575,21 @@ class TestWikiLinks(unittest.TestCase):
         """ Test Complex Settings. """
 
         md = markdown.Markdown(
-            extensions = ['markdown.extensions.wikilinks'],
-            extension_configs = {'markdown.extensions.wikilinks': [
-                                        ('base_url', 'http://example.com/'),
-                                        ('end_url', '.html'),
-                                        ('html_class', '') ]},
-            safe_mode = True)
-        self.assertEqual(md.convert(self.text),
+            extensions=['markdown.extensions.wikilinks'],
+            extension_configs={
+                'markdown.extensions.wikilinks': [
+                    ('base_url', 'http://example.com/'),
+                    ('end_url', '.html'),
+                    ('html_class', '')
+                ]
+            },
+            safe_mode=True
+        )
+        self.assertEqual(
+            md.convert(self.text),
             '<p>Some text with a '
-            '<a href="http://example.com/WikiLink.html">WikiLink</a>.</p>')
+            '<a href="http://example.com/WikiLink.html">WikiLink</a>.</p>'
+        )
 
     def testWikilinksMetaData(self):
         """ test MetaData with Wikilinks Extension. """
@@ -502,25 +600,33 @@ wiki_html_class:
 
 Some text with a [[WikiLink]]."""
         md = markdown.Markdown(extensions=['markdown.extensions.meta', 'markdown.extensions.wikilinks'])
-        self.assertEqual(md.convert(text),
+        self.assertEqual(
+            md.convert(text),
             '<p>Some text with a '
-            '<a href="http://example.com/WikiLink.html">WikiLink</a>.</p>')
+            '<a href="http://example.com/WikiLink.html">WikiLink</a>.</p>'
+        )
 
         # MetaData should not carry over to next document:
-        self.assertEqual(md.convert("No [[MetaData]] here."),
+        self.assertEqual(
+            md.convert("No [[MetaData]] here."),
             '<p>No <a class="wikilink" href="/MetaData/">MetaData</a> '
-            'here.</p>')
+            'here.</p>'
+        )
 
     def testURLCallback(self):
         """ Test used of a custom URL builder. """
-        
+
         from markdown.extensions.wikilinks import WikiLinkExtension
 
         def my_url_builder(label, base, end):
             return '/bar/'
+
         md = markdown.Markdown(extensions=[WikiLinkExtension(build_url=my_url_builder)])
-        self.assertEqual(md.convert('[[foo]]'),
-            '<p><a class="wikilink" href="/bar/">foo</a></p>')
+        self.assertEqual(
+            md.convert('[[foo]]'),
+            '<p><a class="wikilink" href="/bar/">foo</a></p>'
+        )
+
 
 class TestAdmonition(unittest.TestCase):
     """ Test Admonition Extension. """
@@ -538,74 +644,237 @@ class TestAdmonition(unittest.TestCase):
         for test, expected in tests:
             self.assertEqual(RE.match(test).groups(), expected)
 
+
 class TestTOC(unittest.TestCase):
     """ Test TOC Extension. """
-    
+
     def setUp(self):
         self.md = markdown.Markdown(extensions=['markdown.extensions.toc'])
 
     def testMarker(self):
         """ Test TOC with a Marker. """
         text = '[TOC]\n\n# Header 1\n\n## Header 2'
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<div class="toc">\n'
-              '<ul>\n'
-                '<li><a href="#header-1">Header 1</a>'
-                  '<ul>\n'
-                    '<li><a href="#header-2">Header 2</a></li>\n'
-                  '</ul>\n'
-                '</li>\n'
-              '</ul>\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'
             '<h1 id="header-1">Header 1</h1>\n'
-            '<h2 id="header-2">Header 2</h2>')
-    
+            '<h2 id="header-2">Header 2</h2>'
+        )
+
     def testNoMarker(self):
         """ Test TOC without a Marker. """
         text = '# Header 1\n\n## Header 2'
-        self.assertEqual(self.md.convert(text),
+        self.assertEqual(
+            self.md.convert(text),
             '<h1 id="header-1">Header 1</h1>\n'
-            '<h2 id="header-2">Header 2</h2>')
-        self.assertEqual(self.md.toc,
+            '<h2 id="header-2">Header 2</h2>'
+        )
+        self.assertEqual(
+            self.md.toc,
             '<div class="toc">\n'
-              '<ul>\n'
-                '<li><a href="#header-1">Header 1</a>'
-                  '<ul>\n'
-                    '<li><a href="#header-2">Header 2</a></li>\n'
-                  '</ul>\n'
-                '</li>\n'
-              '</ul>\n'
-            '</div>\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'
+        )
+
+    def testAlternateMarker(self):
+        """ Test TOC with user defined marker. """
+        md = markdown.Markdown(
+            extensions=[markdown.extensions.toc.TocExtension(marker='{{marker}}')]
+        )
+        text = '{{marker}}\n\n# Header 1\n\n## Header 2'
+        self.assertEqual(
+            md.convert(text),
+            '<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'
+            '<h1 id="header-1">Header 1</h1>\n'
+            '<h2 id="header-2">Header 2</h2>'
+        )
+
+    def testDisabledMarker(self):
+        """ Test TOC with disabled marker. """
+        md = markdown.Markdown(
+            extensions=[markdown.extensions.toc.TocExtension(marker='')]
+        )
+        text = '[TOC]\n\n# Header 1\n\n## Header 2'
+        self.assertEqual(
+            md.convert(text),
+            '<p>[TOC]</p>\n'
+            '<h1 id="header-1">Header 1</h1>\n'
+            '<h2 id="header-2">Header 2</h2>'
+        )
+        self.assertTrue(md.toc.startswith('<div class="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.md.reset()
+        self.assertEqual(self.md.toc, '')
+
+    def testUniqueIds(self):
+        """ Test Unique IDs. """
+
+        text = '#Header\n#Header\n#Header'
+        self.assertEqual(
+            self.md.convert(text),
+            '<h1 id="header">Header</h1>\n'
+            '<h1 id="header_1">Header</h1>\n'
+            '<h1 id="header_2">Header</h1>'
+        )
+
+    def testHtmlEntities(self):
+        """ Test Headers with HTML Entities. """
+        text = '# Foo &amp; bar'
+        self.assertEqual(
+            self.md.convert(text),
+            '<h1 id="foo-bar">Foo &amp; bar</h1>'
+        )
+
+    def testRawHtml(self):
+        """ Test Headers with raw HTML. """
+        text = '# Foo <b>Bar</b> Baz.'
+        self.assertEqual(
+            self.md.convert(text),
+            '<h1 id="foo-bar-baz">Foo <b>Bar</b> Baz.</h1>'
+        )
+
+    def testBaseLevel(self):
+        """ Test Header Base Level. """
+        md = markdown.Markdown(
+            extensions=[markdown.extensions.toc.TocExtension(baselevel=5)]
+        )
+        text = '# Some Header\n\n## Next Level\n\n### Too High'
+        self.assertEqual(
+            md.convert(text),
+            '<h5 id="some-header">Some Header</h5>\n'
+            '<h6 id="next-level">Next Level</h6>\n'
+            '<h6 id="too-high">Too High</h6>'
+        )
+        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
+                    '<li><a href="#too-high">Too High</a></li>\n'      # noqa
+                  '</ul>\n'                                            # noqa
+                '</li>\n'                                              # noqa
+              '</ul>\n'                                                # noqa
+            '</div>\n'
+        )
+
+    def testHeaderInlineMarkup(self):
+        """ Test Headers with inline markup. """
+
+        text = '#Some *Header* with [markup](http://example.com).'
+        self.assertEqual(
+            self.md.convert(text),
+            '<h1 id="some-header-with-markup">Some <em>Header</em> with '
+            '<a href="http://example.com">markup</a>.</h1>'
+        )
+
+    def testAnchorLink(self):
+        """ Test TOC Anchorlink. """
+        md = markdown.Markdown(
+            extensions=[markdown.extensions.toc.TocExtension(anchorlink=True)]
+        )
+        text = '# Header 1\n\n## Header *2*'
+        self.assertEqual(
+            md.convert(text),
+            '<h1 id="header-1"><a class="toclink" href="#header-1">Header 1</a></h1>\n'
+            '<h2 id="header-2"><a class="toclink" href="#header-2">Header <em>2</em></a></h2>'
+        )
+
+    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>'))
+
+    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 }'
+        self.assertEqual(
+            md.convert(text),
+            '<h1 id="header-1">Header 1</h1>\n'
+            '<h2 id="foo">Header 2</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
+            '</div>\n'
+        )
+
+    def testUniqueFunc(self):
+        """ Test 'unique' function. """
+        from markdown.extensions.toc import unique
+        ids = set(['foo'])
+        self.assertEqual(unique('foo', ids), 'foo_1')
+        self.assertEqual(ids, set(['foo', 'foo_1']))
 
 
 class TestSmarty(unittest.TestCase):
     def setUp(self):
         config = {
-                'markdown.extensions.smarty': [
-                    ('smart_angled_quotes', True),
-                    ('substitutions', {
-                        'ndash': '\u2013',
-                        'mdash': '\u2014',
-                        'ellipsis': '\u2026',
-                        'left-single-quote': '&sbquo;', # sb is not a typo!
-                        'right-single-quote': '&lsquo;',
-                        'left-double-quote': '&bdquo;',
-                        'right-double-quote': '&ldquo;',
-                        'left-angle-quote': '[',
-                        'right-angle-quote': ']',
-                    }),]
+            'markdown.extensions.smarty': [
+                ('smart_angled_quotes', True),
+                ('substitutions', {
+                    'ndash': '\u2013',
+                    'mdash': '\u2014',
+                    'ellipsis': '\u2026',
+                    'left-single-quote': '&sbquo;',  # sb is not a typo!
+                    'right-single-quote': '&lsquo;',
+                    'left-double-quote': '&bdquo;',
+                    'right-double-quote': '&ldquo;',
+                    'left-angle-quote': '[',
+                    'right-angle-quote': ']',
+                }),
+            ]
         }
-        self.md = markdown.Markdown(extensions=['markdown.extensions.smarty'],
-                                    extension_configs=config)
-        
+        self.md = markdown.Markdown(
+            extensions=['markdown.extensions.smarty'],
+            extension_configs=config
+        )
+
     def testCustomSubstitutions(self):
-        text = \
-"""<< The "Unicode char of the year 2014"
+        text = """<< The "Unicode char of the year 2014"
 is the 'mdash': ---
 Must not be confused with 'ndash'  (--) ... >>
 """
-        correct = \
-"""<p>[ The &bdquo;Unicode char of the year 2014&ldquo;
+        correct = """<p>[ The &bdquo;Unicode char of the year 2014&ldquo;
 is the &sbquo;mdash&lsquo;: \u2014
 Must not be confused with &sbquo;ndash&lsquo;  (\u2013) \u2026 ]</p>"""
         self.assertEqual(self.md.convert(text), correct)
diff --git a/tox.ini b/tox.ini
index 5ccefce2ef9922afcd00c5e08ffb7f701f3f71ae..c4384a2180f0a936735b36b6d5f423304b10b714 100644 (file)
--- a/tox.ini
+++ b/tox.ini
@@ -1,11 +1,19 @@
 [tox]
-envlist = py27, py31, py32, py33, py34
+envlist = py27, py32, py33, py34, pypy, flake8, checkspelling
 
 [testenv]
 downloadcache = {toxworkdir}/cache
-deps = nose
-       coverage
-       pyyaml
-       pytidylib
+deps = -rtest-requirements.txt
 commands = coverage run --source=markdown {toxinidir}/run-tests.py {posargs}
            coverage report --show-missing
+
+[testenv:flake8]
+deps = flake8
+commands = flake8 {toxinidir}/markdown {toxinidir}/tests {toxinidir}/setup.py {toxinidir}/run-tests.py
+
+[testenv:checkspelling]
+deps =
+commands = {toxinidir}/checkspelling.sh
+
+[flake8]
+max-line-length = 119
\ No newline at end of file