.
Support
-------
You may report bugs, ask for help, and discuss various other issues on the [bug tracker][].
[bug tracker]: http://github.com/Python-Markdown/markdown/issues
Code of Conduct
---------------
Everyone interacting in the Python-Markdown project's codebases, issue trackers,
and mailing lists is expected to follow the [Code of Conduct].
Markdown-3.1.1/doc-requirements.txt 0000644 0000765 0000024 00000000032 13432642364 017745 0 ustar waylan staff 0000000 0000000 mkdocs>=1.0
mkdocs-nature
Markdown-3.1.1/docs/ 0000755 0000765 0000024 00000000000 13470647777 014672 5 ustar waylan staff 0000000 0000000 Markdown-3.1.1/docs/authors.md 0000644 0000765 0000024 00000004342 13432642364 016665 0 ustar waylan staff 0000000 0000000 title: Authors
Primary Authors
===============
* __[Waylan Limberg](https://github.com/waylan)__
@waylan is the current maintainer of the code and has written much of the
current code base, included a complete refactor of the core for version 2.0.
He started out by authoring many of the available extensions and later was
asked to join Yuri, where he began fixing numerous bugs, adding
documentation and making general improvements to the existing code base.
* __[Dmitry Shachnev](https://github.com/mitya57)__
@mitya57 joined the team after providing a number of helpful patches and has
been assisting with maintenance, reviewing pull requests and ticket triage.
* __[Isaac Muse](https://github.com/facelessuser)__
@facelessuser joined the team after providing a number of helpful patches
and has been assisting with maintenance, reviewing pull requests and ticket
triage.
* __[Yuri Takteyev](http://freewisdom.org/)__
Yuri wrote most of the code found in version 1.x while procrastinating his
Ph.D. Various pieces of his code still exist, most notably the basic
structure.
* __[Manfed Stienstra](http://www.dwerg.net/)__
Manfed wrote the original version of the script and is responsible for
various parts of the existing code base.
* __Artem Yunusov__
Artem, who as part of a 2008 GSoC project, refactored inline patterns,
replaced the NanoDOM with ElementTree support and made various other
improvements.
* __David Wolever__
David refactored the extension API and made other improvements
as he helped to integrate Markdown into Dr.Project.
Other Contributors
==================
The incomplete list of individuals below have provided patches or otherwise
contributed to the project prior to the project being hosted on GitHub. See the
GitHub commit log for a list of recent contributors. We would like to thank
everyone who has contributed to the project in any way.
* Eric Abrahamsen
* Jeff Balogh
* Sergej Chodarev
* Chris Clark
* Tiago Cogumbreiro
* Kjell Magne Fauske
* G. Clark Haynes
* Daniel Krech
* Steward Midwinter
* Jack Miller
* Neale Pickett
* Paul Stansifer
* John Szakmeister
* Malcolm Tredinnick
* Ben Wilson
* and many others who helped by reporting bugs
Markdown-3.1.1/docs/change_log/ 0000755 0000765 0000024 00000000000 13470647777 016760 5 ustar waylan staff 0000000 0000000 Markdown-3.1.1/docs/change_log/index.md 0000644 0000765 0000024 00000021600 13470646542 020375 0 ustar waylan staff 0000000 0000000 title: Change Log
Python-Markdown Change Log
=========================
May 20, 2019: Released version 3.1.1 (a bug-fix release).
* Fixed import failure in `setup.py` when the source directory is not
on `sys.path` (#823).
* Prefer public `packaging` module to pkg_resources' private copy of
it (#825).
Mar 25, 2019: Released version 3.1 ([Notes](release-3.1.md)).
Sept 28, 2018: Released version 3.0.1 (a bug-fix release).
* Brought back the `version` and `version_info` variables (#709).
* Added support for hexadecimal HTML entities (#712).
Sept 21, 2018: Released version 3.0 ([Notes](release-3.0.md)).
Jan 4, 2018: Released version 2.6.11 (a bug-fix release). Added a new
`BACKLINK-TITLE` option to the footnote extension so that non-English
users can provide a custom title to back links (see #610).
Dec 7, 2017: Released version 2.6.10 (a documentation update).
Aug 17, 2017: Released version 2.6.9 (a bug-fix release).
Jan 25, 2017: Released version 2.6.8 (a bug-fix release).
Sept 23, 2016: Released version 2.6.7 (a bug-fix release).
Mar 20, 2016: Released version 2.6.6 (a bug-fix release).
Nov 24, 2015: Released version 2.6.5 (a bug-fix release).
Nov 6, 2015: Released version 2.6.4 (a bug-fix release).
Oct 26, 2015: Released version 2.6.3 (a bug-fix release).
Apr 20, 2015: Released version 2.6.2 (a bug-fix release).
Mar 8, 2015: Released version 2.6.1 (a bug-fix release). The (new)
`yaml` option has been removed from the Meta-Data Extension as it was buggy
(see [#390](https://github.com/Python-Markdown/markdown/issues/390)).
Feb 19, 2015: Released version 2.6 ([Notes](release-2.6.md)).
Nov 19, 2014: Released version 2.5.2 (a bug-fix release).
Sept 26, 2014: Released version 2.5.1 (a bug-fix release).
Sept 12, 2014: Released version 2.5.0 ([Notes](release-2.5.md)).
Feb 16, 2014: Released version 2.4.0 ([Notes](release-2.4.md)).
Mar 22, 2013: Released version 2.3.1 (a bug-fix release).
Mar 14, 2013: Released version 2.3.0 ([Notes](release-2.3.md))
Nov 4, 2012: Released version 2.2.1 (a bug-fix release).
Jul 5, 2012: Released version 2.2.0 ([Notes](release-2.2.md)).
Jan 22, 2012: Released version 2.1.1 (a bug-fix release).
Nov 24, 2011: Released version 2.1.0 ([Notes](release-2.1.md)).
Oct 7, 2009: Released version 2.0.3. (a bug-fix release).
Sept 28, 2009: Released version 2.0.2 (a bug-fix release).
May 20, 2009: Released version 2.0.1 (a bug-fix release).
Mar 30, 2009: Released version 2.0 ([Notes](release-2.0.md)).
Mar 8, 2009: Release Candidate 2.0-rc-1.
Feb 2009: Added support for multi-level lists to new Blockprocessors.
Jan 2009: Added HTML 4 output as an option (thanks Eric Abrahamsen)
Nov 2008: Added Definition List ext. Replaced old core with Blockprocessors.
Broken up into multiple files.
Oct 2008: Changed logging behavior to work better with other systems.
Refactored tree traversing. Added `treap` implementation, then replaced with
OrderedDict. Renamed various processors to better reflect what they actually
do. Refactored footnote ext to match PHP Extra's output.
Sept 2008: Moved `prettifyTree` to a Postprocessor, replaced WikiLink ext
with WikiLinks (note the s) ext (uses bracketed links instead of CamelCase)
and various bug fixes.
August 18 2008: Reorganized directory structure. Added a 'docs' directory
and moved all extensions into a 'markdown-extensions' package.
Added additional documentation and a few bug fixes. (v2.0-beta)
August 4 2008: Updated included extensions to ElementTree. Added a
separate command line script. (v2.0-alpha)
July 2008: Switched from home-grown NanoDOM to ElementTree and
various related bugs (thanks Artem Yunusov).
June 2008: Fixed issues with nested inline patterns and cleaned
up testing framework (thanks Artem Yunusov).
May 2008: Added a number of additional extensions to the
distribution and other minor changes. Moved repository to git from svn.
Mar 2008: Refactored extension API to accept either an
extension name (as a string) or an instance of an extension
(Thanks David Wolever). Fixed various bugs and added doc strings.
Feb 2008: Various bug-fixes mostly regarding extensions.
Feb 18, 2008: Version 1.7.
Feb 13, 2008: A little code cleanup and better documentation
and inheritance for Preprocessors/Postprocessors.
Feb 9, 2008: Double-quotes no longer HTML escaped and raw HTML
honors ``, `<@foo>`, and `<%foo>` for those who run markdown on
template syntax.
Dec 12, 2007: Updated docs. Removed encoding argument from Markdown
and markdown as per list discussion. Clean up in prep for 1.7.
Nov 29, 2007: Added support for images inside links. Also fixed
a few bugs in the footnote extension.
Nov 19, 2007: `message` now uses python's logging module. Also removed
limit imposed by recursion in `_process_section()`. You can now parse as
long of a document as your memory can handle.
Nov 5, 2007: Moved `safe_mode` code to a `textPostprocessor` and added
escaping option.
Nov 3, 2007: Fixed convert method to accept empty strings.
Oct 30, 2007: Fixed `BOM` removal (thanks Malcolm Tredinnick). Fixed
infinite loop in bracket regular expression for inline links.
Oct 11, 2007: `LineBreaks` is now an `inlinePattern`. Fixed `HR` in
blockquotes. Refactored `_processSection` method (see tracker #1793419).
Oct 9, 2007: Added `textPreprocessor` (from 1.6b).
Oct 8, 2008: Fixed Lazy Blockquote. Fixed code block on first line.
Fixed empty inline image link.
Oct 7, 2007: Limit recursion on inline patterns. Added a 'safe' tag
to `htmlStash`.
March 18, 2007: Fixed or merged a bunch of minor bugs, including
multi-line comments and markup inside links. (Tracker #s: 1683066,
1671153, 1661751, 1627935, 1544371, 1458139.) -> v. 1.6b
Oct 10, 2006: Fixed a bug that caused some text to be lost after
comments. Added "safe mode" (user's HTML tags are removed).
Sept 6, 2006: Added exception for PHP tags when handling HTML blocks.
August 7, 2006: Incorporated Sergej Chodarev's patch to fix a problem
with ampersand normalization and HTML blocks.
July 10, 2006: Switched to using `optparse`. Added proper support for
Unicode.
July 9, 2006: Fixed the `" % _escape_cdata(text))
elif tag is ProcessingInstruction:
write("%s?>" % _escape_cdata(text))
elif tag is None:
if text:
write(_escape_cdata(text))
for e in elem:
_serialize_html(write, e, format)
else:
namespace_uri = None
if isinstance(tag, QName):
# QNAME objects store their data as a string: `{uri}tag`
if tag.text[:1] == "{":
namespace_uri, tag = tag.text[1:].split("}", 1)
else:
raise ValueError('QName objects must define a tag.')
write("<" + tag)
items = elem.items()
if items:
items = sorted(items) # lexical order
for k, v in items:
if isinstance(k, QName):
# Assume a text only QName
k = k.text
if isinstance(v, QName):
# Assume a text only QName
v = v.text
else:
v = _escape_attrib_html(v)
if k == v and format == 'html':
# handle boolean attributes
write(" %s" % v)
else:
write(' %s="%s"' % (k, v))
if namespace_uri:
write(' xmlns="%s"' % (_escape_attrib(namespace_uri)))
if format == "xhtml" and tag.lower() in HTML_EMPTY:
write(" />")
else:
write(">")
if text:
if tag.lower() in ["script", "style"]:
write(text)
else:
write(_escape_cdata(text))
for e in elem:
_serialize_html(write, e, format)
if tag.lower() not in HTML_EMPTY:
write("" + tag + ">")
if elem.tail:
write(_escape_cdata(elem.tail))
def _write_html(root, format="html"):
assert root is not None
data = []
write = data.append
_serialize_html(write, root, format)
return "".join(data)
# --------------------------------------------------------------------
# public functions
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")
Markdown-3.1.1/markdown/test_tools.py 0000644 0000765 0000024 00000016010 13330711640 020303 0 ustar waylan staff 0000000 0000000 # -*- coding: utf-8 -*-
"""
Python Markdown
A Python implementation of John Gruber's Markdown.
Documentation: https://python-markdown.github.io/
GitHub: https://github.com/Python-Markdown/markdown/
PyPI: https://pypi.org/project/Markdown/
Started by Manfred Stienstra (http://www.dwerg.net/).
Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
License: BSD (see LICENSE.md for details).
"""
from __future__ import absolute_import
import os
import io
import unittest
import textwrap
from . import markdown
try:
import tidylib
except ImportError:
tidylib = None
__all__ = ['TestCase', 'LegacyTestCase', 'Kwargs']
class TestCase(unittest.TestCase):
"""
A unittest.TestCase subclass with helpers for testing Markdown output.
Define `default_kwargs` as a dict of keywords to pass to Markdown for each
test. The defaults can be overridden on individual tests.
The `assertMarkdownRenders` method accepts the source text, the expected
output, and any keywords to pass to Markdown. The `default_kwargs` are used
except where overridden by `kwargs`. The ouput and expected ouput are passed
to `TestCase.assertMultiLineEqual`. An AssertionError is raised with a diff
if the actual output does not equal the expected output.
The `dedent` method is available to dedent triple-quoted strings if
necessary.
In all other respects, behaves as unittest.TestCase.
"""
default_kwargs = {}
def assertMarkdownRenders(self, source, expected, **kwargs):
"""
Test that source Markdown text renders to expected output with given keywords.
"""
kws = self.default_kwargs.copy()
kws.update(kwargs)
output = markdown(source, **kws)
self.assertMultiLineEqual(output, expected)
def dedent(self, text):
"""
Dedent text.
"""
# TODO: If/when actual output ends with a newline, then use:
# return textwrap.dedent(text.strip('/n'))
return textwrap.dedent(text).strip()
#########################
# Legacy Test Framework #
#########################
class Kwargs(dict):
""" A dict like class for holding keyword arguments. """
pass
def _normalize_whitespace(text):
""" Normalize whitespace for a string of html using tidylib. """
output, errors = tidylib.tidy_fragment(text, options={
'drop_empty_paras': 0,
'fix_backslash': 0,
'fix_bad_comments': 0,
'fix_uri': 0,
'join_styles': 0,
'lower_literals': 0,
'merge_divs': 0,
'output_xhtml': 1,
'quote_ampersand': 0,
'newline': 'LF'
})
return output
class LegacyTestMeta(type):
def __new__(cls, name, bases, dct):
def generate_test(infile, outfile, normalize, kwargs):
def test(self):
with io.open(infile, encoding="utf-8") as f:
input = f.read()
with io.open(outfile, encoding="utf-8") as f:
# Normalize line endings
# (on Windows, git may have altered line endings).
expected = f.read().replace("\r\n", "\n")
output = markdown(input, **kwargs)
if tidylib and normalize:
expected = _normalize_whitespace(expected)
output = _normalize_whitespace(output)
elif normalize:
self.skipTest('Tidylib not available.')
self.assertMultiLineEqual(output, expected)
return test
location = dct.get('location', '')
exclude = dct.get('exclude', [])
normalize = dct.get('normalize', False)
input_ext = dct.get('input_ext', '.txt')
output_ext = dct.get('output_ext', '.html')
kwargs = dct.get('default_kwargs', Kwargs())
if os.path.isdir(location):
for file in os.listdir(location):
infile = os.path.join(location, file)
if os.path.isfile(infile):
tname, ext = os.path.splitext(file)
if ext == input_ext:
outfile = os.path.join(location, tname + output_ext)
tname = tname.replace(' ', '_').replace('-', '_')
kws = kwargs.copy()
if tname in dct:
kws.update(dct[tname])
test_name = 'test_%s' % tname
if tname not in exclude:
dct[test_name] = generate_test(infile, outfile, normalize, kws)
else:
dct[test_name] = unittest.skip('Excluded')(lambda: None)
return type.__new__(cls, name, bases, dct)
# Define LegacyTestCase class with metaclass in Py2 & Py3 compatible way.
# See https://stackoverflow.com/a/38668373/866026
# TODO: If/when py2 support is dropped change to:
# class LegacyTestCase(unittest.Testcase, metaclass=LegacyTestMeta)
class LegacyTestCase(LegacyTestMeta('LegacyTestCase', (unittest.TestCase,), {'__slots__': ()})):
"""
A `unittest.TestCase` subclass for running Markdown's legacy file-based tests.
A subclass should define various properties which point to a directory of
text-based test files and define various behaviors/defaults for those tests.
The following properties are supported:
location: A path to the directory fo test files. An absolute path is preferred.
exclude: A list of tests to exclude. Each test name should comprise the filename
without an extension.
normalize: A boolean value indicating if the HTML should be normalized.
Default: `False`.
input_ext: A string containing the file extension of input files. Default: `.txt`.
ouput_ext: A string containing the file extension of expected output files.
Default: `html`.
default_kwargs: A `Kwargs` instance which stores the default set of keyword
arguments for all test files in the directory.
In addition, properties can be defined for each individual set of test files within
the directory. The property should be given the name of the file wihtout the file
extension. Any spaces and dashes in the filename should be replaced with
underscores. The value of the property should be a `Kwargs` instance which
contains the keyword arguments that should be passed to `Markdown` for that
test file. The keyword arguments will "update" the `default_kwargs`.
When the class instance is created, it will walk the given directory and create
a separate unitttest for each set of test files using the naming scheme:
`test_filename`. One unittest will be run for each set of input and output files.
"""
pass
Markdown-3.1.1/markdown/treeprocessors.py 0000644 0000765 0000024 00000036156 13432642364 021214 0 ustar waylan staff 0000000 0000000 # -*- coding: utf-8 -*-
"""
Python Markdown
A Python implementation of John Gruber's Markdown.
Documentation: https://python-markdown.github.io/
GitHub: https://github.com/Python-Markdown/markdown/
PyPI: https://pypi.org/project/Markdown/
Started by Manfred Stienstra (http://www.dwerg.net/).
Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
License: BSD (see LICENSE.md for details).
"""
from __future__ import unicode_literals
from __future__ import absolute_import
from . import util
from . import inlinepatterns
def build_treeprocessors(md, **kwargs):
""" Build the default treeprocessors for Markdown. """
treeprocessors = util.Registry()
treeprocessors.register(InlineProcessor(md), 'inline', 20)
treeprocessors.register(PrettifyTreeprocessor(md), 'prettify', 10)
return treeprocessors
def isString(s):
""" Check if it's string """
if not isinstance(s, util.AtomicString):
return isinstance(s, util.string_type)
return False
class Treeprocessor(util.Processor):
"""
Treeprocessors are run on the ElementTree object before serialization.
Each Treeprocessor implements a "run" method that takes a pointer to an
ElementTree, modifies it as necessary and returns an ElementTree
object.
Treeprocessors must extend markdown.Treeprocessor.
"""
def run(self, root):
"""
Subclasses of Treeprocessor should implement a `run` method, which
takes a root ElementTree. This method can return another ElementTree
object, and the existing root ElementTree will be replaced, or it can
modify the current tree and return None.
"""
pass # pragma: no cover
class InlineProcessor(Treeprocessor):
"""
A Treeprocessor that traverses a tree, applying inline patterns.
"""
def __init__(self, md):
self.__placeholder_prefix = util.INLINE_PLACEHOLDER_PREFIX
self.__placeholder_suffix = util.ETX
self.__placeholder_length = 4 + len(self.__placeholder_prefix) \
+ len(self.__placeholder_suffix)
self.__placeholder_re = util.INLINE_PLACEHOLDER_RE
self.md = md
self.inlinePatterns = md.inlinePatterns
self.ancestors = []
@property
@util.deprecated("Use 'md' instead.")
def markdown(self):
# TODO: remove this later
return self.md
def __makePlaceholder(self, type):
""" Generate a placeholder """
id = "%04d" % len(self.stashed_nodes)
hash = util.INLINE_PLACEHOLDER % id
return hash, id
def __findPlaceholder(self, data, index):
"""
Extract id from data string, start from index
Keyword arguments:
* data: string
* index: index, from which we start search
Returns: placeholder id and string index, after the found placeholder.
"""
m = self.__placeholder_re.search(data, index)
if m:
return m.group(1), m.end()
else:
return None, index + 1
def __stashNode(self, node, type):
""" Add node to stash """
placeholder, id = self.__makePlaceholder(type)
self.stashed_nodes[id] = node
return placeholder
def __handleInline(self, data, patternIndex=0):
"""
Process string with inline patterns and replace it
with placeholders
Keyword arguments:
* data: A line of Markdown text
* patternIndex: The index of the inlinePattern to start with
Returns: String with placeholders.
"""
if not isinstance(data, util.AtomicString):
startIndex = 0
while patternIndex < len(self.inlinePatterns):
data, matched, startIndex = self.__applyPattern(
self.inlinePatterns[patternIndex], data, patternIndex, startIndex
)
if not matched:
patternIndex += 1
return data
def __processElementText(self, node, subnode, isText=True):
"""
Process placeholders in Element.text or Element.tail
of Elements popped from self.stashed_nodes.
Keywords arguments:
* node: parent node
* subnode: processing node
* isText: bool variable, True - it's text, False - it's tail
Returns: None
"""
if isText:
text = subnode.text
subnode.text = None
else:
text = subnode.tail
subnode.tail = None
childResult = self.__processPlaceholders(text, subnode, isText)
if not isText and node is not subnode:
pos = list(node).index(subnode) + 1
else:
pos = 0
childResult.reverse()
for newChild in childResult:
node.insert(pos, newChild[0])
def __processPlaceholders(self, data, parent, isText=True):
"""
Process string with placeholders and generate ElementTree tree.
Keyword arguments:
* data: string with placeholders instead of ElementTree elements.
* parent: Element, which contains processing inline data
Returns: list with ElementTree elements with applied inline patterns.
"""
def linkText(text):
if text:
if result:
if result[-1][0].tail:
result[-1][0].tail += text
else:
result[-1][0].tail = text
elif not isText:
if parent.tail:
parent.tail += text
else:
parent.tail = text
else:
if parent.text:
parent.text += text
else:
parent.text = text
result = []
strartIndex = 0
while data:
index = data.find(self.__placeholder_prefix, strartIndex)
if index != -1:
id, phEndIndex = self.__findPlaceholder(data, index)
if id in self.stashed_nodes:
node = self.stashed_nodes.get(id)
if index > 0:
text = data[strartIndex:index]
linkText(text)
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
)
if child.text:
if child.text.strip():
self.__processElementText(child, child)
else: # it's just a string
linkText(node)
strartIndex = phEndIndex
continue
strartIndex = phEndIndex
result.append((node, self.ancestors[:]))
else: # wrong placeholder
end = index + len(self.__placeholder_prefix)
linkText(data[strartIndex:end])
strartIndex = end
else:
text = data[strartIndex:]
if isinstance(data, util.AtomicString):
# We don't want to loose the AtomicString
text = util.AtomicString(text)
linkText(text)
data = ""
return result
def __applyPattern(self, pattern, data, patternIndex, startIndex=0):
"""
Check if the line fits the pattern, create the necessary
elements, add it to stashed_nodes.
Keyword arguments:
* data: the text to be processed
* pattern: the pattern to be checked
* patternIndex: index of current pattern
* startIndex: string index, from which we start searching
Returns: String with placeholders instead of ElementTree elements.
"""
new_style = isinstance(pattern, inlinepatterns.InlineProcessor)
for exclude in pattern.ANCESTOR_EXCLUDES:
if exclude.lower() in self.ancestors:
return data, False, 0
if new_style:
match = None
# Since handleMatch may reject our first match,
# we iterate over the buffer looking for matches
# until we can't find any more.
for match in pattern.getCompiledRegExp().finditer(data, startIndex):
node, start, end = pattern.handleMatch(match, data)
if start is None or end is None:
startIndex += match.end(0)
match = None
continue
break
else: # pragma: no cover
match = pattern.getCompiledRegExp().match(data[startIndex:])
leftData = data[:startIndex]
if not match:
return data, False, 0
if not new_style: # pragma: no cover
node = pattern.handleMatch(match)
start = match.start(0)
end = match.end(0)
if node is None:
return data, True, end
if not isString(node):
if not isinstance(node.text, util.AtomicString):
# We need to process current node too
for child in [node] + list(node):
if not isString(node):
if child.text:
self.ancestors.append(child.tag.lower())
child.text = self.__handleInline(
child.text, patternIndex + 1
)
self.ancestors.pop()
if child.tail:
child.tail = self.__handleInline(
child.tail, patternIndex
)
placeholder = self.__stashNode(node, pattern.type())
if new_style:
return "%s%s%s" % (data[:start],
placeholder, data[end:]), True, 0
else: # pragma: no cover
return "%s%s%s%s" % (leftData,
match.group(1),
placeholder, match.groups()[-1]), True, 0
def __build_ancestors(self, parent, parents):
"""Build the ancestor list."""
ancestors = []
while parent is not None:
if parent is not None:
ancestors.append(parent.tag.lower())
parent = self.parent_map.get(parent)
ancestors.reverse()
parents.extend(ancestors)
def run(self, tree, ancestors=None):
"""Apply inline patterns to a parsed Markdown tree.
Iterate over ElementTree, find elements with inline tag, apply inline
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:
node.text = markdown.AtomicString("This will not be processed.")
Arguments:
* tree: ElementTree object, representing Markdown tree.
* ancestors: List of parent tag names that precede the tree node (if needed).
Returns: ElementTree object with applied inline patterns.
"""
self.stashed_nodes = {}
# Ensure a valid parent list, but copy passed in lists
# to ensure we don't have the user accidentally change it on us.
tree_parents = [] if ancestors is None else ancestors[:]
self.parent_map = dict((c, p) for p in tree.iter() for c in p)
stack = [(tree, tree_parents)]
while stack:
currElement, parents = stack.pop()
self.ancestors = parents
self.__build_ancestors(currElement, self.ancestors)
insertQueue = []
for child in currElement:
if child.text and not isinstance(
child.text, util.AtomicString
):
self.ancestors.append(child.tag.lower())
text = child.text
child.text = None
lst = self.__processPlaceholders(
self.__handleInline(text), child
)
for l in lst:
self.parent_map[l[0]] = child
stack += lst
insertQueue.append((child, lst))
self.ancestors.pop()
if child.tail:
tail = self.__handleInline(child.tail)
dumby = util.etree.Element('d')
child.tail = None
tailResult = self.__processPlaceholders(tail, dumby, False)
if dumby.tail:
child.tail = dumby.tail
pos = list(currElement).index(child) + 1
tailResult.reverse()
for newChild in tailResult:
self.parent_map[newChild[0]] = currElement
currElement.insert(pos, newChild[0])
if len(child):
self.parent_map[child] = currElement
stack.append((child, self.ancestors[:]))
for element, lst in insertQueue:
for i, obj in enumerate(lst):
newChild = obj[0]
element.insert(i, newChild)
return tree
class PrettifyTreeprocessor(Treeprocessor):
""" Add linebreaks to the html document. """
def _prettifyETree(self, elem):
""" Recursively add linebreaks to ElementTree children. """
i = "\n"
if self.md.is_block_level(elem.tag) and elem.tag not in ['code', 'pre']:
if (not elem.text or not elem.text.strip()) \
and len(elem) and self.md.is_block_level(elem[0].tag):
elem.text = i
for e in elem:
if self.md.is_block_level(e.tag):
self._prettifyETree(e)
if not elem.tail or not elem.tail.strip():
elem.tail = i
if not elem.tail or not elem.tail.strip():
elem.tail = i
def run(self, root):
""" Add linebreaks to ElementTree root object. """
self._prettifyETree(root)
# Do
's separately as they are often in the middle of
# inline content and missed by _prettifyETree.
brs = root.iter('br')
for br in brs:
if not br.tail or not br.tail.strip():
br.tail = '\n'
else:
br.tail = '\n%s' % br.tail
# Clean up extra empty lines at end of code blocks.
pres = root.iter('pre')
for pre in pres:
if len(pre) and pre[0].tag == 'code':
pre[0].text = util.AtomicString(pre[0].text.rstrip() + '\n')
Markdown-3.1.1/markdown/util.py 0000644 0000765 0000024 00000036572 13432642364 017111 0 ustar waylan staff 0000000 0000000 # -*- coding: utf-8 -*-
"""
Python Markdown
A Python implementation of John Gruber's Markdown.
Documentation: https://python-markdown.github.io/
GitHub: https://github.com/Python-Markdown/markdown/
PyPI: https://pypi.org/project/Markdown/
Started by Manfred Stienstra (http://www.dwerg.net/).
Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
License: BSD (see LICENSE.md for details).
"""
from __future__ import unicode_literals
import re
import sys
from collections import namedtuple
from functools import wraps
import warnings
"""
Python 3 Stuff
=============================================================================
"""
PY3 = sys.version_info[0] == 3
PY37 = (3, 7) <= sys.version_info
if PY3: # pragma: no cover
string_type = str
text_type = str
int2str = chr
iterrange = range
else: # pragma: no cover
string_type = basestring # noqa
text_type = unicode # noqa
int2str = unichr # noqa
iterrange = xrange # noqa
"""
Constants you might want to modify
-----------------------------------------------------------------------------
"""
BLOCK_LEVEL_ELEMENTS = [
# Elements which are invalid to wrap in a `` tag.
# See http://w3c.github.io/html/grouping-content.html#the-p-element
'address', 'article', 'aside', 'blockquote', 'details', 'div', 'dl',
'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3',
'h4', 'h5', 'h6', 'header', 'hr', 'main', 'menu', 'nav', 'ol', 'p', 'pre',
'section', 'table', 'ul',
# Other elements which Markdown should not be mucking up the contents of.
'canvas', 'dd', 'dt', 'group', 'iframe', 'li', 'math', 'noscript', 'output',
'progress', 'script', 'style', 'tbody', 'td', 'th', 'thead', 'tr', 'video'
]
# Placeholders
STX = '\u0002' # Use STX ("Start of text") for start-of-placeholder
ETX = '\u0003' # Use ETX ("End of text") for end-of-placeholder
INLINE_PLACEHOLDER_PREFIX = STX+"klzzwxh:"
INLINE_PLACEHOLDER = INLINE_PLACEHOLDER_PREFIX + "%s" + ETX
INLINE_PLACEHOLDER_RE = re.compile(INLINE_PLACEHOLDER % r'([0-9]+)')
AMP_SUBSTITUTE = STX+"amp"+ETX
HTML_PLACEHOLDER = STX + "wzxhzdk:%s" + ETX
HTML_PLACEHOLDER_RE = re.compile(HTML_PLACEHOLDER % r'([0-9]+)')
TAG_PLACEHOLDER = STX + "hzzhzkh:%s" + ETX
"""
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
)
# 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
# Is the C implementation of ElementTree available?
import xml.etree.cElementTree as etree
from xml.etree.ElementTree import Comment
# Serializers (including ours) test with non-c Comment
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
# Use the Python implementation of ElementTree?
import xml.etree.ElementTree as etree
if etree.VERSION < "1.1":
raise RuntimeError("ElementTree version 1.1 or higher is required")
"""
AUXILIARY GLOBAL FUNCTIONS
=============================================================================
"""
def deprecated(message, stacklevel=2):
"""
Raise a DeprecationWarning when wrapped function/method is called.
Borrowed from https://stackoverflow.com/a/48632082/866026
"""
def deprecated_decorator(func):
@wraps(func)
def deprecated_func(*args, **kwargs):
warnings.warn(
"'{}' is deprecated. {}".format(func.__name__, message),
category=DeprecationWarning,
stacklevel=stacklevel
)
return func(*args, **kwargs)
return deprecated_func
return deprecated_decorator
@deprecated("Use 'Markdown.is_block_level' instead.")
def isBlockLevel(tag):
"""Check if the tag is a block level HTML tag."""
if isinstance(tag, string_type):
return tag.lower().rstrip('/') in BLOCK_LEVEL_ELEMENTS
# 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
fail_on_errors=False, returns None."""
if not isinstance(value, string_type):
if preserve_none and value is None:
return value
return bool(value)
elif preserve_none and value.lower() == 'none':
return None
elif value.lower() in ('true', 'yes', 'y', 'on', '1'):
return True
elif value.lower() in ('false', 'no', 'n', 'off', '0', 'none'):
return False
elif fail_on_errors:
raise ValueError('Cannot parse bool value: %r' % value)
def code_escape(text):
"""Escape code."""
if "&" in text:
text = text.replace("&", "&")
if "<" in text:
text = text.replace("<", "<")
if ">" in text:
text = text.replace(">", ">")
return text
"""
MISC AUXILIARY CLASSES
=============================================================================
"""
class AtomicString(text_type):
"""A string which should not be further processed."""
pass
class Processor(object):
def __init__(self, md=None):
self.md = md
@property
@deprecated("Use 'md' instead.")
def markdown(self):
# TODO: remove this later
return self.md
class HtmlStash(object):
"""
This class is used for stashing HTML objects that we extract
in the beginning and replace with place-holders.
"""
def __init__(self):
""" Create a HtmlStash. """
self.html_counter = 0 # for counting inline html segments
self.rawHtmlBlocks = []
self.tag_counter = 0
self.tag_data = [] # list of dictionaries in the order tags appear
def store(self, html):
"""
Saves an HTML segment for later reinsertion. Returns a
placeholder string that needs to be inserted into the
document.
Keyword arguments:
* html: an html segment
Returns : a placeholder string
"""
self.rawHtmlBlocks.append(html)
placeholder = self.get_placeholder(self.html_counter)
self.html_counter += 1
return placeholder
def reset(self):
self.html_counter = 0
self.rawHtmlBlocks = []
def get_placeholder(self, key):
return HTML_PLACEHOLDER % key
def store_tag(self, tag, attrs, left_index, right_index):
"""Store tag data and return a placeholder."""
self.tag_data.append({'tag': tag, 'attrs': attrs,
'left_index': left_index,
'right_index': right_index})
placeholder = TAG_PLACEHOLDER % str(self.tag_counter)
self.tag_counter += 1 # equal to the tag's index in self.tag_data
return placeholder
# Used internally by `Registry` for each item in its sorted list.
# Provides an easier to read API when editing the code later.
# For example, `item.name` is more clear than `item[0]`.
_PriorityItem = namedtuple('PriorityItem', ['name', 'priority'])
class Registry(object):
"""
A priority sorted registry.
A `Registry` instance provides two public methods to alter the data of the
registry: `register` and `deregister`. Use `register` to add items and
`deregister` to remove items. See each method for specifics.
When registering an item, a "name" and a "priority" must be provided. All
items are automatically sorted by "priority" from highest to lowest. The
"name" is used to remove ("deregister") and get items.
A `Registry` instance it like a list (which maintains order) when reading
data. You may iterate over the items, get an item and get a count (length)
of all items. You may also check that the registry contains an item.
When getting an item you may use either the index of the item or the
string-based "name". For example:
registry = Registry()
registry.register(SomeItem(), 'itemname', 20)
# Get the item by index
item = registry[0]
# Get the item by name
item = registry['itemname']
When checking that the registry contains an item, you may use either the
string-based "name", or a reference to the actual item. For example:
someitem = SomeItem()
registry.register(someitem, 'itemname', 20)
# Contains the name
assert 'itemname' in registry
# Contains the item instance
assert someitem in registry
The method `get_index_for_name` is also available to obtain the index of
an item using that item's assigned "name".
"""
def __init__(self):
self._data = {}
self._priority = []
self._is_sorted = False
def __contains__(self, item):
if isinstance(item, string_type):
# Check if an item exists by this name.
return item in self._data.keys()
# Check if this instance exists.
return item in self._data.values()
def __iter__(self):
self._sort()
return iter([self._data[k] for k, p in self._priority])
def __getitem__(self, key):
self._sort()
if isinstance(key, slice):
data = Registry()
for k, p in self._priority[key]:
data.register(self._data[k], k, p)
return data
if isinstance(key, int):
return self._data[self._priority[key].name]
return self._data[key]
def __len__(self):
return len(self._priority)
def __repr__(self):
return '<{0}({1})>'.format(self.__class__.__name__, list(self))
def get_index_for_name(self, name):
"""
Return the index of the given name.
"""
if name in self:
self._sort()
return self._priority.index(
[x for x in self._priority if x.name == name][0]
)
raise ValueError('No item named "{0}" exists.'.format(name))
def register(self, item, name, priority):
"""
Add an item to the registry with the given name and priority.
Parameters:
* `item`: The item being registered.
* `name`: A string used to reference the item.
* `priority`: An integer or float used to sort against all items.
If an item is registered with a "name" which already exists, the
existing item is replaced with the new item. Tread carefully as the
old item is lost with no way to recover it. The new item will be
sorted according to its priority and will **not** retain the position
of the old item.
"""
if name in self:
# Remove existing item of same name first
self.deregister(name)
self._is_sorted = False
self._data[name] = item
self._priority.append(_PriorityItem(name, priority))
def deregister(self, name, strict=True):
"""
Remove an item from the registry.
Set `strict=False` to fail silently.
"""
try:
index = self.get_index_for_name(name)
del self._priority[index]
del self._data[name]
except ValueError:
if strict:
raise
def _sort(self):
"""
Sort the registry by priority from highest to lowest.
This method is called internally and should never be explicitly called.
"""
if not self._is_sorted:
self._priority.sort(key=lambda item: item.priority, reverse=True)
self._is_sorted = True
# Deprecated Methods which provide a smooth transition from OrderedDict
def __setitem__(self, key, value):
""" Register item with priorty 5 less than lowest existing priority. """
if isinstance(key, string_type):
warnings.warn(
'Using setitem to register a processor or pattern is deprecated. '
'Use the `register` method instead.',
DeprecationWarning,
stacklevel=2,
)
if key in self:
# Key already exists, replace without altering priority
self._data[key] = value
return
if len(self) == 0:
# This is the first item. Set priority to 50.
priority = 50
else:
self._sort()
priority = self._priority[-1].priority - 5
self.register(value, key, priority)
else:
raise TypeError
def __delitem__(self, key):
""" Deregister an item by name. """
if key in self:
self.deregister(key)
warnings.warn(
'Using del to remove a processor or pattern is deprecated. '
'Use the `deregister` method instead.',
DeprecationWarning,
stacklevel=2,
)
else:
raise TypeError
def add(self, key, value, location):
""" Register a key by location. """
if len(self) == 0:
# This is the first item. Set priority to 50.
priority = 50
elif location == '_begin':
self._sort()
# Set priority 5 greater than highest existing priority
priority = self._priority[0].priority + 5
elif location == '_end':
self._sort()
# Set priority 5 less than lowest existing priority
priority = self._priority[-1].priority - 5
elif location.startswith('<') or location.startswith('>'):
# Set priority halfway between existing priorities.
i = self.get_index_for_name(location[1:])
if location.startswith('<'):
after = self._priority[i].priority
if i > 0:
before = self._priority[i-1].priority
else:
# Location is first item`
before = after + 10
else:
# location.startswith('>')
before = self._priority[i].priority
if i < len(self) - 1:
after = self._priority[i+1].priority
else:
# location is last item
after = before - 10
priority = before - ((before - after) / 2)
else:
raise ValueError('Not a valid location: "%s". Location key '
'must start with a ">" or "<".' % location)
self.register(value, key, priority)
warnings.warn(
'Using the add method to register a processor or pattern is deprecated. '
'Use the `register` method instead.',
DeprecationWarning,
stacklevel=2,
)
Markdown-3.1.1/mkdocs.yml 0000644 0000765 0000024 00000004154 13432642364 015732 0 ustar waylan staff 0000000 0000000 site_name: Python-Markdown
site_url: https://Python-Markdown.github.io/
repo_url: https://github.com/Python-Markdown/markdown
site_author: "The Python-Markdown Project"
copyright: "Copyright © 2010-2017"
use_directory_urls: true
theme:
name: nature
icon: py.png
release: !!python/name:markdown.__version__
issue_tracker: https://github.com/Python-Markdown/markdown/issues
nav:
- Python-Markdown: index.md
- Installation: install.md
- Library Reference: reference.md
- Command Line: cli.md
- Extensions: extensions/index.md
- Officially Supported Extensions:
- Abbreviations: extensions/abbreviations.md
- Admonition: extensions/admonition.md
- Attribute Lists: extensions/attr_list.md
- CodeHilite: extensions/code_hilite.md
- Definition Lists: extensions/definition_lists.md
- Extra: extensions/extra.md
- Fenced Code Blocks: extensions/fenced_code_blocks.md
- Footnotes: extensions/footnotes.md
- Legacy Attributes: extensions/legacy_attr.md
- Legacy Emphasis: extensions/legacy_em.md
- Meta-Data: extensions/meta_data.md
- New Line to Break: extensions/nl2br.md
- Sane Lists: extensions/sane_lists.md
- SmartyPants: extensions/smarty.md
- Table of Contents: extensions/toc.md
- Tables: extensions/tables.md
- WikiLinks: extensions/wikilinks.md
- Extension API: extensions/api.md
- Test Tools: test_tools.md
- Contributing to Python-Markdown: contributing.md
- Change Log: change_log/index.md
- Release Notes for v.3.1: change_log/release-3.1.md
- Release Notes for v.3.0: change_log/release-3.0.md
- Release Notes for v.2.6: change_log/release-2.6.md
- Release Notes for v.2.5: change_log/release-2.5.md
- Release Notes for v.2.4: change_log/release-2.4.md
- Release Notes for v.2.3: change_log/release-2.3.md
- Release Notes for v.2.2: change_log/release-2.2.md
- Release Notes for v.2.1: change_log/release-2.1.md
- Release Notes for v.2.0: change_log/release-2.0.md
- Authors: authors.md
markdown_extensions:
- extra
- admonition
- smarty
- codehilite
- toc:
permalink: true
Markdown-3.1.1/setup.cfg 0000644 0000765 0000024 00000000151 13470647777 015560 0 ustar waylan staff 0000000 0000000 [bdist_wheel]
universal = 1
[metadata]
license_file = LICENSE.md
[egg_info]
tag_build =
tag_date = 0
Markdown-3.1.1/setup.py 0000755 0000765 0000024 00000013162 13470641027 015440 0 ustar waylan staff 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
"""
Python Markdown
A Python implementation of John Gruber's Markdown.
Documentation: https://python-markdown.github.io/
GitHub: https://github.com/Python-Markdown/markdown/
PyPI: https://pypi.org/project/Markdown/
Started by Manfred Stienstra (http://www.dwerg.net/).
Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
License: BSD (see LICENSE.md for details).
"""
import os
import sys
from setuptools import setup
def get_version():
"""Get version and version_info from markdown/__meta__.py file."""
module_path = os.path.join(os.path.dirname('__file__'), 'markdown', '__meta__.py')
if sys.version_info[0] == 2:
import imp
meta = imp.load_source('__meta__', module_path)
return meta.__version__, meta.__version_info__
import importlib.util
spec = importlib.util.spec_from_file_location('__meta__', module_path)
meta = importlib.util.module_from_spec(spec)
spec.loader.exec_module(meta)
return meta.__version__, meta.__version_info__
__version__, __version_info__ = get_version()
# Get development Status for classifiers
dev_status_map = {
'dev': '2 - Pre-Alpha',
'alpha': '3 - Alpha',
'beta': '4 - Beta',
'rc': '4 - Beta',
'final': '5 - Production/Stable'
}
DEVSTATUS = dev_status_map[__version_info__[3]]
# The command line script name. Currently set to "markdown_py" so as not to
# conflict with the perl implimentation (which uses "markdown").
SCRIPT_NAME = 'markdown_py'
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
supported by the `Available Extensions`_.
.. _Markdown: http://daringfireball.net/projects/markdown/
.. _Features: https://Python-Markdown.github.io#features
.. _`Available Extensions`: https://Python-Markdown.github.io/extensions/
Support
=======
You may report bugs, ask for help, and discuss various other issues on
the `bug tracker`_.
.. _`bug tracker`: http://github.com/Python-Markdown/markdown/issues
'''
setup(
name='Markdown',
version=__version__,
url='https://Python-Markdown.github.io/',
download_url='http://pypi.python.org/packages/source/M/Markdown/Markdown-%s-py2.py3-none-any.whl' % __version__,
description='Python implementation of Markdown.',
long_description=long_description,
author='Manfred Stienstra, Yuri takhteyev and Waylan limberg',
author_email='waylan.limberg@icloud.com',
maintainer='Waylan Limberg',
maintainer_email='waylan.limberg@icloud.com',
license='BSD License',
packages=['markdown', 'markdown.extensions'],
python_requires='>=2.7,!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*',
install_requires=['setuptools >= 36'],
extras_require={
'testing': [
'coverage',
'pyyaml',
],
},
entry_points={
'console_scripts': [
'%s = markdown.__main__:run' % SCRIPT_NAME,
],
# Register the built in extensions
'markdown.extensions': [
'abbr = markdown.extensions.abbr:AbbrExtension',
'admonition = markdown.extensions.admonition:AdmonitionExtension',
'attr_list = markdown.extensions.attr_list:AttrListExtension',
'codehilite = markdown.extensions.codehilite:CodeHiliteExtension',
'def_list = markdown.extensions.def_list:DefListExtension',
'extra = markdown.extensions.extra:ExtraExtension',
'fenced_code = markdown.extensions.fenced_code:FencedCodeExtension',
'footnotes = markdown.extensions.footnotes:FootnoteExtension',
'meta = markdown.extensions.meta:MetaExtension',
'nl2br = markdown.extensions.nl2br:Nl2BrExtension',
'sane_lists = markdown.extensions.sane_lists:SaneListExtension',
'smarty = markdown.extensions.smarty:SmartyExtension',
'tables = markdown.extensions.tables:TableExtension',
'toc = markdown.extensions.toc:TocExtension',
'wikilinks = markdown.extensions.wikilinks:WikiLinkExtension',
'legacy_attrs = markdown.extensions.legacy_attrs:LegacyAttrExtension',
'legacy_em = markdown.extensions.legacy_em:LegacyEmExtension',
]
},
classifiers=[
'Development Status :: %s' % DEVSTATUS,
'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.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Topic :: Communications :: Email :: Filters',
'Topic :: Internet :: WWW/HTTP :: Dynamic Content :: CGI Tools/Libraries',
'Topic :: Internet :: WWW/HTTP :: Site Management',
'Topic :: Software Development :: Documentation',
'Topic :: Software Development :: Libraries :: Python Modules',
'Topic :: Text Processing :: Filters',
'Topic :: Text Processing :: Markup :: HTML'
]
)
Markdown-3.1.1/tests/ 0000755 0000765 0000024 00000000000 13470647777 015104 5 ustar waylan staff 0000000 0000000 Markdown-3.1.1/tests/__init__.py 0000644 0000765 0000024 00000001371 13330711640 017167 0 ustar waylan staff 0000000 0000000 # -*- coding: utf-8 -*-
"""
Python Markdown
A Python implementation of John Gruber's Markdown.
Documentation: https://python-markdown.github.io/
GitHub: https://github.com/Python-Markdown/markdown/
PyPI: https://pypi.org/project/Markdown/
Started by Manfred Stienstra (http://www.dwerg.net/).
Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
Currently maintained by Waylan Limberg (https://github.com/waylan),
Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)
License: BSD (see LICENSE.md for details).
"""
Markdown-3.1.1/tests/basic/ 0000755 0000765 0000024 00000000000 13470647777 016165 5 ustar waylan staff 0000000 0000000 Markdown-3.1.1/tests/basic/amps-and-angle-encoding.html 0000644 0000765 0000024 00000000763 13212354257 023407 0 ustar waylan staff 0000000 0000000
AT&T has an ampersand in their name.
AT&T is another way to write it.
This & that.
4 < 5.
6 > 5.
Here's a link with an ampersand in the URL.
Here's a link with an amersand in the link text: AT&T.
Here's an inline link.
Here's an inline link.