diff options
-rw-r--r-- | .gitignore | 4 | ||||
-rw-r--r-- | MANIFEST | 31 | ||||
-rw-r--r-- | docs/AUTHORS | 36 | ||||
-rw-r--r-- | docs/CHANGE_LOG (renamed from CHANGE_LOG.txt) | 0 | ||||
-rw-r--r-- | docs/INSTALL | 59 | ||||
-rw-r--r-- | docs/README (renamed from README.txt) | 0 | ||||
-rw-r--r-- | docs/README.html (renamed from README.html) | 0 | ||||
-rwxr-xr-x | docs/writing_extensions.txt | 388 | ||||
-rwxr-xr-x | markdown.py | 183 | ||||
-rw-r--r-- | markdown_extensions/__init__.py | 0 | ||||
-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.py | 13 | ||||
-rw-r--r-- | tests/misc/headers.html | 7 | ||||
-rw-r--r-- | tests/misc/headers.txt | 4 |
24 files changed, 622 insertions, 103 deletions
@@ -2,5 +2,5 @@ *.bak *.tmp tmp/* -__init__.py -markdown_old.py +build/* +dist/* @@ -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.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 @@ -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 |