aboutsummaryrefslogtreecommitdiffstats
path: root/docs/writing_extensions.txt
diff options
context:
space:
mode:
authorWaylan Limberg <waylan@gmail.com>2011-08-23 10:53:49 -0400
committerWaylan Limberg <waylan@gmail.com>2011-08-23 10:53:49 -0400
commita6fd7b5cba69cad049046b3a372d0f2d6c495abe (patch)
tree42a8987d08eb5a05eb2b3cb1399540583e8cbf67 /docs/writing_extensions.txt
parente5b2813bbf02710c7deb148896085a3dac4828dc (diff)
downloadmarkdown-a6fd7b5cba69cad049046b3a372d0f2d6c495abe.tar.gz
markdown-a6fd7b5cba69cad049046b3a372d0f2d6c495abe.tar.bz2
markdown-a6fd7b5cba69cad049046b3a372d0f2d6c495abe.zip
Renamed *.txt -> *.md in docs.
Diffstat (limited to 'docs/writing_extensions.txt')
-rw-r--r--docs/writing_extensions.txt611
1 files changed, 0 insertions, 611 deletions
diff --git a/docs/writing_extensions.txt b/docs/writing_extensions.txt
deleted file mode 100644
index 2ecd4c9..0000000
--- a/docs/writing_extensions.txt
+++ /dev/null
@@ -1,611 +0,0 @@
-Writing Extensions for Python-Markdown
-======================================
-
-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. If you really want to dive in,
-there are also blockprocessors which are part of the core BlockParser.
-
-As the parser builds an [ElementTree][] object which is later rendered
-as Unicode text, there are also some helpers provided to ease manipulation of
-the tree. Each part of the API is discussed in its respective section below.
-Additionaly, reading the source of some [[Available Extensions]] may be helpful.
-For example, the [[Footnotes]] extension uses most of the features documented
-here.
-
-* [Preprocessors][]
-* [InlinePatterns][]
-* [Treeprocessors][]
-* [Postprocessors][]
-* [BlockParser][]
-* [Working with the ElementTree][]
-* [Integrating your code into Markdown][]
- * [extendMarkdown][]
- * [OrderedDict][]
- * [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.
-
-Preprocessors should inherit from ``markdown.preprocessors.Preprocessor`` and
-implement a ``run`` method with one argument ``lines``. The ``run`` method of
-each Preprocessor will be passed the entire source text as a list of Unicode
-strings. Each string will contain one line of text. The ``run`` method should
-return either that list, or an altered list of Unicode strings.
-
-A pseudo example:
-
- from markdown.preprocessors import Preprocessor
-
- class MyPreprocessor(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.inlinepatterns.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 and set the
-`re.DOTALL` and `re.UNICODE` flags. This means that the first group of your
-match will be ``m.group(2)`` as ``m.group(1)`` will match everything before the
-pattern.
-
-For an example, consider this simplified emphasis pattern:
-
- from markdown.inlinepatterns import Pattern
- from markdown.util import etree
-
- class EmphasisPattern(Pattern):
- def handleMatch(self, m):
- el = 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 the Pattern classes found at `markdown.inlinepatterns`.
-
-**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="treeprocessors">Treeprocessors</h3>
-
-Treeprocessors manipulate an ElemenTree object after it has passed through the
-core BlockParser. This is where additional manipulation of the tree takes
-place. Additionally, the InlineProcessor is a Treeprocessor which steps through
-the tree and runs the InlinePatterns on the text of each Element in the tree.
-
-A Treeprocessor should inherit from ``markdown.treeprocessors.Treeprocessor``,
-over-ride the ``run`` method which takes one argument ``root`` (an Elementree
-object) and returns either that root element or a modified root element.
-
-A pseudo example:
-
- from markdown.treprocessors import Treeprocessor
-
- class MyTreeprocessor(Treeprocessor):
- def run(self, root):
- #do stuff
- return my_modified_root
-
-For specifics on manipulating the ElementTree, see
-[Working with the ElementTree][] below.
-
-<h3 id="postprocessors">Postprocessors</h3>
-
-Postprocessors manipulate the document after the ElementTree has been
-serialized into a string. Postprocessors should be used to work with the
-text just before output.
-
-A Postprocessor should inherit from ``markdown.postprocessors.Postprocessor``
-and over-ride the ``run`` method which takes one argument ``text`` and returns
-a Unicode string.
-
-Postprocessors 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:
-
- from markdown.postprocessors import Postprocessor
-
- class TocPostprocessor(Postprocessor):
- def run(self, text):
- return MYMARKERRE.sub(MyToc, text)
-
-<h3 id="blockparser">BlockParser</h3>
-
-Sometimes, pre/tree/postprocessors and Inline Patterns aren't going to do what
-you need. Perhaps you want a new type of block type that needs to be integrated
-into the core parsing. In such a situation, you can add/change/remove
-functionality of the core ``BlockParser``. The BlockParser is composed of a
-number of Blockproccessors. The BlockParser steps through each block of text
-(split by blank lines) and passes each block to the appropriate Blockprocessor.
-That Blockprocessor parses the block and adds it to the ElementTree. The
-[[Definition Lists]] extension would be a good example of an extension that
-adds/modifies Blockprocessors.
-
-A Blockprocessor should inherit from ``markdown.blockprocessors.BlockProcessor``
-and implement both the ``test`` and ``run`` methods.
-
-The ``test`` method is used by BlockParser to identify the type of block.
-Therefore the ``test`` method must return a boolean value. If the test returns
-``True``, then the BlockParser will call that Blockprocessor's ``run`` method.
-If it returns ``False``, the BlockParser will move on to the next
-BlockProcessor.
-
-The **``test``** method takes two arguments:
-
-* **``parent``**: The parent etree Element of the block. This can be useful as
- the block may need to be treated differently if it is inside a list, for
- example.
-
-* **``block``**: A string of the current block of text. The test may be a
- simple string method (such as ``block.startswith(some_text)``) or a complex
- regular expression.
-
-The **``run``** method takes two arguments:
-
-* **``parent``**: A pointer to the parent etree Element of the block. The run
- method will most likely attach additional nodes to this parent. Note that
- nothing is returned by the method. The Elementree object is altered in place.
-
-* **``blocks``**: A list of all remaining blocks of the document. Your run
- method must remove (pop) the first block from the list (which it altered in
- place - not returned) and parse that block. You may find that a block of text
- legitimately contains multiple block types. Therefore, after processing the
- first type, your processor can insert the remaining text into the beginning
- of the ``blocks`` list for future parsing.
-
-Please be aware that a single block can span multiple text blocks. For example,
-The official Markdown syntax rules state that a blank line does not end a
-Code Block. If the next block of text is also indented, then it is part of
-the previous block. Therefore, the BlockParser was specifically designed to
-address these types of situations. If you notice the ``CodeBlockProcessor``,
-in the core, you will note that it checks the last child of the ``parent``.
-If the last child is a code block (``<pre><code>...</code></pre>``), then it
-appends that block to the previous code block rather than creating a new
-code block.
-
-Each BlockProcessor has the following utility methods available:
-
-* **``lastChild(parent)``**:
-
- Returns the last child of the given etree Element or ``None`` if it had no
- children.
-
-* **``detab(text)``**:
-
- Removes one level of indent (four spaces by default) from the front of each
- line of the given text string.
-
-* **``looseDetab(text, level)``**:
-
- Removes "level" levels of indent (defaults to 1) from the front of each line
- of the given text string. However, this methods allows secondary lines to
- not be indented as does some parts of the Markdown syntax.
-
-Each BlockProcessor also has a pointer to the containing BlockParser instance at
-``self.parser``, which can be used to check or alter the state of the parser.
-The BlockParser tracks it's state in a stack at ``parser.state``. The state
-stack is an instance of the ``State`` class.
-
-**``State``** is a subclass of ``list`` and has the additional methods:
-
-* **``set(state)``**:
-
- Set a new state to string ``state``. The new state is appended to the end
- of the stack.
-
-* **``reset()``**:
-
- Step back one step in the stack. The last state at the end is removed from
- the stack.
-
-* **``isstate(state)``**:
-
- Test that the top (current) level of the stack is of the given string
- ``state``.
-
-Note that to ensure that the state stack doesn't become corrupted, each time a
-state is set for a block, that state *must* be reset when the parser finishes
-parsing that block.
-
-An instance of the **``BlockParser``** is found at ``Markdown.parser``.
-``BlockParser`` has the following methods:
-
-* **``parseDocument(lines)``**:
-
- Given a list of lines, an ElementTree object is returned. This should be
- passed an entire document and is the only method the ``Markdown`` class
- calls directly.
-
-* **``parseChunk(parent, text)``**:
-
- Parses a chunk of markdown text composed of multiple blocks and attaches
- those blocks to the ``parent`` Element. The ``parent`` is altered in place
- and nothing is returned. Extensions would most likely use this method for
- block parsing.
-
-* **``parseBlocks(parent, blocks)``**:
-
- Parses a list of blocks of text and attaches those blocks to the ``parent``
- Element. The ``parent`` is altered in place and nothing is returned. This
- method will generally only be used internally to recursively parse nested
- blocks of text.
-
-While is is not recommended, an extension could subclass or completely replace
-the ``BlockParser``. The new class would have to provide the same public API.
-However, be aware that other extensions may expect the core parser provided
-and will not work with such a drastically different parser.
-
-<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 found at
-``markdown.util.etree`` within Markdown.
-
- from markdown.util import etree
-
-``markdown.util.etree`` tries to import ElementTree from any known location,
-first as a standard library module (from ``xml.etree`` in Python 2.5), then as
-a third party package (``Elementree``). In each instance, ``cElementTree`` is
-tried first, then ``ElementTree`` if the faster C implementation is not
-available on your system.
-
-Sometimes you may want text inserted into an element to be parsed by
-[InlinePatterns][]. In such a situation, simply insert the text as you normally
-would and the text will be automatically run through the InlinePatterns.
-However, if you do *not* want some text to be parsed by InlinePatterns,
-then insert the text as an ``AtomicString``.
-
- from markdown.util import AtomicString
- some_element.text = AtomicString(some_text)
-
-Here's a basic example which creates an HTML table (note that the contents of
-the second cell (``td2``) will be run through InlinePatterns latter):
-
- 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 = markdown.AtomicString("Cell content") # Add plain text content
- td2 = etree.SubElement(tr, "td") # Add second td to tr
- td2.text = "*text* with **inline** formatting." # Add markup text
- table.tail = "Text after table" # Add text after table
-
-You can also manipulate an existing tree. Consider the following example which
-adds a ``class`` attribute to ``<a>`` elements:
-
- 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</h3>
-
-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.extensions.Extension`` and
-over-rides the ``extendMarkdown`` method. Within this class you will manage
-configuration options for your extension and attach the various processors and
-patterns to the Markdown instance.
-
-It is important to note that the order of the various processors and patterns
-matters. For example, if we replace ``http://...`` links with <a> elements, and
-*then* try to deal with inline html, we will end up with a mess. Therefore,
-the various types of processors and patterns are stored within an instance of
-the Markdown class in [OrderedDict][]s. Your ``Extension`` class will need to
-manipulate those OrderedDicts appropriately. You may insert instances of your
-processors and patterns into the appropriate location in an OrderedDict, remove
-a built-in instance, or replace a built-in instance with your own.
-
-<h4 id="extendmarkdown">extendMarkdown</h4>
-
-The ``extendMarkdown`` method of a ``markdown.extensions.Extension`` class
-accepts two arguments:
-
-* **``md``**:
-
- A pointer to the instance of the Markdown class. You should use this to
- access the [OrderedDict][]s of processors and patterns. They are found
- under the following attributes:
-
- * ``md.preprocessors``
- * ``md.inlinePatterns``
- * ``md.parser.blockprocessors``
- * ``md.treepreprocessors``
- * ``md.postprocessors``
-
- Some other things you may want to access in the markdown instance are:
-
- * ``md.htmlStash``
- * ``md.output_formats``
- * ``md.set_output_format()``
- * ``md.registerExtension()``
- * ``md.html_replacement_text``
- * ``md.tab_length``
- * ``md.enable_attributes``
- * ``md.smart_emphasis``
-
-* **``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. 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
-
-A simple example:
-
- from markdown.extensions import Extension
-
- class MyExtension(Extension):
- def extendMarkdown(self, md, md_globals):
- # Insert instance of 'mypattern' before 'references' pattern
- md.inlinePatterns.add('mypattern', MyPattern(md), '<references')
-
-<h4 id="ordereddict">OrderedDict</h4>
-
-An OrderedDict is a dictionary like object that retains the order of it's
-items. The items are ordered in the order in which they were appended to
-the OrderedDict. However, an item can also be inserted into the OrderedDict
-in a specific location in relation to the existing items.
-
-Think of OrderedDict as a combination of a list and a dictionary as it has
-methods common to both. For example, you can get and set items using the
-``od[key] = value`` syntax and the methods ``keys()``, ``values()``, and
-``items()`` work as expected with the keys, values and items returned in the
-proper order. At the same time, you can use ``insert()``, ``append()``, and
-``index()`` as you would with a list.
-
-Generally speaking, within Markdown extensions you will be using the special
-helper method ``add()`` to add additional items to an existing OrderedDict.
-
-The ``add()`` method accepts three arguments:
-
-* **``key``**: A string. The key is used for later reference to the item.
-
-* **``value``**: The object instance stored in this item.
-
-* **``location``**: Optional. The items location in relation to other items.
-
- Note that the location can consist of a few different values:
-
- * The special strings ``"_begin"`` and ``"_end"`` insert that item at the
- beginning or end of the OrderedDict respectively.
-
- * A less-than sign (``<``) followed by an existing key (i.e.:
- ``"<somekey"``) inserts that item before the existing key.
-
- * A greater-than sign (``>``) followed by an existing key (i.e.:
- ``">somekey"``) inserts that item after the existing key.
-
-Consider the following example:
-
- >>> from markdown.odict import OrderedDict
- >>> od = OrderedDict()
- >>> od['one'] = 1 # The same as: od.add('one', 1, '_begin')
- >>> od['three'] = 3 # The same as: od.add('three', 3, '>one')
- >>> od['four'] = 4 # The same as: od.add('four', 4, '_end')
- >>> od.items()
- [("one", 1), ("three", 3), ("four", 4)]
-
-Note that when building an OrderedDict in order, the extra features of the
-``add`` method offer no real value and are not necessary. However, when
-manipulating an existing OrderedDict, ``add`` can be very helpful. So let's
-insert another item into the OrderedDict.
-
- >>> od.add('two', 2, '>one') # Insert after 'one'
- >>> od.values()
- [1, 2, 3, 4]
-
-Now let's insert another item.
-
- >>> od.add('twohalf', 2.5, '<three') # Insert before 'three'
- >>> od.keys()
- ["one", "two", "twohalf", "three", "four"]
-
-Note that we also could have set the location of "twohalf" to be 'after two'
-(i.e.: ``'>two'``). However, it's unlikely that you will have control over the
-order in which extensions will be loaded, and this could affect the final
-sorted order of an OrderedDict. For example, suppose an extension adding
-'twohalf' in the above examples was loaded before a separate extension which
-adds 'two'. You may need to take this into consideration when adding your
-extension components to the various markdown OrderedDicts.
-
-Once an OrderedDict is created, the items are available via key:
-
- MyNode = od['somekey']
-
-Therefore, to delete an existing item:
-
- del od['somekey']
-
-To change the value of an existing item (leaving location unchanged):
-
- od['somekey'] = MyNewObject()
-
-To change the location of an existing item:
-
- t.link('somekey', '<otherkey')
-
-<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
-[InlinePatterns]: #inlinepatterns
-[Treeprocessors]: #treeprocessors
-[Postprocessors]: #postprocessors
-[BlockParser]: #blockparser
-[Working with the ElementTree]: #working_with_et
-[Integrating your code into Markdown]: #integrating_into_markdown
-[extendMarkdown]: #extendmarkdown
-[OrderedDict]: #ordereddict
-[registerExtension]: #registerextension
-[Config Settings]: #configsettings
-[makeExtension]: #makeextension
-[ElementTree]: http://effbot.org/zone/element-index.htm