aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--markdown/__init__.py187
-rw-r--r--markdown/__main__.py53
-rw-r--r--markdown/__version__.py3
-rw-r--r--markdown/blockparser.py51
-rw-r--r--markdown/blockprocessors.py125
-rw-r--r--markdown/extensions/__init__.py33
-rw-r--r--markdown/extensions/abbr.py26
-rw-r--r--markdown/extensions/admonition.py14
-rw-r--r--markdown/extensions/attr_list.py30
-rw-r--r--markdown/extensions/codehilite.py77
-rw-r--r--markdown/extensions/def_list.py14
-rw-r--r--markdown/extensions/extra.py6
-rw-r--r--markdown/extensions/fenced_code.py34
-rw-r--r--markdown/extensions/footnotes.py102
-rw-r--r--markdown/extensions/headerid.py28
-rw-r--r--markdown/extensions/meta.py16
-rw-r--r--markdown/extensions/nl2br.py6
-rw-r--r--markdown/extensions/sane_lists.py9
-rw-r--r--markdown/extensions/smart_strong.py12
-rw-r--r--markdown/extensions/smarty.py76
-rw-r--r--markdown/extensions/tables.py14
-rw-r--r--markdown/extensions/toc.py93
-rw-r--r--markdown/extensions/wikilinks.py31
-rw-r--r--markdown/inlinepatterns.py144
-rw-r--r--markdown/odict.py12
-rw-r--r--markdown/postprocessors.py22
-rw-r--r--markdown/preprocessors.py58
-rw-r--r--markdown/serializers.py32
-rw-r--r--markdown/treeprocessors.py53
-rw-r--r--markdown/util.py46
-rwxr-xr-xrun-tests.py12
-rwxr-xr-xsetup.py127
-rw-r--r--tests/__init__.py67
-rw-r--r--tests/plugins.py50
34 files changed, 955 insertions, 708 deletions
diff --git a/markdown/__init__.py b/markdown/__init__.py
index 3ea8e05..0c4f271 100644
--- a/markdown/__init__.py
+++ b/markdown/__init__.py
@@ -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 # flake8: noqa
import codecs
import sys
import logging
@@ -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,14 @@ 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 pending depreacted in '
+ 'Markdown and will be deprecated in version 2.6. '
+ 'Use keyword arguments only.',
+ PendingDeprecationWarning)
# Loop through kwargs and assign defaults
for option, default in self.option_defaults.items():
@@ -131,16 +136,19 @@ 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 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)
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 pending '
+ 'deprecation in Python-Markdown and will be '
+ 'deprecated in version 2.6 along with "safe_mode".',
+ PendingDeprecationWarning)
self.registeredExtensions = []
self.docType = ""
@@ -180,8 +188,10 @@ class Markdown(object):
ext = self.build_extension(ext, configs.get(ext, {}))
if isinstance(ext, Extension):
ext.extendMarkdown(self, globals())
- logger.debug('Successfully loaded extension "%s.%s".'
- % (ext.__class__.__module__, ext.__class__.__name__))
+ 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,63 +206,85 @@ 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 '
+ 'pending deprecation. It is recommended that you '
+ 'pass an instance of the extension class to '
+ 'Markdown or use the "extension_configs" keyword. '
+ 'The current behavior will be deprecated in '
+ 'version 2.6 and raise an error in version 2.7. '
+ 'See the Release Notes for Python-Markdown version '
+ '2.5 for more info.', PendingDeprecationWarning)
# Get class name (if provided): `path.to.module:ClassName`
- ext_name, class_name = ext_name.split(':', 1) if ':' in ext_name else (ext_name, '')
+ 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)
- # For backward compat (until deprecation) check that this is an extension
- if '.' not in ext_name and not (hasattr(module, 'extendMarkdown') or (class_name and hasattr(module, class_name))):
- # We have a name conflict (eg: extensions=['tables'] and PyTables is installed)
+ 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, 'extendMarkdown')
+ 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 pending deprecation. Use the '
+ 'full path to the extension with Python\'s dot '
+ 'notation (eg: "%s" instead of "%s"). The '
+ 'current behavior will be deprecated in '
+ 'version 2.6 and raise an error in version '
+ '2.7. See the Release Notes for '
+ 'Python-Markdown version 2.5 for more info.' %
+ (module_name, ext_name),
+ PendingDeprecationWarning)
except ImportError:
# Preppend `mdx_` to name
module_name_old_style = '_'.join(['mdx', ext_name])
- try:
+ 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 behavuor of appending "mdx_" '
+ 'to an extension name is pending '
+ 'deprecation. Use the full path to the '
+ 'extension with Python\'s dot notation '
+ '(eg: "%s" instead of "%s"). The current '
+ 'behavior will be deprecated in version '
+ '2.6 and raise an error in version 2.7. '
+ 'See the Release Notes for Python-Markdown '
+ 'version 2.5 for more info.' %
+ (module_name_old_style, ext_name),
+ PendingDeprecationWarning)
except ImportError as e:
- message = "Failed loading extension '%s' from '%s', '%s' or '%s'" \
- % (ext_name, ext_name, module_name, module_name_old_style)
+ 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
@@ -297,8 +329,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
@@ -354,16 +386,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():
@@ -407,7 +441,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)
@@ -426,7 +460,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).
@@ -446,6 +480,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.
@@ -489,12 +524,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 pending depreacted in '
+ 'Markdown and will be deprecated in version 2.6. '
+ 'Use keyword arguments only.',
+ PendingDeprecationWarning)
md = Markdown(**kwargs)
md.convertFile(kwargs.get('input', None),
kwargs.get('output', None),
kwargs.get('encoding', None))
-
diff --git a/markdown/__main__.py b/markdown/__main__.py
index d085540..3586ead 100644
--- a/markdown/__main__.py
+++ b/markdown/__main__.py
@@ -8,15 +8,16 @@ import markdown
import sys
import optparse
import codecs
-try:
+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
-logger = logging.getLogger('MARKDOWN')
+logger = logging.getLogger('MARKDOWN')
+
def parse_options(args=None, values=None):
"""
@@ -27,7 +28,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,24 +37,28 @@ 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",
@@ -75,11 +80,14 @@ 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
@@ -92,20 +100,23 @@ def parse_options(args=None, values=None):
'output_format': options.output_format,
'lazy_ol': options.lazy_ol}, options.verbose
-def run(): #pragma: no cover
+
+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())
# Run
markdown.markdownFromFile(**options)
-if __name__ == '__main__': #pragma: no cover
- # Support running module as a commandline command.
+
+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]`.
# Python 2.7 & 3.x do: `python -m markdown [options] [args]`.
run()
diff --git a/markdown/__version__.py b/markdown/__version__.py
index 397dfb3..33d0ac5 100644
--- a/markdown/__version__.py
+++ b/markdown/__version__.py
@@ -1,12 +1,13 @@
#
# 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, 2, 'final', 0)
+
def _get_version():
" Returns a PEP 386-compliant version number from version_info. "
assert len(version_info) == 5
diff --git a/markdown/blockparser.py b/markdown/blockparser.py
index 4504a16..32d3254 100644
--- a/markdown/blockparser.py
+++ b/markdown/blockparser.py
@@ -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
-
-
diff --git a/markdown/blockprocessors.py b/markdown/blockprocessors.py
index 08fbcf8..9c137df 100644
--- a/markdown/blockprocessors.py
+++ b/markdown/blockprocessors.py
@@ -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] 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:
#
diff --git a/markdown/extensions/__init__.py b/markdown/extensions/__init__.py
index 03b2a4c..a823fef 100644
--- a/markdown/extensions/__init__.py
+++ b/markdown/extensions/__init__.py
@@ -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,7 +19,7 @@ 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. """
@@ -26,23 +27,26 @@ class Extension(object):
# (there only ever used to be one so we use arg[0])
if len(args):
self.setConfigs(args[0])
- warnings.warn('Extension classes accepting positional args is pending Deprecation. '
- 'Each setting should be passed into the Class as a keyword. Positional '
- 'args will be deprecated in version 2.6 and raise an error in version '
- '2.7. See the Release Notes for Python-Markdown version 2.5 for more info.',
+ warnings.warn('Extension classes accepting positional args is '
+ 'pending Deprecation. Each setting should be '
+ 'passed into the Class as a keyword. Positional '
+ 'args will be deprecated in version 2.6 and raise '
+ 'an error in version 2.7. See the Release Notes for '
+ 'Python-Markdown version 2.5 for more info.',
PendingDeprecationWarning)
# check for configs kwarg for backward compat.
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 '
+ warnings.warn('Extension classes accepting a dict on the single '
+ 'keyword "config" is pending Deprecation. Each '
+ 'setting should be passed into the Class as a '
+ 'keyword directly. The "config" keyword will be '
+ 'deprecated in version 2.6 and raise an error in '
+ 'version 2.7. See the Release Notes for '
'Python-Markdown version 2.5 for more info.',
PendingDeprecationWarning)
# finally, use kwargs
self.setConfigs(kwargs)
-
def getConfig(self, key, default=''):
""" Return a setting for the given key or an empty string. """
@@ -88,6 +92,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__)
+ )
diff --git a/markdown/extensions/abbr.py b/markdown/extensions/abbr.py
index 58dd0aa..353d126 100644
--- a/markdown/extensions/abbr.py
+++ b/markdown/extensions/abbr.py
@@ -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)
diff --git a/markdown/extensions/admonition.py b/markdown/extensions/admonition.py
index 189f2c2..8fe3aee 100644
--- a/markdown/extensions/admonition.py
+++ b/markdown/extensions/admonition.py
@@ -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 # flake8: 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 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)
-
diff --git a/markdown/extensions/attr_list.py b/markdown/extensions/attr_list.py
index 59da3b4..395259a 100644
--- a/markdown/extensions/attr_list.py
+++ b/markdown/extensions/attr_list.py
@@ -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):
diff --git a/markdown/extensions/codehilite.py b/markdown/extensions/codehilite.py
index 0c3df7e..211db21 100644
--- a/markdown/extensions/codehilite.py
+++ b/markdown/extensions/codehilite.py
@@ -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,7 +19,7 @@ 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
@@ -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):
self.src = src
self.lang = lang
self.linenums = linenums
@@ -132,9 +133,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):
"""
@@ -142,8 +143,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
+ a 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
@@ -156,9 +157,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'''
@@ -192,8 +193,9 @@ class CodeHilite(object):
self.src = "\n".join(lines).strip("\n")
-
# ------------------ The Markdown Extension -------------------------------
+
+
class HiliteTreeprocessor(Treeprocessor):
""" Hilight source code in code blocks. """
@@ -203,13 +205,15 @@ class HiliteTreeprocessor(Treeprocessor):
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)
+ 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
+ )
placeholder = self.markdown.htmlStash.store(code.hilite(),
safe=True)
# Clear codeblock in etree instance
@@ -226,13 +230,22 @@ 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"],
+ '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']
}
super(CodeHiliteExtension, self).__init__(*args, **kwargs)
@@ -247,4 +260,4 @@ class CodeHiliteExtension(Extension):
def makeExtension(*args, **kwargs):
- return CodeHiliteExtension(*args, **kwargs)
+ return CodeHiliteExtension(*args, **kwargs)
diff --git a/markdown/extensions/def_list.py b/markdown/extensions/def_list.py
index 22e2491..118b739 100644
--- a/markdown/extensions/def_list.py
+++ b/markdown/extensions/def_list.py
@@ -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
@@ -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)
-
diff --git a/markdown/extensions/extra.py b/markdown/extensions/extra.py
index 4044a87..de5db03 100644
--- a/markdown/extensions/extra.py
+++ b/markdown/extensions/extra.py
@@ -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)
diff --git a/markdown/extensions/fenced_code.py b/markdown/extensions/fenced_code.py
index 2aacca6..4af8891 100644
--- a/markdown/extensions/fenced_code.py
+++ b/markdown/extensions/fenced_code.py
@@ -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)
-
diff --git a/markdown/extensions/footnotes.py b/markdown/extensions/footnotes.py
index a59de97..4779f0c 100644
--- a/markdown/extensions/footnotes.py
+++ b/markdown/extensions/footnotes.py
@@ -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,7 +282,7 @@ 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):
@@ -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)
-
diff --git a/markdown/extensions/headerid.py b/markdown/extensions/headerid.py
index f7b7805..c9f2a21 100644
--- a/markdown/extensions/headerid.py
+++ b/markdown/extensions/headerid.py
@@ -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)
"""
@@ -41,18 +41,18 @@ def unique(id, ids):
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)
+ id = '%s_%d' % (m.group(1), int(m.group(2))+1)
else:
- id = '%s_%d'% (id, 1)
+ id = '%s_%d' % (id, 1)
ids.add(id)
return id
def itertext(elem):
- """ Loop through all children and return text only.
-
+ """ 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
@@ -103,7 +103,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 +110,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,11 +119,11 @@ 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)
@@ -146,4 +145,3 @@ class HeaderIdExtension(Extension):
def makeExtension(*args, **kwargs):
return HeaderIdExtension(*args, **kwargs)
-
diff --git a/markdown/extensions/meta.py b/markdown/extensions/meta.py
index bcc25a0..a733a8a 100644
--- a/markdown/extensions/meta.py
+++ b/markdown/extensions/meta.py
@@ -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)
"""
@@ -25,13 +25,16 @@ import re
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>.*)')
+
class MetaExtension (Extension):
""" Meta-Data extension for Python-Markdown. """
def extendMarkdown(self, md, md_globals):
""" Add MetaPreprocessor to Markdown instance. """
- md.preprocessors.add("meta", MetaPreprocessor(md), ">normalize_whitespace")
+ md.preprocessors.add(
+ "meta", MetaPreprocessor(md), ">normalize_whitespace"
+ )
class MetaPreprocessor(Preprocessor):
@@ -44,7 +47,7 @@ class MetaPreprocessor(Preprocessor):
while lines:
line = lines.pop(0)
if line.strip() == '':
- break # blank line - done
+ break # blank line - done
m1 = META_RE.match(line)
if m1:
key = m1.group('key').lower().strip()
@@ -60,11 +63,10 @@ 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
self.markdown.Meta = meta
return lines
-
+
def makeExtension(*args, **kwargs):
return MetaExtension(*args, **kwargs)
-
diff --git a/markdown/extensions/nl2br.py b/markdown/extensions/nl2br.py
index 062a7e6..8acd60c 100644
--- a/markdown/extensions/nl2br.py
+++ b/markdown/extensions/nl2br.py
@@ -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)
-
diff --git a/markdown/extensions/sane_lists.py b/markdown/extensions/sane_lists.py
index 9eb3a11..213c8a6 100644
--- a/markdown/extensions/sane_lists.py
+++ b/markdown/extensions/sane_lists.py
@@ -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)
-
diff --git a/markdown/extensions/smart_strong.py b/markdown/extensions/smart_strong.py
index 331dae8..58570bb 100644
--- a/markdown/extensions/smart_strong.py
+++ b/markdown/extensions/smart_strong.py
@@ -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)
diff --git a/markdown/extensions/smarty.py b/markdown/extensions/smarty.py
index 00c330f..3d79061 100644
--- a/markdown/extensions/smarty.py
+++ b/markdown/extensions/smarty.py
@@ -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)
diff --git a/markdown/extensions/tables.py b/markdown/extensions/tables.py
index 57507e9..cbf711a 100644
--- a/markdown/extensions/tables.py
+++ b/markdown/extensions/tables.py
@@ -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)
-
diff --git a/markdown/extensions/toc.py b/markdown/extensions/toc.py
index f7fb675..99542ed 100644
--- a/markdown/extensions/toc.py
+++ b/markdown/extensions/toc.py
@@ -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)
"""
@@ -68,9 +68,11 @@ def order_toc_list(toc_list):
# Note current level as last
levels.append(current_level)
- # Level is the same, so append to the current parent (if available)
+ # 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)
+ (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
@@ -84,14 +86,14 @@ def order_toc_list(toc_list):
class TocTreeprocessor(Treeprocessor):
-
+
# Iterator wrapper to get parent and child all at once
def iterparent(self, root):
for parent in root.getiterator():
for child in parent:
yield parent, child
-
- def add_anchor(self, c, elem_id): #@ReservedAssignment
+
+ def add_anchor(self, c, elem_id): # @ReservedAssignment
anchor = etree.Element("a")
anchor.text = c.text
anchor.attrib["href"] = "#" + elem_id
@@ -105,12 +107,13 @@ 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):
# Add title to the div
if self.config["title"]:
@@ -129,20 +132,20 @@ 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"]
-
+
# Get a list of id attributes
used_ids = set()
for c in doc.getiterator():
@@ -160,7 +163,7 @@ class TocTreeprocessor(Treeprocessor):
# 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
+ # 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']:
@@ -169,32 +172,34 @@ class TocTreeprocessor(Treeprocessor):
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:
+
+ # Do not override pre-existing ids
+ if "id" not in c.attrib:
elem_id = stashedHTML2text(text, self.markdown)
- elem_id = unique(self.config["slugify"](elem_id, '-'), used_ids)
+ 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})
+ 'id': elem_id,
+ 'name': text})
if self.use_anchors:
self.add_anchor(c, elem_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 prettify:
+ prettify.run(div)
if not marker_found:
# serialize and attach to markdown instance.
toc = self.markdown.serializer(div)
@@ -204,26 +209,26 @@ class TocTreeprocessor(Treeprocessor):
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" : ["",
- "Title to insert into TOC <div> - "
- "Defaults to an empty string"],
- "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 - "
+ "Defaults to \"[TOC]\""],
+ "slugify": [slugify,
+ "Function to generate anchors based on header text - "
+ "Defaults to the headerid ext's slugify function."],
+ "title": ["",
+ "Title to insert into TOC <div> - "
+ "Defaults to an empty string"],
+ "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"]
}
super(TocExtension, self).__init__(*args, **kwargs)
@@ -232,8 +237,8 @@ class TocExtension(Extension):
tocext = self.TreeProcessorClass(md)
tocext.config = 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")
diff --git a/markdown/extensions/wikilinks.py b/markdown/extensions/wikilinks.py
index 64377cf..94e1b67 100644
--- a/markdown/extensions/wikilinks.py
+++ b/markdown/extensions/wikilinks.py
@@ -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)
diff --git a/markdown/inlinepatterns.py b/markdown/inlinepatterns.py
index c9d82fd..27690bf 100644
--- a/markdown/inlinepatterns.py
+++ b/markdown/inlinepatterns.py
@@ -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')
@@ -91,47 +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**
-EM_STRONG_RE = r'(\*|_)\2{2}(.+?)\2(.*?)\2{2}' # ***strongem*** or ***em*strong**
-STRONG_EM_RE = r'(\*|_)\2{2}(.+?)\2{2}(.*?)\2' # ***strong**em*
-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'((^| )(\*|_)( |$))'
-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
+# <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/][^\>]*?|\!--.*?--)\>)'
+
+# &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})."""
@@ -145,6 +184,7 @@ The pattern classes
-----------------------------------------------------------------------------
"""
+
class Pattern(object):
"""Base class that inline patterns subclass. """
@@ -180,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 """
@@ -190,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:
@@ -204,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:
@@ -239,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
@@ -251,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"
@@ -278,14 +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:
+ 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
@@ -294,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)
@@ -351,7 +394,7 @@ class LinkPattern(Pattern):
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 ''
@@ -361,18 +404,20 @@ class LinkPattern(Pattern):
# 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):
@@ -396,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. """
@@ -413,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]
@@ -454,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>`).
@@ -480,4 +527,3 @@ class AutomailPattern(Pattern):
ord(letter) for letter in mailto])
el.set('href', mailto)
return el
-
diff --git a/markdown/odict.py b/markdown/odict.py
index b158e06..584ad7c 100644
--- a/markdown/odict.py
+++ b/markdown/odict.py
@@ -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()
diff --git a/markdown/postprocessors.py b/markdown/postprocessors.py
index 7b568ad..2d4dcb5 100644
--- a/markdown/postprocessors.py
+++ b/markdown/postprocessors.py
@@ -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
diff --git a/markdown/preprocessors.py b/markdown/preprocessors.py
index ed11c39..9330a91 100644
--- a/markdown/preprocessors.py
+++ b/markdown/preprocessors.py
@@ -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
@@ -188,7 +192,7 @@ class HtmlBlockPreprocessor(Preprocessor):
items = []
left_tag = ''
right_tag = ''
- in_tag = False # flag
+ in_tag = False # flag
while text:
block = text[0]
@@ -204,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,
@@ -213,13 +217,13 @@ class HtmlBlockPreprocessor(Preprocessor):
# keep checking conditions below and maybe just append
if data_index < len(block) \
- and (util.isBlockLevel(left_tag)
- or left_tag == '--'):
+ 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
@@ -240,14 +244,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:
@@ -317,11 +321,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)
@@ -339,4 +345,4 @@ class ReferencePreprocessor(Preprocessor):
else:
new_text.append(line)
- return new_text #+ "\n"
+ return new_text # + "\n"
diff --git a/markdown/serializers.py b/markdown/serializers.py
index f53ae31..1e8d9dd 100644
--- a/markdown/serializers.py
+++ b/markdown/serializers.py
@@ -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")
diff --git a/markdown/treeprocessors.py b/markdown/treeprocessors.py
index 303e460..d06f192 100644
--- a/markdown/treeprocessors.py
+++ b/markdown/treeprocessors.py
@@ -38,7 +38,7 @@ class Treeprocessor(util.Processor):
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):
@@ -183,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
@@ -199,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
@@ -245,11 +247,13 @@ class InlineProcessor(Treeprocessor):
for child in [node] + list(node):
if not isString(node):
if child.text:
- child.text = self.__handleInline(child.text,
- patternIndex + 1)
+ 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())
@@ -262,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.")
@@ -282,11 +286,14 @@ 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:
@@ -306,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
diff --git a/markdown/util.py b/markdown/util.py
index 0541e7b..e3a4689 100644
--- a/markdown/util.py
+++ b/markdown/util.py
@@ -10,11 +10,11 @@ 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
+else: # pragma: no cover
string_type = basestring
text_type = unicode
int2str = unichr
@@ -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
diff --git a/run-tests.py b/run-tests.py
index 2727a20..5748953 100755
--- a/run-tests.py
+++ b/run-tests.py
@@ -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:
diff --git a/setup.py b/setup.py
index f635b48..7e8862c 100755
--- 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,6 +42,7 @@ 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):
@@ -50,13 +53,15 @@ 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)
except Exception:
- _, err, _ = sys.exc_info() # for both 2.x & 3.x compatability
+ _, err, _ = sys.exc_info() # for both 2.x & 3.x compatability
print ('ERROR: Unable to create %s: %s' % (bat_path, err))
@@ -79,9 +84,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 +102,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
@@ -145,7 +152,14 @@ class build_docs(Command):
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':
@@ -192,11 +206,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 +228,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='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'
+ ]
+)
diff --git a/tests/__init__.py b/tests/__init__.py
index 15710ac..f759ceb 100644
--- a/tests/__init__.py
+++ b/tests/__init__.py
@@ -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()])
-
diff --git a/tests/plugins.py b/tests/plugins.py
index 12bac55..90c5c0d 100644
--- a/tests/plugins.py
+++ b/tests/plugins.py
@@ -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>')
-