aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuri Takhteyev <yuri@freewisdom.org>2008-08-15 10:40:52 -0700
committerYuri Takhteyev <yuri@freewisdom.org>2008-08-15 10:40:52 -0700
commit5e3d88cf03303c52956f258898def2cd6a294674 (patch)
tree7899797058a8ee712664eaad062824b5c9e2f716
parent3a78823a59e9d0c89d541b2bd259ecfc55aef2cd (diff)
parent88c72d709be87321b736778d19d9277db02422a8 (diff)
downloadmarkdown-5e3d88cf03303c52956f258898def2cd6a294674.tar.gz
markdown-5e3d88cf03303c52956f258898def2cd6a294674.tar.bz2
markdown-5e3d88cf03303c52956f258898def2cd6a294674.zip
Merge branch 'master' of git@gitorious.org:python-markdown/mainline
-rw-r--r--.gitignore4
-rw-r--r--MANIFEST31
-rw-r--r--docs/AUTHORS36
-rw-r--r--docs/CHANGE_LOG (renamed from CHANGE_LOG.txt)0
-rw-r--r--docs/INSTALL59
-rw-r--r--docs/README (renamed from README.txt)0
-rw-r--r--docs/README.html (renamed from README.html)0
-rwxr-xr-xdocs/writing_extensions.txt388
-rwxr-xr-xmarkdown.py183
-rw-r--r--markdown_extensions/__init__.py0
-rw-r--r--markdown_extensions/codehilite.py (renamed from mdx_codehilite.py)0
-rw-r--r--markdown_extensions/extra.py (renamed from mdx_extra.py)0
-rw-r--r--markdown_extensions/fenced_code.py (renamed from mdx_fenced_code.py)0
-rw-r--r--markdown_extensions/footnotes.py (renamed from mdx_footnotes.py)0
-rw-r--r--markdown_extensions/headerid.py (renamed from mdx_headerid.py)0
-rw-r--r--markdown_extensions/imagelinks.py (renamed from mdx_imagelinks.py)0
-rw-r--r--markdown_extensions/meta.py (renamed from mdx_meta.py)0
-rw-r--r--markdown_extensions/rss.py (renamed from mdx_rss.py)0
-rw-r--r--markdown_extensions/tables.py (renamed from mdx_tables.py)0
-rw-r--r--markdown_extensions/wikilink.py (renamed from mdx_wikilink.py)0
-rw-r--r--scripts/odt2txt.py (renamed from odt2txt.py)0
-rw-r--r--setup.py13
-rw-r--r--tests/misc/headers.html7
-rw-r--r--tests/misc/headers.txt4
24 files changed, 622 insertions, 103 deletions
diff --git a/.gitignore b/.gitignore
index 64c7154..5434639 100644
--- a/.gitignore
+++ b/.gitignore
@@ -2,5 +2,5 @@
*.bak
*.tmp
tmp/*
-__init__.py
-markdown_old.py
+build/*
+dist/*
diff --git a/MANIFEST b/MANIFEST
index d634b69..4e7e3df 100644
--- a/MANIFEST
+++ b/MANIFEST
@@ -1,15 +1,20 @@
-README.txt
-README.html
-CHANGE_LOG.txt
markdown.py
-mdx_codehilite.py
-mdx_fenced_code.py
-mdx_footnotes.py
-mdx_headerid.py
-mdx_imagelinks.py
-mdx_meta.py
-mdx_rss.py
-mdx_tables.py
-mdx_wikilink.py
+markdown_extnesions/__init__.py
+markdown_extensions/codehilite.py
+markdown_extensions/fenced_code.py
+markdown_extensions/footnotes.py
+markdown_extensions/headerid.py
+markdown_extensions/imagelinks.py
+markdown_extensions/meta.py
+markdown_extensions/rss.py
+markdown_extensions/tables.py
+markdown_extensions/wikilink.py
setup.py
-scripts/pymarkdown.py \ No newline at end of file
+scripts/pymarkdown.py
+docs/README
+docs/README.html
+docs/CHANGE_LOG
+docs/INSTALL
+docs/AUTHORS
+docs/writing_extensions.txt
+
diff --git a/docs/AUTHORS b/docs/AUTHORS
new file mode 100644
index 0000000..b4cd939
--- /dev/null
+++ b/docs/AUTHORS
@@ -0,0 +1,36 @@
+Primary Authors
+===============
+
+Yuri Takteyev <http://freewisdom.org/>, who has written much of the current code
+while procrastingating his Ph.D.
+
+Waylan Limberg <http://achinghead.com/>, who has written most of the available
+extensions and later was asked to join Yuri, fixing nummrious bugs, adding
+documentation and making general improvements to the existing codebase.
+
+Artem Yunusov, who as part of a 2008 GSoC project, has refactored inline
+patterns, replaced the NanoDOM with ElementTree support and made various other
+improvements.
+
+Manfed Stienstra <http://www.dwerg.net/>, who wrote the original version of
+the script and is responsible for various parts of the existing codebase.
+
+David Wolever, who refactored the extension API and made other improvements
+as he helped to integrate Markdown into Dr.Project.
+
+Other Contributors
+==================
+
+The incomplete list of individuals below have provided patches
+or otherwise contributed to the project in various ways. We would like to thank
+everyone who has contributed to the progect in any way.
+
+Jeff Balogh
+Sergej Chodarev
+Chris Clark
+Tiago Cogumbreiro
+G. Clark Haynes
+Daniel Krech
+Steward Midwinter
+Malcolm Tredinnick
+and many others to helped by reporting bugs
diff --git a/CHANGE_LOG.txt b/docs/CHANGE_LOG
index 200d5b7..200d5b7 100644
--- a/CHANGE_LOG.txt
+++ b/docs/CHANGE_LOG
diff --git a/docs/INSTALL b/docs/INSTALL
new file mode 100644
index 0000000..56d6c78
--- /dev/null
+++ b/docs/INSTALL
@@ -0,0 +1,59 @@
+Installing Python-Markdown
+==========================
+
+Checking Dependencies
+---------------------
+
+Python-Markdown requires the ElementTree module to be installed. In Python2.5+
+ElementTree is included as part of the standard library. For earlier versions
+of Python, open a Python shell and type the following:
+
+ >>> import cElementTree
+ >>> import ElementTree
+
+If at least one of those does not generate any errors, then you have a working
+copy of ElementTree installed on your system. As cElementTree is faster, you
+may want install that if you don't already have it and it's available for your
+system.
+
+The East Way
+------------
+
+The simplest way to install Python-Markdown is by using SetupTools. As and
+Admin/Root user on your system do:
+
+ easy_install ElementTree
+ easy_install Markdown
+
+That's it, your done.
+
+Installing on Windows
+---------------------
+
+
+
+Download the Windows installer (.exe) from PyPI:
+
+<http://pypi.python.org/pypi/Markdown>
+
+Doubleclick the file and follow the instructions.
+
+If you preffer to manually install Python-Markdown in Windows, download the
+Zip file, unzip it, and on the command line in the directory you unzipped to:
+
+ python setup.py install
+
+If you plan to use the provided commandline script, you need to make sure your
+script directory is on your system path. On a typical Python install on Windows
+the Scripts directory is `C:\Python25\Scripts\`. Adjust according to your
+system and add that to your system path.
+
+Installing on *nix Sytems
+-------------------------
+
+From the command line do the following:
+
+ wget http://pypi.python.org/packages/source/M/Markdown/markdown-2.0.tar.gz
+ tar xvzf markdown-2.0.tar.gz
+ cd markdown-2.0/
+ sudo python setup.py install
diff --git a/README.txt b/docs/README
index b859924..b859924 100644
--- a/README.txt
+++ b/docs/README
diff --git a/README.html b/docs/README.html
index 6d94e8e..6d94e8e 100644
--- a/README.html
+++ b/docs/README.html
diff --git a/docs/writing_extensions.txt b/docs/writing_extensions.txt
new file mode 100755
index 0000000..df17698
--- /dev/null
+++ b/docs/writing_extensions.txt
@@ -0,0 +1,388 @@
+### Overview
+
+Python-Markdown includes an API for extension writers to plug their own
+custom functionality and/or syntax into the parser. There are preprocessors
+which allow you to alter the source before it is passed to the parser,
+inline patterns which allow you to add, remove or override the syntax of
+any inline elements, and postprocessors which allow munging of the
+output of the parser before it is returned.
+
+As the parser builds an [ElementTree][] object which is later rendered
+as Unicode text, there are also some helpers provided to make manipulation of
+the tree easier. Each part of the API is discussed in its respective
+section below. You may find reading the source of some [[Available Extensions]]
+helpful as well. For example, the [[Footnotes]] extension uses most of the
+features documented here.
+
+* [Preprocessors][]
+ * [TextPreprocessors][]
+ * [Line Preprocessors][]
+* [InlinePatterns][]
+* [Postprocessors][]
+ * [ElementTree Postprocessors][]
+ * [TextProstprocessors][]
+* [Working with the ElementTree][]
+* [Integrating your code into Markdown][]
+ * [extendMarkdown][]
+ * [registerExtension][]
+ * [Config Settings][]
+ * [makeExtension][]
+
+<h3 id="preprocessors">Preprocessors</h3>
+
+Preprocessors munge the source text before it is passed into the Markdown
+core. This is an excellent place to clean up bad syntax, extract things the
+parser may otherwise choke on and perhaps even store it for later retrieval.
+
+There are two types of preprocessors: [TextPreprocessors][] and
+[Line Preprocessors][].
+
+<h4 id="textpreprocessors">TextPreprocessors</h4>
+
+TextPreprocessors should inherit from `markdown.TextPreprocessor` and implement
+a `run` method with one argument `text`. The `run` method of each
+TextPreprocessor will be passed the entire source text as a single Unicode
+string and should either return that single Unicode string, or an altered
+version of it.
+
+For example, a simple TextPreprocessor that normalizes newlines [^1] might look
+like this:
+
+ class NormalizePreprocessor(markdown.TextPreprocessor):
+ def run(self, text):
+ return text.replace("\r\n", "\n").replace("\r", "\n")
+
+[^1]: It should be noted that Markdown already normalizes newlines. This
+example is for illustrative purposes only.
+
+<h4 id="linepreprocessors">Line Preprocessors</h4>
+
+Line Preprocessors should inherit from `markdown.Preprocessor` and implement
+a `run` method with one argument `lines`. The `run` method of each Line
+Preprocessor will be passed the entire source text as a list of Unicode strings.
+Each string will contain one line of text. The `run` method should return
+either that list, or an altered list of Unicode strings.
+
+A pseudo example:
+
+ class MyPreprocessor(markdown.Preprocessor):
+ def run(self, lines):
+ new_lines = []
+ for line in lines:
+ m = MYREGEX.match(line)
+ if m:
+ # do stuff
+ else:
+ new_lines.append(line)
+ return new_lines
+
+<h3 id="inlinepatterns">Inline Patterns</h3>
+
+Inline Patterns implement the inline HTML element syntax for Markdown such as
+`*emphasis*` or `[links](http://example.com)`. Pattern objects should be
+instances of classes that inherit from `markdown.Pattern` or one of its
+children. Each pattern object uses a single regular expression and must have
+the following methods:
+
+* `getCompiledRegExp()`: Returns a compiled regular expression.
+* `handleMatch(m)`: Accepts a match object and returns an ElementTree
+element of a plain Unicode string.
+
+Note that any regular expression returned by `getCompiledRegExp` must capture
+the whole block. Therefore, they should all start with `r'^(.*?)'` and end
+with `r'(.*?)!'. When using the default `getCompiledRegExp()` method provided
+in the `Pattern` you can pass in a regular expression without that and
+`getCompiledRegExp` will wrap your expression for you. This means that the first
+group of your match will be `m.group(2)` as `m.group(1)` will match everything
+before the pattern.
+
+For an example, consider this simplified emphasis pattern:
+
+ class EmphasisPattern(markdown.Pattern):
+ def handleMatch(self, m):
+ el = markdown.etree.Element('em')
+ el.text = m.group(3)
+ return el
+
+As discussed in [Integrating Your Code Into Markdown][], an instance of this
+class will need to be provided to Markdown. That instance would be created
+like so:
+
+ # an oversimplified regex
+ MYPATTERN = r'\*([^*]+)\*'
+ # pass in pattern and create instance
+ emphasis = EmphasisPattern(MYPATTERN)
+
+Actually it would not be necessary to create that pattern (and not just because
+a more sophisticated emphasis pattern already exists in Markdown). The fact is,
+that example pattern is not very DRY. A pattern for `**strong**` text would
+be almost identical, with the exception that it would create a 'strong' element.
+Therefore, Markdown provides a number of generic pattern classes that can
+provide some common functionality. For example, both emphasis and strong are
+implemented with separate instances of the `SimpleTagPettern` listed below.
+Feel free to use or extend any of these Pattern classes.
+
+**Generic Pattern Classes**
+
+* `SimpleTextPattern(pattern)`:
+
+ Returns simple text of `group(2)` of a `pattern`.
+
+* `SimpleTagPattern(pattern, tag)`:
+
+ Returns an element of type "`tag`" with a text attribute of `group(3)`
+ of a `pattern`. `tag` should be a string of a HTML element (i.e.: 'em').
+
+* `SubstituteTagPattern(pattern, tag)`:
+
+ Returns an element of type "`tag`" with no children or text (i.e.: 'br').
+
+There may be other Pattern classes in the Markdown source that you could extend
+or use as well. Read through the source and see if there is anything you can
+use. You might even get a few ideas for different approaches to your specific
+situation.
+
+<h3 id="postprocessors">Postprocessors</h3>
+
+Postprocessors manipulate a document after it has passed through the Markdown
+core. This is were stored text gets added back in such as a list of footnotes,
+a table of contents or raw html.
+
+There are two types of postprocessors: [ElementTree Postprocessors][] and
+[TextPostprocessors][].
+
+<h4 id="etpostprocessors">ElementTree Postprocessors</h4>
+
+An ElementTree Postprocessor should inherit from `markdown.Postprocessor`,
+over-ride the `run` method which takes one argument `root` and return either
+that root element or a modified root element.
+
+A pseudo example:
+
+ class MyPostprocessor(markdown.Postprocessor):
+ def run(self, root):
+ #do stufff
+ return my_modified_root
+
+For specifics on manipulating the ElementTree, see
+[Working with the ElementTree][] below.
+
+<h4 id="textpostprocessors">TextPostprocessors</h4>
+
+A TextPostprocessor should inherit from `markdown.TextPostprocessor` and
+over-ride the `run` method which takes one argument `text` and returns a
+Unicode string.
+
+TextPostprocessors are run after the ElementTree has been serialized back into
+Unicode text. For example, this may be an appropriate place to add a table of
+contents to a document:
+
+ class TocTextPostprocessor(markdown.TextPostprocessor):
+ def run(self, text):
+ return MYMARKERRE.sub(MyToc, text)
+
+<h3 id="working_with_et">Working with the ElementTree</h3>
+
+As mentioned, the Markdown parser converts a source document to an
+[ElementTree][] object before serializing that back to Unicode text.
+Markdown has provided some helpers to ease that manipulation within the context
+of the Markdown module.
+
+First, to get access to the ElementTree module import ElementTree from
+``markdown`` rather than importing it directly. This will ensure you are using
+the same version of ElementTree as markdown. The module is named ``etree``
+within Markdown.
+
+ from markdown import etree
+
+``markdown.etree`` tries to import ElementTree from any known location, first
+as a standard library module (from ``xml.etree`` in Python 2.5), then as a third
+party package (``Elementree``). In each instance, ``cElementTree`` is tried
+first, then ``ElementTree`` if the faster C implementation is not available on
+your system.
+
+Sometimes you may want text inserted into an element to be parsed by
+[InlinePatterns][]. In such a situation, simply insert the text into an
+`inline` tag and the text will be automatically run through the InlinePatterns.
+
+Here's a basic example which creates an HTML table (note that the contents of
+the second cell (``td2``) will be run through InlinePatterns latter):
+
+ table = etree.Element("table")
+ table.set("cellpadding", "2") # Set cellpadding to 2
+ tr = etree.SubElement(table, "tr") # Add child tr to table
+ td1 = etree.SubElement(tr, "td") # Add child td1 to tr
+ td1.text = "Cell content" # Add plain text content to td1 element
+ td2 = etree.SubElement(tr, "td") # Add second td to tr
+ inline = etree.SubElement(td2, "inline") # Add an inline element to td2
+ inline.text = "Some *text* with **inline** formatting." # Add markup text
+ table.tail = "Text after table" # Added text after table Element
+
+You can also manipulate an existing tree. Consider the following example which
+adds a ``class`` attribute to all ``a`` elements:
+
+ def set_link_class(self, element):
+ for child in element:
+ if child.tag == "a":
+ child.set("class", "myclass") #set the class attribute
+ set_link_class(child) # run recursively on children
+
+For more information about working with ElementTree see the ElementTree
+[Documentation](http://effbot.org/zone/element-index.htm)
+([Python Docs](http://docs.python.org/lib/module-xml.etree.ElementTree.html)).
+
+<h3 id="integrating_into_markdown">Integrating Your Code Into Markdown
+
+Once you have the various pieces of your extension built, you need to tell
+Markdown about them and ensure that they are run in the proper sequence.
+Markdown accepts a `Extension` instance for each extension. Therefore, you
+will need to define a class that extends `markdown.Extension` and over-rides
+the `extendMarkdown` method. Within this class you will manage configuration
+options for your extension and attach the various processors and patterns to
+the Markdown instance.
+
+It is important to note that the order of the various processors and patterns
+matters. For example, if we replace `http://...` links with <a> elements, and
+*then* try to deal with inline html, we will end up with a mess. Therefore,
+the various types of processors and patterns are stored within an instance of
+the Markdown class within lists. Your `Extension` class will need to manipulate
+those lists appropriately. You may insert instances of your processors and
+patterns into the appropriate location in a list, remove a built-in instances,
+or replace a built-in instance with your own.
+
+<h4 id="extendmarkdown">`extendMarkdown`</h4>
+
+The `extendMarkdown` method of a `markdown.Extension` class accepts two
+arguments:
+
+* `md`:
+
+ A pointer to the instance of the Markdown class. You should use this to
+ access the lists of processors and patterns. They are found under the
+ following attributes:
+
+ * `md.textPreprocessors`
+ * `md.preprocessors`
+ * `md.inlinePatterns`
+ * `md.postpreprocessors`
+ * `md.textPostprocessors`
+
+ Some other things you may want to access in the markdown instance are:
+
+ * `md.inlineStash`
+ * `md.htmlStash`
+ * `md.registerExtension()`
+
+* `md_globals`
+
+ Contains all the various global variables within the markdown module.
+
+Of course, with access to those items, theoretically you have the option to
+changing anything through various [monkey_patching][] techniques. In fact, this
+is how both the [[HeaderId]] and [[CodeHilite]] extensions work. However, you
+should be aware that the various undocumented or private parts of markdown may
+change without notice and your monkey_patches may break with a new release.
+Therefore, what you really should be doing is inserting processors and patterns
+into the markdown pipeline. Consider yourself warned.
+
+[monkey_patching]: http://en.wikipedia.org/wiki/Monkey_patch
+
+<h4 id="registerextension">registerExtension</h4>
+
+Some extensions may need to have their state reset between multiple runs of the
+Markdown class. For example, consider the following use of the [[Footnotes]]
+extension:
+
+ md = markdown.Markdown(extensions=['footnotes'])
+ html1 = md.convert(text_with_footnote)
+ md.reset()
+ html2 = md.convert(text_without_footnote)
+
+Without calling ``reset``, the footnote definitions from the first document will
+be inserted into the second document as they are still stored within the class
+instance. Therefore the ``Extension`` class needs to define a ``reset`` method
+that will reset the state of the extension (i.e.: ``self.footnotes = {}``).
+However, as many extensions do not have a need for ``reset``, ``reset`` is only
+called on extensions that are registered.
+
+To register an extension, call ``md.registerExtension`` from within your
+``extendMarkdown`` method:
+
+
+ def extendMarkdown(self, md, md_globals):
+ md.registerExtension(self)
+ # insert processors and patterns here
+
+Then, each time ``reset`` is called on the Markdown instance, the ``reset``
+method of each registered extension will be called as well. You should also
+note that ``reset`` will be called on each registered extension after it is
+initialized the first time. Keep that in mind when over-riding the extension's
+``reset`` method.
+
+<h4 id="configsettings">Config Settings</h4>
+
+If an extension uses any parameters that the user may want to change,
+those parameters should be stored in `self.config` of your `markdown.Extension`
+class in the following format:
+
+ self.config = {parameter_1_name : [value1, description1],
+ parameter_2_name : [value2, description2] }
+
+When stored this way the config parameters can be over-ridden from the
+command line or at the time Markdown is initiated:
+
+ markdown.py -x myextension(SOME_PARAM=2) inputfile.txt > output.txt
+
+Note that parameters should always be assumed to be set to string
+values, and should be converted at run time. For example:
+
+ i = int(self.getConfig("SOME_PARAM"))
+
+<h4 id="makeextension">`makeExtension`</h4>
+
+Each extension should ideally be placed in its own module starting
+with the ``mdx_`` prefix (e.g. ``mdx_footnotes.py``). The module must
+provide a module-level function called ``makeExtension`` that takes
+an optional parameter consisting of a dictionary of configuration over-rides
+and returns an instance of the extension. An example from the footnote
+extension:
+
+ def makeExtension(configs=None) :
+ return FootnoteExtension(configs=configs)
+
+By following the above example, when Markdown is passed the name of your
+extension as a string (i.e.: ``'footnotes'``), it will automatically import
+the module and call the ``makeExtension`` function initiating your extension.
+
+You may have noted that the extensions packaged with Python-Markdown do not
+use the ``mdx_`` prefix in their module names. This is because they are all
+part of the ``markdown_extensions`` package. Markdown will first try to import
+from ``markdown_extensions.extname`` and upon failure, ``mdx_extname``. If both
+fail, Markdown will continue without the extension.
+
+However, Markdown will also accept an already existing instance of an extension.
+For example:
+
+ import markdown
+ import myextension
+ configs = {...}
+ myext = myextension.MyExtension(configs=configs)
+ md = markdown.Markdown(extensions=[myext])
+
+This is useful if you need to implement a large number of extensions with more
+than one residing in a module.
+
+[Preprocessors]: #preprocessors
+[TextPreprocessors]: #textpreprocessors
+[Line Preprocessors]: #linepreprocessors
+[InlinePatterns]: #inlinepatterns
+[Postprocessors]: #postprocessors
+[ElementTree Postprocessors]: #etpostprocessors
+[TextProstprocessors]: #textpostprocessors
+[Working with the ElementTree]: #working_with_et
+[Integrating your code into Markdown]: #integrating_into_markdown
+[extendMarkdown]: #extendmarkdown
+[registerExtension]: #registerextension
+[Config Settings]: #configsettings
+[makeExtension]: #makeextension
+[ElementTree]: http://effbot.org/zone/element-index.htm
diff --git a/markdown.py b/markdown.py
index df85bab..f0bc0e8 100755
--- a/markdown.py
+++ b/markdown.py
@@ -15,8 +15,8 @@ script. (You might want to read that before you try modifying this
file.)
Started by [Manfred Stienstra](http://www.dwerg.net/). Continued and
-maintained by [Yuri Takhteyev](http://www.freewisdom.org) and [Waylan
-Limberg](http://achinghead.com/).
+maintained by [Yuri Takhteyev](http://www.freewisdom.org), [Waylan
+Limberg](http://achinghead.com/) and [Artem Yunusov](http://blog.splyer.com).
Contact:
@@ -53,15 +53,15 @@ def message(level, text):
logger.log(level, text)
def isstr(s):
+ """ Check if it's string """
return isinstance(s, unicode) or isinstance(s, str)
def importETree():
- """ Imports best variant of ElementTree
- and returns module object """
-
+ """ Import best variant of ElementTree and return module object """
+ cetree = None
try:
# Python 2.5+
- import xml.etree.cElementTree as etree
+ import xml.etree.cElementTree as cetree
except ImportError:
try:
# Python 2.5+
@@ -69,7 +69,7 @@ def importETree():
except ImportError:
try:
# normal cElementTree install
- import cElementTree as etree
+ import cElementTree as cetree
except ImportError:
try:
# normal ElementTree install
@@ -78,11 +78,28 @@ def importETree():
message(CRITICAL,
"Failed to import ElementTree from any known place")
sys.exit(1)
+ if cetree:
+ if cetree.VERSION < "1.0":
+ message(CRITICAL,
+ "cElementTree version is too old, 1.0 and upper required")
+ sys.exit(1)
+
+ etree = cetree
+ else:
+ if etree.VERSION < "1.1":
+ message(CRITICAL,
+ "ElementTree version is too old, 1.1 and upper required")
+ sys.exit(1)
+
return etree
-etree = importETree()
+"""ElementTree module
+in extensions use: `from markdown import etree`
+to access to the ElemetTree module, do not import it by yourself"""
+etree = importETree()
def indentETree(elem, level=0):
+ """ Indent ElementTree before serialization """
if level > 1:
i = "\n" + (level-1) * " "
@@ -148,7 +165,6 @@ EXECUTABLE_NAME_FOR_USAGE = "python markdown.py"
# --------------- CONSTANTS YOU _SHOULD NOT_ HAVE TO CHANGE ----------
-AND_SUBSTITUTE = unichr(2) + unichr(4) + unichr(3)
# placeholders
STX = u'\u0002' # Use STX ("Start of text") for start-of-placeholder
@@ -158,6 +174,8 @@ HTML_PLACEHOLDER = HTML_PLACEHOLDER_PREFIX + "%d"+ETX
INLINE_PLACEHOLDER_PREFIX = STX+"inline:"
INLINE_PLACEHOLDER_SUFFIX = ETX
+AMP_SUBSTITUTE = STX+"amp"+ETX
+
BLOCK_LEVEL_ELEMENTS = ['p', 'div', 'blockquote', 'pre', 'table',
'dl', 'ol', 'ul', 'script', 'noscript',
@@ -174,16 +192,18 @@ def isBlockLevel (tag):
def codepoint2name(code):
- """ Returns entity definition by code, or code
- if there is no such entity definition"""
+ """
+ Return entity definition by code, or code
+ if there is no such entity definition
+ """
entity = htmlentitydefs.codepoint2name.get(code)
if entity:
- return "%s%s;" % (AND_SUBSTITUTE, entity)
+ return "%s%s;" % (AMP_SUBSTITUTE, entity)
else:
- return "%s#%d;" % (AND_SUBSTITUTE, code)
+ return "%s#%d;" % (AMP_SUBSTITUTE, code)
def handleAttributes(text, parent):
-
+ """ Handale attributes, e.g {@id=123} """
def attributeCallback(match):
parent.set(match.group(1), match.group(2))
@@ -613,7 +633,7 @@ class BacktickPattern (Pattern):
class DoubleTagPattern (SimpleTagPattern):
"""
Return a ElementTree element nested in tag2 nested in tag1.
- Usefull for strong emphasis etc.
+ Useful for strong emphasis etc.
"""
def handleMatch(self, m):
@@ -774,7 +794,7 @@ class AutomailPattern (Pattern):
el.text += codepoint2name(ord(letter))
mailto = "mailto:" + email
- mailto = "".join([AND_SUBSTITUTE + '#%d;' %
+ mailto = "".join([AMP_SUBSTITUTE + '#%d;' %
ord(letter) for letter in mailto])
el.set('href', mailto)
return el
@@ -836,7 +856,7 @@ class Postprocessor:
"""
Subclasses of Postprocessor should implement a `run` method, which
takes a root Element. Method can return another Element, and global
- root Element will be replaced, or just mnodify current and return None.
+ root Element will be replaced, or just modify current and return None.
"""
pass
@@ -857,7 +877,7 @@ class TextPostprocessor:
"""
Subclasses of TextPostprocessor should implement a `run` method, which
takes the html document as a single text string and returns a
- (possably modified) string.
+ (possibly modified) string.
"""
pass
@@ -903,10 +923,10 @@ class AndSubstitutePostprocessor(TextPostprocessor):
def run(self, text):
- text = text.replace(AND_SUBSTITUTE, "&")
+ text = text.replace(AMP_SUBSTITUTE, "&")
return text
-ANDSUBSTITUTETEXTPOSTPROCESSOR = AndSubstitutePostprocessor()
+AMPSUBSTITUTETEXTPOSTPROCESSOR = AndSubstitutePostprocessor()
"""
@@ -1046,20 +1066,31 @@ def dequote(string):
class InlineStash:
def __init__(self):
+ """ Create a InlineStash. """
self.prefix = INLINE_PLACEHOLDER_PREFIX
self.suffix = INLINE_PLACEHOLDER_SUFFIX
self._nodes = {}
self.phLength = 4 + len(self.prefix) + len(self.suffix)
def _genPlaceholder(self, type):
- """ Generates placeholder """
+ """ Generate a placeholder """
id = "%04d" % len(self._nodes)
hash = "%s%s:%s%s" % (self.prefix, type, id,
self.suffix)
return hash, id
def extractId(self, data, index):
- """ Extracting id from data string, starting from 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
+ found placeholder
+ """
endIndex = data.find(self.suffix, index+1)
if endIndex == -1:
return None, index + 1
@@ -1074,15 +1105,17 @@ class InlineStash:
return self._nodes.has_key(id)
def get(self, id):
- """ Returns node by id """
+ """ Return node by id """
return self._nodes.get(id)
def add(self, node, type):
+ """ Add node to stash """
pholder, id = self._genPlaceholder(type)
self._nodes[id] = node
return pholder
def rest(self):
+ """ Reset instance """
self._nodes = {}
"""
@@ -1133,7 +1166,7 @@ class Markdown:
"""
- def __init__(self, source=None, # depreciated
+ def __init__(self,
extensions=[],
extension_configs={},
safe_mode = False):
@@ -1142,7 +1175,6 @@ class Markdown:
Keyword arguments:
- * source: The text in Markdown format. Depreciated!
* extensions: A list of extensions.
If they are of type string, the module mdx_name.py will be loaded.
If they are a subclass of markdown.Extension, they will be used
@@ -1152,9 +1184,7 @@ class Markdown:
"""
- self.source = source
- if source is not None:
- message(WARN, "The `source` arg of Markdown.__init__() is depreciated and will be removed in the future. Use `instance.convert(source)` instead.")
+ self.source = None
self.safeMode = safe_mode
self.blockGuru = BlockGuru()
self.registeredExtensions = []
@@ -1176,7 +1206,7 @@ class Markdown:
self.textPostprocessors = [# a footnote postprocessor will get
# inserted here
RAWHTMLTEXTPOSTPROCESSOR,
- ANDSUBSTITUTETEXTPOSTPROCESSOR]
+ AMPSUBSTITUTETEXTPOSTPROCESSOR]
self.prePatterns = []
@@ -1620,7 +1650,7 @@ class Markdown:
def _handleInline(self, data, patternIndex=0):
"""
- Processinf string with inline patterns and replasing it
+ Process string with inline patterns and replace it
with placeholders
Keyword arguments:
@@ -1628,7 +1658,7 @@ class Markdown:
* data: A line of Markdown text
* patternIndex: The index of the inlinePattern to start with
- Return: String with placeholders.
+ Returns: String with placeholders.
"""
startIndex = 0
@@ -1646,19 +1676,17 @@ class Markdown:
def _applyInline(self, pattern, data, patternIndex, startIndex=0):
"""
- Given a pattern name, this function checks if the line
- fits the pattern, creates the necessary elements, adds it
- to InlineStash, and returns string with placeholders,
- instead of ElementTree elements.
+ Check if the line fits the pattern, create the necessary
+ elements, add it to InlineStash
Keyword arguments:
* data: the text to be processed
* pattern: the pattern to be checked
* patternIndex: index of current pattern
- * startIndex: string index, from wich we starting search
+ * startIndex: string index, from which we starting search
- Returns: String with placeholders.
+ Returns: String with placeholders instead of ElementTree elements.
"""
match = pattern.getCompiledRegExp().match(data[startIndex:])
leftData = data[:startIndex]
@@ -1690,7 +1718,19 @@ class Markdown:
pholder, match.groups()[-1]), True, 0
def _processElementText(self, node, subnode, isText=True):
+ """
+ Process placeholders in Element.text or Element.tail
+ of Elements popped from InlineStash
+
+ 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
@@ -1712,9 +1752,12 @@ class Markdown:
def _processPlaceholders(self, data, parent):
"""
- Processes string with placeholders and generates ElementTree tree.
+ 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.
"""
@@ -1783,18 +1826,22 @@ class Markdown:
data = ""
return result
-
- def _processTree(self, el):
+
+
+ def applyInlinePatterns(self, markdownTree):
"""
- Processing ElementTree, and applying inline patterns
+ Iterate over ElementTree, find elements with inline tag,
+ apply inline patterns and append newly created Elements to tree
Keyword arguments:
- * el - parent element of ElementTree.
+ * markdownTree: ElementTree object, representing Markdown tree.
Returns: ElementTree object with applied inline patterns.
"""
-
+
+ el = markdownTree.getroot()
+
stack = [el]
while stack:
@@ -1831,32 +1878,15 @@ class Markdown:
newChild)
currElement.insert(pos, newChild)
pos += 1
-
-
- def applyInlinePatterns(self, markdownTree):
- """
- Retrun ElementTree, with applied
- inline paterns
-
- Keyword arguments:
-
- * markdownTree: ElementTree object, reppresenting Markdown tree.
-
- Returns: ElementTree object.
- """
-
- el = markdownTree.getroot()
-
- self._processTree(el)
return markdownTree
def markdownToTree(self, source=None):
"""
- Retrun ElementTree, without applying inline paterns,
- all data, that should be processed with
- inline patterns included in <inline></inline> sections.
+ Create ElementTree, without applying inline paterns,
+ all data, include all data, that must be procesed wit inline
+ patterns in <inline></inline> sections.
Keyword arguments:
@@ -1890,13 +1920,11 @@ class Markdown:
markdownTree = self._transform()
- return markdownTree
-
-
+ return markdownTree
def convert (self, source=None):
"""
- Return the document in XHTML format.
+ Create the document in XHTML format.
Keyword arguments:
@@ -2098,17 +2126,22 @@ def load_extension(ext_name, configs = []):
pairs = [x.split("=") for x in ext_args.split(",")]
configs.update([(x.strip(), y.strip()) for (x, y) in pairs])
- extension_module_name = "mdx_" + ext_name
+ ext_module = 'markdown_extensions'
+ module_name = '.'.join([ext_module, ext_name])
+ extension_module_name = '_'.join(['mdx', ext_name])
try:
- module = __import__(extension_module_name)
-
+ module = __import__(module_name, {}, {}, [ext_module])
except ImportError:
- message(WARN,
- "Couldn't load extension '%s' from \"%s\" - continuing without."
- % (ext_name, extension_module_name) )
- # Return a dummy (do nothing) Extension as silent failure
- return Extension(configs={})
+ try:
+ module = __import__(extension_module_name)
+ except:
+ message(WARN,
+ "Failed loading extension '%s' from '%s' or '%s' "
+ "- continuing without."
+ % (ext_name, module_name, extension_module_name) )
+ # Return a dummy (do nothing) Extension as silent failure
+ return Extension(configs={})
return module.makeExtension(configs.items())
diff --git a/markdown_extensions/__init__.py b/markdown_extensions/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/markdown_extensions/__init__.py
diff --git a/mdx_codehilite.py b/markdown_extensions/codehilite.py
index 73c1a79..73c1a79 100644
--- a/mdx_codehilite.py
+++ b/markdown_extensions/codehilite.py
diff --git a/mdx_extra.py b/markdown_extensions/extra.py
index e99bec4..e99bec4 100644
--- a/mdx_extra.py
+++ b/markdown_extensions/extra.py
diff --git a/mdx_fenced_code.py b/markdown_extensions/fenced_code.py
index c3d9f7f..c3d9f7f 100644
--- a/mdx_fenced_code.py
+++ b/markdown_extensions/fenced_code.py
diff --git a/mdx_footnotes.py b/markdown_extensions/footnotes.py
index b46efbb..b46efbb 100644
--- a/mdx_footnotes.py
+++ b/markdown_extensions/footnotes.py
diff --git a/mdx_headerid.py b/markdown_extensions/headerid.py
index 2360071..2360071 100644
--- a/mdx_headerid.py
+++ b/markdown_extensions/headerid.py
diff --git a/mdx_imagelinks.py b/markdown_extensions/imagelinks.py
index e545b24..e545b24 100644
--- a/mdx_imagelinks.py
+++ b/markdown_extensions/imagelinks.py
diff --git a/mdx_meta.py b/markdown_extensions/meta.py
index 30dea8a..30dea8a 100644
--- a/mdx_meta.py
+++ b/markdown_extensions/meta.py
diff --git a/mdx_rss.py b/markdown_extensions/rss.py
index b88b9b5..b88b9b5 100644
--- a/mdx_rss.py
+++ b/markdown_extensions/rss.py
diff --git a/mdx_tables.py b/markdown_extensions/tables.py
index 829044c..829044c 100644
--- a/mdx_tables.py
+++ b/markdown_extensions/tables.py
diff --git a/mdx_wikilink.py b/markdown_extensions/wikilink.py
index 47037a6..47037a6 100644
--- a/mdx_wikilink.py
+++ b/markdown_extensions/wikilink.py
diff --git a/odt2txt.py b/scripts/odt2txt.py
index bb8fab0..bb8fab0 100644
--- a/odt2txt.py
+++ b/scripts/odt2txt.py
diff --git a/setup.py b/setup.py
index b47c08b..424b7dd 100644
--- a/setup.py
+++ b/setup.py
@@ -10,16 +10,7 @@ setup(
maintainer_email = "waylan [at] gmail.com",
url = "http://www.freewisdom.org/projects/python-markdown",
license = "BSD License, GNU Public License (GPL)",
- py_modules = ["markdown",
- "mdx_codehilite",
- "mdx_fenced_code",
- "mdx_footnotes",
- "mdx_headerid",
- "mdx_imagelinks",
- "mdx_meta",
- "mdx_rss",
- "mdx_tables",
- "mdx_wikilink",
- ],
+ py_modules = ["markdown"],
+ packages = ['markdown_extensions'],
scripts = ['scripts/pymarkdown.py'],
)
diff --git a/tests/misc/headers.html b/tests/misc/headers.html
index a65d7b2..fb553ff 100644
--- a/tests/misc/headers.html
+++ b/tests/misc/headers.html
@@ -3,7 +3,10 @@
Line 3</p>
<h1>[Markdown][5]</h1>
<h1>
-<a href="http://some.link.com/">Markdown</a>
+ <a href="http://some.link.com/">Markdown</a>
</h1>
<h1>[5]: http://foo.com/</h1>
-<h1>Issue #1: Markdown</h1> \ No newline at end of file
+<h1>Issue #1: Markdown</h1>
+<p>Text</p>
+<h1>Header</h1>
+<p>Some other text</p>
diff --git a/tests/misc/headers.txt b/tests/misc/headers.txt
index 2ddb391..db114ed 100644
--- a/tests/misc/headers.txt
+++ b/tests/misc/headers.txt
@@ -9,3 +9,7 @@ Line 3
# [5]: http://foo.com/
# Issue #1: Markdown
+
+Text
+# Header
+Some other text