aboutsummaryrefslogtreecommitdiffstats
path: root/markdown.py
diff options
context:
space:
mode:
Diffstat (limited to 'markdown.py')
-rwxr-xr-xmarkdown.py183
1 files changed, 108 insertions, 75 deletions
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())