From c0c88a2777a1641d4312b6aafe454f466fe104b2 Mon Sep 17 00:00:00 2001 From: Waylan Limberg Date: Mon, 12 May 2008 00:52:14 -0400 Subject: Added Meta-Data and HeaderId extensions. Updated setup.py to include all packages extensions. --- mdx_headerid.py | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ mdx_meta.py | 81 ++++++++++++++++++++++++++ setup.py | 12 +++- 3 files changed, 265 insertions(+), 1 deletion(-) create mode 100644 mdx_headerid.py create mode 100644 mdx_meta.py diff --git a/mdx_headerid.py b/mdx_headerid.py new file mode 100644 index 0000000..2e658cd --- /dev/null +++ b/mdx_headerid.py @@ -0,0 +1,173 @@ +#!/usr/bin/python + +""" +HeaderID Extension for Python-Markdown +====================================== + +Adds ability to set HTML IDs for headers. + +Basic usage: + + >>> import markdown + >>> text = "# Some Header # {#some_id}" + >>> md = markdown.markdown(text, ['headerid']) + >>> md + u'

Some Header

' + +All header IDs are unique: + + >>> text = ''' + ... #Header + ... #Another Header {#header} + ... #Third Header {#header}''' + >>> md = markdown.markdown(text, ['headerid']) + >>> md + u'

Header

\\n\\n

Another Header

\\n\\n

Third Header

' + +To fit within a html template's hierarchy, set the header base level: + + >>> text = ''' + ... #Some Header + ... ## Next Level''' + >>> md = markdown.markdown(text, ['headerid(level=3)']) + >>> md + u'

Some Header

\\n\\n

Next Level

' + +Turn off auto generated IDs: + + >>> text = ''' + ... # Some Header + ... # Header with ID # { #foo }''' + >>> md = markdown.markdown(text, ['headerid(forceid=False)']) + >>> md + u'

Some Header

\\n\\n

Header with ID

' + +Use with MetaData extension: + + >>> text = '''header_level: 2 + ... header_forceid: Off + ... + ... # A Header''' + >>> md = markdown.markdown(text, ['headerid', 'meta']) + >>> md + u'

A Header

' + +By [Waylan Limberg](http://achinghead.com/). + +Project website: http://achinghead.com/markdown-headerid/ +Contact: waylan [at] gmail [dot] com + +License: [BSD](http://www.opensource.org/licenses/bsd-license.php) + +Version: 0.1 (May 2, 2008) + +Dependencies: +* [Python 2.3+](http://python.org) +* [Markdown 1.7+](http://www.freewisdom.org/projects/python-markdown/) + +""" + +import markdown +import re +from string import ascii_lowercase, digits, punctuation + +ID_CHARS = ascii_lowercase + digits + '-_' + +HEADER_RE = re.compile(r'''^(\#{1,6}) # group(1) = string of hashes + ( [^{^#]*) # group(2) = Header text + [\#]* # optional closing hashes (not counted) + (?:[ \t]*\{[ \t]*\#([-_:a-zA-Z0-9]+)[ \t]*\})? # group(3) = id attr''', + re.VERBOSE) + +IDCOUNT_RE = re.compile(r'^(.*)_([0-9]+)$') + +class HeaderIdExtension (markdown.Extension) : + def __init__(self, configs): + # set defaults + self.config = { + 'level' : ['1', 'Base level for headers.'], + 'forceid' : ['True', 'Force all headers to have an id.'] + } + + for key, value in configs: + self.setConfig(key, value) + + + def extendMarkdown(self, md, md_globals) : + + md.IDs = [] + + def _processHeaderId(parent_elem, paragraph) : + ''' + Overrides _processHeader of Markdown() and + adds an 'id' to the header. + ''' + m = HEADER_RE.match(paragraph[0]) + if m : + start_level, force_id = _get_meta() + level = len(m.group(1)) + start_level + if level > 6: level = 6 + h = md.doc.createElement("h%d" % level) + parent_elem.appendChild(h) + for item in md._handleInline(m.group(2).strip()) : + h.appendChild(item) + if m.group(3) : + h.setAttribute('id', _unique_id(m.group(3))) + elif force_id: + h.setAttribute('id', _create_id(m.group(2).strip())) + else : + message(CRITICAL, "We've got a problem header!") + + md._processHeader = _processHeaderId + + def _get_meta(): + ''' Return meta data suported by this ext as a tuple ''' + level = int(self.config['level'][0]) - 1 + force = _str2bool(self.config['forceid'][0]) + if hasattr(md, 'Meta'): + if md.Meta.has_key('header_level'): + level = int(md.Meta['header_level'][0]) - 1 + if md.Meta.has_key('header_forceid'): + force = _str2bool(md.Meta['header_forceid'][0]) + return level, force + + def _str2bool(s, default=False): + ''' Convert a string to a booleen value. ''' + s = str(s) + if s.lower() in ['0', 'f', 'false', 'off', 'no', 'n']: + return False + elif s.lower() in ['1', 't', 'true', 'on', 'yes', 'y']: + return True + return default + + def _unique_id(id): + ''' Ensure ID is unique. Append '_1', '_2'... if not ''' + while id in md.IDs: + m = IDCOUNT_RE.match(id) + if m: + id = '%s_%d'% (m.group(1), int(m.group(2))+1) + else: + id = '%s_%d'% (id, 1) + md.IDs.append(id) + return id + + + def _create_id(header): + ''' Return ID from Header text. ''' + h = '' + for c in header.lower().replace(' ', '_'): + if c in ID_CHARS: + h += c + elif c not in punctuation: + h += '+' + return _unique_id(h) + + + +def makeExtension(configs=None) : + return HeaderIdExtension(configs=configs) + +if __name__ == "__main__": + import doctest + doctest.testmod() + diff --git a/mdx_meta.py b/mdx_meta.py new file mode 100644 index 0000000..30dea8a --- /dev/null +++ b/mdx_meta.py @@ -0,0 +1,81 @@ +#!usr/bin/python + +''' +Meta Data Extension for Python-Markdown +========================================== + +This extension adds Meta Data handling to markdown. + + >>> import markdown + >>> text = """Title: A Test Doc. + ... Author: Waylan Limberg + ... John Doe + ... Blank_Data: + ... + ... The body. This is paragraph one. + ... """ + >>> md = markdown.Markdown(text, ['meta']) + >>> md.convert() + u'

The body. This is paragraph one.\\n

' + >>> md.Meta + {u'blank_data': [u''], u'author': [u'Waylan Limberg', u'John Doe'], u'title': [u'A Test Doc.']} + +Make sure text without Meta Data still works (markdown < 1.6b returns a

). + + >>> text = ' Some Code - not extra lines of meta data.' + >>> md = markdown.Markdown(text, ['meta']) + >>> md.convert() + u'

Some Code - not extra lines of meta data.\\n
' + >>> md.Meta + {} + +''' + +import markdown, re + +# Global Vars +META_RE = re.compile(r'^[ ]{0,3}(?P[A-Za-z0-9_-]+):\s*(?P.*)') +META_MORE_RE = re.compile(r'^[ ]{4,}(?P.*)') + +class MetaExtension (markdown.Extension) : + def __init__(self, configs): + pass + + def extendMarkdown(self, md, md_globals) : + self.md = md + + # Insert meta preprocessor first + META_PREPROCESSOR = MetaPreprocessor() + META_PREPROCESSOR.md = md + md.preprocessors.insert(0, META_PREPROCESSOR) + +class MetaPreprocessor(markdown.Preprocessor) : + def run(self, lines) : + meta = {} + key = None + while 1: + line = lines.pop(0) + if line.strip() == '': + break # blank line - done + m1 = META_RE.match(line) + if m1: + key = m1.group('key').lower().strip() + meta[key] = [m1.group('value').strip()] + else: + m2 = META_MORE_RE.match(line) + if m2 and key: + # Add another line to existing key + meta[key].append(m2.group('value').strip()) + else: + lines.insert(0, line) + break # no meta data - done + self.md.Meta = meta + return lines + + +def makeExtension(configs=None) : + return MetaExtension(configs=configs) + +if __name__ == "__main__": + import doctest + doctest.testmod() diff --git a/setup.py b/setup.py index de36dfc..5596eca 100644 --- a/setup.py +++ b/setup.py @@ -10,5 +10,15 @@ 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_footnotes", "mdx_rss"], + py_modules = ["markdown", + "mdx_codehilite", + "mdx_fenced_code", + "mdx_footnotes", + "mdx_headerid", + "mdx_imagelinks", + "mdx_meta", + "mdx_rss", + "mdx_tables", + "mdx_wikilink", + ], ) -- cgit v1.2.3