Skip to content

smarty

Smarty extension for Python-Markdown.

Adds conversion of ASCII dashes, quotes and ellipses to their HTML entity equivalents.

See https://pythonhosted.org/Markdown/extensions/smarty.html for documentation.

Author: 2013, Dmitry Shachnev mitya57@gmail.com

All changes Copyright 2013-2014 The Python Markdown Project

License: BSD

SmartyPants license:

Copyright (c) 2003 John Gruber http://daringfireball.net/ All rights reserved.

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

  • Neither the name "SmartyPants" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.

This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.

smartypants.py license:

smartypants.py is a derivative work of SmartyPants. Copyright (c) 2004, 2007 Chad Miller http://web.chad.org/

Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:

  • Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.

  • Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.

This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage.

Attributes

punctClass = '[!"#\\$\\%\'()*+,-.\\/:;<=>?\\@\\[\\\\\\]\\^_`{|}~]' module-attribute

endOfWordClass = '[\\s.,;:!?)]' module-attribute

closeClass = '[^\\ \\t\\r\\n\\[\\{\\(\\-\\u0002\\u0003]' module-attribute

openingQuotesBase = '(\\s|&nbsp;|--|–|—|&[mn]dash;|&#8211;|&#8212;)' module-attribute

substitutions = {'mdash': '&mdash;', 'ndash': '&ndash;', 'ellipsis': '&hellip;', 'left-angle-quote': '&laquo;', 'right-angle-quote': '&raquo;', 'left-single-quote': '&lsquo;', 'right-single-quote': '&rsquo;', 'left-double-quote': '&ldquo;', 'right-double-quote': '&rdquo;'} module-attribute

singleQuoteStartRe = "^'(?=%s\\B)" % punctClass module-attribute

doubleQuoteStartRe = '^"(?=%s\\B)' % punctClass module-attribute

doubleQuoteSetsRe = '"\'(?=\\w)' module-attribute

singleQuoteSetsRe = '\'"(?=\\w)' module-attribute

decadeAbbrRe = "(?<!\\w)'(?=\\d{2}s)" module-attribute

openingDoubleQuotesRegex = '%s"(?=\\w)' % openingQuotesBase module-attribute

closingDoubleQuotesRegex = '"(?=\\s)' module-attribute

closingDoubleQuotesRegex2 = '(?<=%s)"' % closeClass module-attribute

openingSingleQuotesRegex = "%s'(?=\\w)" % openingQuotesBase module-attribute

closingSingleQuotesRegex = "(?<=%s)'(?!\\s|s\\b|\\d)" % closeClass module-attribute

closingSingleQuotesRegex2 = "(?<=%s)'(\\s|s\\b)" % closeClass module-attribute

remainingSingleQuotesRegex = "'" module-attribute

remainingDoubleQuotesRegex = '"' module-attribute

HTML_STRICT_RE = HTML_RE + '(?!\\>)' module-attribute

Classes

SubstituteTextPattern(pattern, replace, markdown_instance)

Bases: HtmlPattern

Text pattern substitution handler.

Replaces matches with some text.

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def __init__(self, pattern, replace, markdown_instance):
    """Replaces matches with some text."""
    HtmlPattern.__init__(self, pattern)
    self.replace = replace
    self.markdown = markdown_instance

Attributes

pattern = pattern instance-attribute
compiled_re = re.compile('^(.*?)%s(.*)$' % pattern, re.DOTALL | re.UNICODE) instance-attribute
safe_mode = False instance-attribute
replace = replace instance-attribute
markdown = markdown_instance instance-attribute

Functions

getCompiledRegExp()

Return a compiled regular expression.

Source code in pyrevitlib/pyrevit/coreutils/markdown/inlinepatterns.py
def getCompiledRegExp(self):
    """Return a compiled regular expression."""
    return self.compiled_re
type()

Return class name, to define pattern type.

Source code in pyrevitlib/pyrevit/coreutils/markdown/inlinepatterns.py
def type(self):
    """Return class name, to define pattern type."""
    return self.__class__.__name__
unescape(text)

Return unescaped text given text with an inline placeholder.

Source code in pyrevitlib/pyrevit/coreutils/markdown/inlinepatterns.py
def unescape(self, text):
    """Return unescaped text given text with an inline placeholder."""
    try:
        stash = self.markdown.treeprocessors['inline'].stashed_nodes
    except KeyError:  # pragma: no cover
        return text

    def get_stash(m):
        id = m.group(1)
        value = stash.get(id)
        if value is not None:
            try:
                return self.markdown.serializer(value)
            except:
                return r'\%s' % value

    return util.INLINE_PLACEHOLDER_RE.sub(get_stash, text)
handleMatch(m)
Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def handleMatch(self, m):
    result = ''
    for part in self.replace:
        if isinstance(part, int):
            result += m.group(part)
        else:
            result += self.markdown.htmlStash.store(part, safe=True)
    return result

SmartyExtension(*args, **kwargs)

Bases: Extension

Smarty Extension.

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def __init__(self, *args, **kwargs):
    self.config = {
        'smart_quotes': [True, 'Educate quotes'],
        'smart_angled_quotes': [False, 'Educate angled quotes'],
        'smart_dashes': [True, 'Educate dashes'],
        'smart_ellipses': [True, 'Educate ellipses'],
        'substitutions': [{}, 'Overwrite default substitutions'],
    }
    super(SmartyExtension, self).__init__(*args, **kwargs)
    self.substitutions = dict(substitutions)
    self.substitutions.update(self.getConfig('substitutions', default={}))

Attributes

config = {'smart_quotes': [True, 'Educate quotes'], 'smart_angled_quotes': [False, 'Educate angled quotes'], 'smart_dashes': [True, 'Educate dashes'], 'smart_ellipses': [True, 'Educate ellipses'], 'substitutions': [{}, 'Overwrite default substitutions']} instance-attribute
substitutions = dict(substitutions) instance-attribute

Functions

getConfig(key, default='')

Return a setting for the given key or an empty string.

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/__init__.py
def getConfig(self, key, default=''):
    """Return a setting for the given key or an empty string."""
    if key in self.config:
        return self.config[key][0]
    else:
        return default
getConfigs()

Return all configs settings as a dict.

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/__init__.py
def getConfigs(self):
    """Return all configs settings as a dict."""
    return dict([(key, self.getConfig(key)) for key in self.config.keys()])
getConfigInfo()

Return all config descriptions as a list of tuples.

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/__init__.py
def getConfigInfo(self):
    """Return all config descriptions as a list of tuples."""
    return [(key, self.config[key][1]) for key in self.config.keys()]
setConfig(key, value)

Set a config setting for key with the given value.

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/__init__.py
def setConfig(self, key, value):
    """Set a config setting for `key` with the given `value`."""
    if isinstance(self.config[key][0], bool):
        value = parseBoolValue(value)
    if self.config[key][0] is None:
        value = parseBoolValue(value, preserve_none=True)
    self.config[key][0] = value
setConfigs(items)

Set multiple config settings given a dict or list of tuples.

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/__init__.py
def setConfigs(self, items):
    """Set multiple config settings given a dict or list of tuples."""
    if hasattr(items, 'items'):
        # it's a dict
        items = items.items()
    for key, value in items:
        self.setConfig(key, value)
educateDashes(md)
Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def educateDashes(self, md):
    emDashesPattern = SubstituteTextPattern(
        r'(?<!-)---(?!-)', (self.substitutions['mdash'],), md
    )
    enDashesPattern = SubstituteTextPattern(
        r'(?<!-)--(?!-)', (self.substitutions['ndash'],), md
    )
    self.inlinePatterns.add('smarty-em-dashes', emDashesPattern, '_begin')
    self.inlinePatterns.add(
        'smarty-en-dashes', enDashesPattern, '>smarty-em-dashes'
    )
educateEllipses(md)
Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def educateEllipses(self, md):
    ellipsesPattern = SubstituteTextPattern(
        r'(?<!\.)\.{3}(?!\.)', (self.substitutions['ellipsis'],), md
    )
    self.inlinePatterns.add('smarty-ellipses', ellipsesPattern, '_begin')
educateAngledQuotes(md)
Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def educateAngledQuotes(self, md):
    leftAngledQuotePattern = SubstituteTextPattern(
        r'\<\<', (self.substitutions['left-angle-quote'],), md
    )
    rightAngledQuotePattern = SubstituteTextPattern(
        r'\>\>', (self.substitutions['right-angle-quote'],), md
    )
    self.inlinePatterns.add(
        'smarty-left-angle-quotes', leftAngledQuotePattern, '_begin'
    )
    self.inlinePatterns.add(
        'smarty-right-angle-quotes',
        rightAngledQuotePattern,
        '>smarty-left-angle-quotes'
    )
educateQuotes(md)
Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def educateQuotes(self, md):
    lsquo = self.substitutions['left-single-quote']
    rsquo = self.substitutions['right-single-quote']
    ldquo = self.substitutions['left-double-quote']
    rdquo = self.substitutions['right-double-quote']
    patterns = (
        (singleQuoteStartRe, (rsquo,)),
        (doubleQuoteStartRe, (rdquo,)),
        (doubleQuoteSetsRe, (ldquo + lsquo,)),
        (singleQuoteSetsRe, (lsquo + ldquo,)),
        (decadeAbbrRe, (rsquo,)),
        (openingSingleQuotesRegex, (2, lsquo)),
        (closingSingleQuotesRegex, (rsquo,)),
        (closingSingleQuotesRegex2, (rsquo, 2)),
        (remainingSingleQuotesRegex, (lsquo,)),
        (openingDoubleQuotesRegex, (2, ldquo)),
        (closingDoubleQuotesRegex, (rdquo,)),
        (closingDoubleQuotesRegex2, (rdquo,)),
        (remainingDoubleQuotesRegex, (ldquo,))
    )
    self._addPatterns(md, patterns, 'quotes')
extendMarkdown(md, md_globals)
Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def extendMarkdown(self, md, md_globals):
    configs = self.getConfigs()
    self.inlinePatterns = OrderedDict()
    if configs['smart_ellipses']:
        self.educateEllipses(md)
    if configs['smart_quotes']:
        self.educateQuotes(md)
    if configs['smart_angled_quotes']:
        self.educateAngledQuotes(md)
        # Override HTML_RE from inlinepatterns.py so that it does not
        # process tags with duplicate closing quotes.
        md.inlinePatterns["html"] = HtmlPattern(HTML_STRICT_RE, md)
    if configs['smart_dashes']:
        self.educateDashes(md)
    inlineProcessor = InlineProcessor(md)
    inlineProcessor.inlinePatterns = self.inlinePatterns
    md.treeprocessors.add('smarty', inlineProcessor, '_end')
    md.ESCAPED_CHARS.extend(['"', "'"])

Functions

makeExtension(*args, **kwargs)

Source code in pyrevitlib/pyrevit/coreutils/markdown/extensions/smarty.py
def makeExtension(*args, **kwargs):
    return SmartyExtension(*args, **kwargs)