From 13f2bf66a5683aafc6c124af3585b4989042b301 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Thu, 7 Aug 2008 17:10:34 -0400 Subject: First draft of writing_extensions.txt. It's a little rough yet and still lacks documentation regarding the use of ElementTree. --- writing_extensions.txt | 298 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 298 insertions(+) create mode 100644 writing_extensions.txt diff --git a/writing_extensions.txt b/writing_extensions.txt new file mode 100644 index 0000000..2ae279c --- /dev/null +++ b/writing_extensions.txt @@ -0,0 +1,298 @@ +### 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][] DOM object which is later rendered +as Unicode text, there are also some helpers provided to make manipulation of +the DOM tree easier. Each part of the API is discussed in its respective +section below. You may find reading the source of some [[existing extensions]] +helpful as well. For example, the [[footnote]] extension uses most of the +features documented here. + +* [Preprocessors][] + * [TextPreprocessors][] + * [Line Preprocessors][] +* [InlinePatterns][] +* [Postprocessors][] + * [DOM Postprocessors][] + * [TextProstprocessors][] +* [Working with the DOM][] +* [Integrating your code into Markdown][] + * [extendMarkdown][] + * [Config Settings][] + * [makeExtension][] + +

Preprocessors

+ +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][]. + +

TextPreprocessors

+ +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. + +

Line Preprocessors

+ +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 + +

Inline Patterns

+ +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. + +

Postprocessors

+ +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: [DOM Postprocessors][] and +[TextPostprocessors][]. + +

DOM Postprocessors

+ +A DOM Postprocessor should inherit from `markdown.Postprocessor` and over-ride +the `run` method which takes one argument `root` and should 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 DOM, see [Working with the DOM][] below. + +

TextPostprocessors

+ +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 DOM 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) + +

Working with the DOM

+ +As mentioned, the Markdown parser converts a source document to an +[ElementTree][] DOM object before serializing that back to Unicode text. +Markdown has provided some helpers to ease that manipulation within the context +of the Markdown module... + +

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 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. + +

`extendMarkdown`

+ +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 monkeypatching techniques. However, you should +be aware that the various undocumented or private parts of markdown may change +without notice and your monkeypatches may no longer work. Therefore, what you +really should be doing is inserting processors and patterns into the markdown +pipeline. + +

Config Settings

+ +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")) + +

`makeExtension`

+ +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. + +However, Markdown will also accept an already existing instance of an extension.For example: + + import markdown, mdx_myextension + configs = {...} + myext = mdx_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 +[DOM Postprocessors]: #dompostprocessors +[TextProstprocessors]: #textpostprocessors +[Working with the DOM]: #working_with_dom +[Integrating your code into Markdown]: #integrating_into_markdown +[extendMarkdown]: #extendmarkdown +[Config Settings]: #configsettings +[makeExtension]: #makeextension + -- cgit v1.2.3 From 58d96ea769135a090a70eb322e536c544ee29c81 Mon Sep 17 00:00:00 2001 From: Artem Yunusov Date: Fri, 8 Aug 2008 03:43:56 +0500 Subject: ElementTree version check added. --- markdown.py | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/markdown.py b/markdown.py index df85bab..a39a99c 100755 --- a/markdown.py +++ b/markdown.py @@ -58,10 +58,10 @@ def isstr(s): def importETree(): """ Imports best variant of ElementTree and returns 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,6 +78,19 @@ 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() -- cgit v1.2.3 From 3c525ce0feda3656c200130ae75837941815e49c Mon Sep 17 00:00:00 2001 From: Artem Yunusov Date: Sat, 9 Aug 2008 04:35:26 +0500 Subject: Comments cleanup, deleted Markdown._processTree function. --- markdown.py | 108 +++++++++++++++++++++++++++++++++--------------------------- 1 file changed, 60 insertions(+), 48 deletions(-) diff --git a/markdown.py b/markdown.py index a39a99c..f64fd16 100755 --- a/markdown.py +++ b/markdown.py @@ -53,11 +53,11 @@ 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+ @@ -96,6 +96,7 @@ def importETree(): etree = importETree() def indentETree(elem, level=0): + """ Indent ElementTree before serialization """ if level > 1: i = "\n" + (level-1) * " " @@ -187,7 +188,7 @@ def isBlockLevel (tag): def codepoint2name(code): - """ Returns entity definition by code, or code + """ Return entity definition by code, or code if there is no such entity definition""" entity = htmlentitydefs.codepoint2name.get(code) if entity: @@ -196,7 +197,7 @@ def codepoint2name(code): return "%s#%d;" % (AND_SUBSTITUTE, code) def handleAttributes(text, parent): - + """ Handale attributes, e.g {@id=123} """ def attributeCallback(match): parent.set(match.group(1), match.group(2)) @@ -626,7 +627,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): @@ -849,7 +850,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 @@ -870,7 +871,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 @@ -1059,20 +1060,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 @@ -1087,15 +1099,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 = {} """ @@ -1633,7 +1647,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: @@ -1641,7 +1655,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 @@ -1659,19 +1673,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] @@ -1703,7 +1715,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 @@ -1725,9 +1749,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. """ @@ -1796,18 +1823,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: @@ -1844,32 +1875,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 sections. + Create ElementTree, without applying inline paterns, + all data, include all data, that must be procesed wit inline + patterns in sections. Keyword arguments: @@ -1903,13 +1917,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: -- cgit v1.2.3 From fe8a2ff307ca2535e841e546cabfa31ce296d4ef Mon Sep 17 00:00:00 2001 From: Artem Yunusov Date: Sat, 9 Aug 2008 04:43:55 +0500 Subject: AND_SUBSTITUTE -> AMP_SUBSTITUTE --- markdown.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/markdown.py b/markdown.py index f64fd16..707b89c 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: @@ -93,7 +93,10 @@ def importETree(): 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 """ @@ -162,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 @@ -172,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', @@ -188,13 +192,15 @@ def isBlockLevel (tag): def codepoint2name(code): - """ Return 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} """ @@ -788,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 @@ -917,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() """ @@ -1203,7 +1209,7 @@ class Markdown: self.textPostprocessors = [# a footnote postprocessor will get # inserted here RAWHTMLTEXTPOSTPROCESSOR, - ANDSUBSTITUTETEXTPOSTPROCESSOR] + AMPSUBSTITUTETEXTPOSTPROCESSOR] self.prePatterns = [] -- cgit v1.2.3 From b941beb7973025d359f3e7839a5b99ea7f62c534 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Sat, 9 Aug 2008 22:28:45 -0400 Subject: Reorganized docs. Added an AUTHORS and INSTALL files. INSTALL is incomplete. --- CHANGE_LOG.txt | 152 --------------- MANIFEST | 15 -- README.html | 91 --------- README.txt | 98 ---------- docs/AUTHORS | 36 ++++ docs/CHANGE_LOG | 152 +++++++++++++++ docs/INSTALL | 59 ++++++ docs/MANIFEST | 19 ++ docs/README | 98 ++++++++++ docs/README.html | 91 +++++++++ docs/writing_extensions.txt | 298 ++++++++++++++++++++++++++++ odt2txt.py | 465 -------------------------------------------- scripts/odt2txt.py | 465 ++++++++++++++++++++++++++++++++++++++++++++ writing_extensions.txt | 298 ---------------------------- 14 files changed, 1218 insertions(+), 1119 deletions(-) delete mode 100644 CHANGE_LOG.txt delete mode 100644 MANIFEST delete mode 100644 README.html delete mode 100644 README.txt create mode 100644 docs/AUTHORS create mode 100644 docs/CHANGE_LOG create mode 100644 docs/INSTALL create mode 100644 docs/MANIFEST create mode 100644 docs/README create mode 100644 docs/README.html create mode 100644 docs/writing_extensions.txt delete mode 100644 odt2txt.py create mode 100644 scripts/odt2txt.py delete mode 100644 writing_extensions.txt diff --git a/CHANGE_LOG.txt b/CHANGE_LOG.txt deleted file mode 100644 index 200d5b7..0000000 --- a/CHANGE_LOG.txt +++ /dev/null @@ -1,152 +0,0 @@ -PYTHON MARKDOWN CHANGELOG -========================= - -August 2008: Updated included extensions to ElementTree. Added a -seperate commanline script. (v2.0-alpha) - -July 2008: Switched from home-grown NanoDOM to ElementTree and -various related bugs (thanks Artem Yunusov). - -June 2008: Fixed issues with nested inline patterns and cleaned -up testing framework (thanks Artem Yunusov). - -May 2008: Added a number of additional extensions to the -distribution and other minor changes. Moved repo to git from svn. - -Mar 2008: Refactored extension api to accept either an -extension name (as a string) or an instance of an extension -(Thanks David Wolever). Fixed various bugs and added doc strings. - -Feb 2008: Various bugfixes mostly regarding extensions. - -Feb 18, 2008: Version 1.7. - -Feb 13, 2008: A little code cleanup and better documentation -and inheritance for pre/post proccessors. - -Feb 9, 2008: Doublequotes no longer html escaped and rawhtml -honors , <@foo>, and <%foo> for those who run markdown on -template syntax. - -Dec 12, 2007: Updated docs. Removed encoding arg from Markdown -and markdown as per list discussion. Clean up in prep for 1.7. - -Nov 29, 2007: Added support for images inside links. Also fixed -a few bugs in the footnote extension. - -Nov 19, 2007: `message` now uses python's logging module. Also removed -limit imposed by recursion in _process_section(). You can now parse as -long of a document as your memory can handle. - -Nov 5, 2007: Moved safe_mode code to a textPostprocessor and added -escaping option. - -Nov 3, 2007: Fixed convert method to accept empty strings. - -Oct 30, 2007: Fixed BOM removal (thanks Malcolm Tredinnick). Fixed -infinite loop in bracket regex for inline links. - -Oct 11, 2007: LineBreaks is now an inlinePattern. Fixed HR in -blockquotes. Refactored _processSection method (see tracker #1793419). - -Oct 9, 2007: Added textPreprocessor (from 1.6b). - -Oct 8, 2008: Fixed Lazy Blockquote. Fixed code block on first line. -Fixed empty inline image link. - -Oct 7, 2007: Limit recursion on inlinePatterns. Added a 'safe' tag -to htmlStash. - -March 18, 2007: Fixed or merged a bunch of minor bugs, including -multi-line comments and markup inside links. (Tracker #s: 1683066, -1671153, 1661751, 1627935, 1544371, 1458139.) -> v. 1.6b - -Oct 10, 2006: Fixed a bug that caused some text to be lost after -comments. Added "safe mode" (user's html tags are removed). - -Sept 6, 2006: Added exception for PHP tags when handling html blocks. - -August 7, 2006: Incorporated Sergej Chodarev's patch to fix a problem -with ampersand normalization and html blocks. - -July 10, 2006: Switched to using optparse. Added proper support for -unicode. - -July 9, 2006: Fixed the