Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Updates for Sublime Text 3 compatibility #19

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions MarkdownBuild.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import sublime
import sublime_plugin
import markdown_python
from .markdown_python import markdown
import os
import tempfile
import webbrowser
Expand All @@ -27,7 +27,7 @@ def run(self):
if not file_name:
return
contents = view.substr(sublime.Region(0, view.size()))
md = markdown_python.markdown(contents)
md = markdown(contents)
html = '<html><meta charset="' + charset + '">'
if use_css:
css = os.path.join(sublime.packages_path(), 'MarkdownBuild', 'markdown.css')
Expand Down
145 changes: 80 additions & 65 deletions markdown_python/__init__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
import markdown
html = markdown.markdown(your_text_string)

See <http://www.freewisdom.org/projects/python-markdown/> for more
See <http://packages.python.org/Markdown/> for more
information and instructions on how to extend the functionality of
Python Markdown. Read that before you try modifying this file.

Expand All @@ -22,48 +22,48 @@

Contact: [email protected]

Copyright 2007-2012 The Python Markdown Project (v. 1.7 and later)
Copyright 2007-2013 The Python Markdown Project (v. 1.7 and later)
Copyright 200? Django Software Foundation (OrderedDict implementation)
Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
Copyright 2004 Manfred Stienstra (the original version)

License: BSD (see LICENSE for details).
"""

version = "2.1.1"
version_info = (2,1,1, "final")

from __future__ import absolute_import
from __future__ import unicode_literals
from .__version__ import version, version_info
import re
import codecs
import sys
import logging
import util
from preprocessors import build_preprocessors
from blockprocessors import build_block_parser
from treeprocessors import build_treeprocessors
from inlinepatterns import build_inlinepatterns
from postprocessors import build_postprocessors
from extensions import Extension
from serializers import to_html_string, to_xhtml_string
from . import util
from .preprocessors import build_preprocessors
from .blockprocessors import build_block_parser
from .treeprocessors import build_treeprocessors
from .inlinepatterns import build_inlinepatterns
from .postprocessors import build_postprocessors
from .extensions import Extension
from .serializers import to_html_string, to_xhtml_string

__all__ = ['Markdown', 'markdown', 'markdownFromFile']

logger = logging.getLogger('MARKDOWN')


class Markdown:
class Markdown(object):
"""Convert Markdown to HTML."""

doc_tag = "div" # Element used to wrap document - later removed

option_defaults = {
'html_replacement_text' : '[HTML_REMOVED]',
'tab_length' : 4,
'enable_attributes' : True,
'smart_emphasis' : True,
'lazy_ol' : True,
}

output_formats = {
'html' : to_html_string,
'html4' : to_html_string,
Expand All @@ -73,9 +73,9 @@ class Markdown:
'xhtml5': to_xhtml_string,
}

ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']',
ESCAPED_CHARS = ['\\', '`', '*', '_', '{', '}', '[', ']',
'(', ')', '>', '#', '+', '-', '.', '!']

def __init__(self, *args, **kwargs):
"""
Creates a new Markdown instance.
Expand All @@ -86,7 +86,7 @@ def __init__(self, *args, **kwargs):
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
as-is.
* extension-configs: Configuration settingis for extensions.
* extension_configs: Configuration settingis for extensions.
* output_format: Format of output. Supported formats are:
* "xhtml1": Outputs XHTML 1.x. Default.
* "xhtml5": Outputs XHTML style tags of HTML 5
Expand All @@ -106,11 +106,11 @@ def __init__(self, *args, **kwargs):

"""

# For backward compatability, loop through old positional args
# For backward compatibility, loop through old positional args
pos = ['extensions', 'extension_configs', 'safe_mode', 'output_format']
c = 0
for arg in args:
if not kwargs.has_key(pos[c]):
if pos[c] not in kwargs:
kwargs[pos[c]] = arg
c += 1
if c == len(pos):
Expand All @@ -119,9 +119,13 @@ def __init__(self, *args, **kwargs):

# Loop through kwargs and assign defaults
for option, default in self.option_defaults.items():
setattr(self, option, kwargs.get(option, default))
setattr(self, option, kwargs.get(option, default))

self.safeMode = kwargs.get('safe_mode', False)
if self.safeMode and 'enable_attributes' not in kwargs:
# Disable attributes in safeMode when not explicitly set
self.enable_attributes = False

self.registeredExtensions = []
self.docType = ""
self.stripTopLevelTags = True
Expand All @@ -130,15 +134,15 @@ def __init__(self, *args, **kwargs):

self.references = {}
self.htmlStash = util.HtmlStash()
self.set_output_format(kwargs.get('output_format', 'xhtml1'))
self.registerExtensions(extensions=kwargs.get('extensions', []),
configs=kwargs.get('extension_configs', {}))
self.set_output_format(kwargs.get('output_format', 'xhtml1'))
self.reset()

def build_parser(self):
""" Build the parser from the various parts. """
self.preprocessors = build_preprocessors(self)
self.parser = build_block_parser(self)
self.parser = build_block_parser(self)
self.inlinePatterns = build_inlinepatterns(self)
self.treeprocessors = build_treeprocessors(self)
self.postprocessors = build_postprocessors(self)
Expand All @@ -156,13 +160,13 @@ def registerExtensions(self, extensions, configs):

"""
for ext in extensions:
if isinstance(ext, basestring):
if isinstance(ext, util.string_type):
ext = self.build_extension(ext, configs.get(ext, []))
if isinstance(ext, Extension):
# might raise NotImplementedError, but that's the extension author's problem
ext.extendMarkdown(self, globals())
else:
raise ValueError('Extension "%s.%s" must be of type: "markdown.Extension".' \
elif ext is not None:
raise TypeError(
'Extension "%s.%s" must be of type: "markdown.Extension"'
% (ext.__class__.__module__, ext.__class__.__name__))

return self
Expand Down Expand Up @@ -196,20 +200,23 @@ def build_extension(self, ext_name, configs = []):
module_name_old_style = '_'.join(['mdx', ext_name])
try: # Old style (mdx_<extension>)
module = __import__(module_name_old_style)
except ImportError:
logger.warn("Failed loading extension '%s' from '%s' or '%s'"
% (ext_name, module_name, module_name_old_style))
# Return None so we don't try to initiate none-existant extension
return None
except ImportError as e:
message = "Failed loading extension '%s' from '%s' or '%s'" \
% (ext_name, module_name, module_name_old_style)
e.args = (message,) + e.args[1:]
raise

# If the module is loaded successfully, we expect it to define a
# function called makeExtension()
try:
return module.makeExtension(configs.items())
except AttributeError, e:
logger.warn("Failed to initiate extension '%s': %s" % (ext_name, e))
return None

except AttributeError as e:
message = e.args[0]
message = "Failed to initiate extension " \
"'%s': %s" % (ext_name, message)
e.args = (message,) + e.args[1:]
raise

def registerExtension(self, extension):
""" This gets called by the extension """
self.registeredExtensions.append(extension)
Expand All @@ -230,11 +237,17 @@ def reset(self):

def set_output_format(self, format):
""" Set the output format for the class instance. """
self.output_format = format.lower()
try:
self.serializer = self.output_formats[format.lower()]
except KeyError:
raise KeyError('Invalid Output Format: "%s". Use one of %s.' \
% (format, self.output_formats.keys()))
self.serializer = self.output_formats[self.output_format]
except KeyError as e:
valid_formats = list(self.output_formats.keys())
valid_formats.sort()
message = 'Invalid Output Format: "%s". Use one of %s.' \
% (self.output_format,
'"' + '", "'.join(valid_formats) + '"')
e.args = (message,) + e.args[1:]
raise
return self

def convert(self, source):
Expand All @@ -250,31 +263,26 @@ def convert(self, source):
1. A bunch of "preprocessors" munge the input text.
2. BlockParser() parses the high-level structural elements of the
pre-processed text into an ElementTree.
3. A bunch of "treeprocessors" are run against the ElementTree. One
such treeprocessor runs InlinePatterns against the ElementTree,
3. A bunch of "treeprocessors" are run against the ElementTree. One
such treeprocessor runs InlinePatterns against the ElementTree,
detecting inline markup.
4. Some post-processors are run against the text after the ElementTree
4. Some post-processors are run against the text after the ElementTree
has been serialized into text.
5. The output is written to a string.

"""

# Fixup the source text
if not source.strip():
return u"" # a blank unicode string
return '' # a blank unicode string

try:
source = unicode(source)
except UnicodeDecodeError, e:
source = util.text_type(source)
except UnicodeDecodeError as e:
# Customise error message while maintaining original trackback
e.reason += '. -- Note: Markdown only accepts unicode input!'
raise

source = source.replace(util.STX, "").replace(util.ETX, "")
source = source.replace("\r\n", "\n").replace("\r", "\n") + "\n\n"
source = re.sub(r'\n\s+\n', '\n\n', source)
source = source.expandtabs(self.tab_length)

# Split into lines and run the line preprocessors.
self.lines = source.split("\n")
for prep in self.preprocessors.values():
Expand Down Expand Up @@ -335,15 +343,15 @@ def convertFile(self, input=None, output=None, encoding=None):

# Read the source
if input:
if isinstance(input, str):
if isinstance(input, util.string_type):
input_file = codecs.open(input, mode="r", encoding=encoding)
else:
input_file = codecs.getreader(encoding)(input)
text = input_file.read()
input_file.close()
else:
text = sys.stdin.read()
if not isinstance(text, unicode):
if not isinstance(text, util.text_type):
text = text.decode(encoding)

text = text.lstrip('\ufeff') # remove the byte-order mark
Expand All @@ -353,9 +361,9 @@ def convertFile(self, input=None, output=None, encoding=None):

# Write to file or stdout
if output:
if isinstance(output, str):
output_file = codecs.open(output, "w",
encoding=encoding,
if isinstance(output, util.string_type):
output_file = codecs.open(output, "w",
encoding=encoding,
errors="xmlcharrefreplace")
output_file.write(html)
output_file.close()
Expand All @@ -365,7 +373,14 @@ def convertFile(self, input=None, output=None, encoding=None):
output_file.write(html)
# Don't close here. User may want to write more.
else:
sys.stdout.write(html)
# Encode manually and write bytes to stdout.
html = html.encode(encoding, "xmlcharrefreplace")
try:
# Write bytes directly to buffer (Python 3).
sys.stdout.buffer.write(html)
except AttributeError:
# Probably Python 2, which works with bytes by default.
sys.stdout.write(html)

return self

Expand Down Expand Up @@ -399,30 +414,30 @@ def markdown(text, *args, **kwargs):

def markdownFromFile(*args, **kwargs):
"""Read markdown code from a file and write it to a file or a stream.

This is a shortcut function which initializes an instance of Markdown,
and calls the convertFile method rather than convert.

Keyword arguments:

* input: a file name or readable object.
* output: a file name or writable object.
* encoding: Encoding of input and output.
* Any arguments accepted by the Markdown class.

"""
# For backward compatibility loop through positional args
pos = ['input', 'output', 'extensions', 'encoding']
c = 0
for arg in args:
if not kwargs.has_key(pos[c]):
if pos[c] not in kwargs:
kwargs[pos[c]] = arg
c += 1
if c == len(pos):
break

md = Markdown(**kwargs)
md.convertFile(kwargs.get('input', None),
md.convertFile(kwargs.get('input', None),
kwargs.get('output', None),
kwargs.get('encoding', None))

4 changes: 2 additions & 2 deletions markdown_python/__main__.py
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,11 @@ def parse_options():
usage = """%prog [options] [INPUTFILE]
(STDIN is assumed if no INPUTFILE is given)"""
desc = "A Python implementation of John Gruber's Markdown. " \
"http://www.freewisdom.org/projects/python-markdown/"
"http://packages.python.org/Markdown/"
ver = "%%prog %s" % markdown.version

parser = optparse.OptionParser(usage=usage, description=desc, version=ver)
parser.add_option("-f", "--file", dest="filename", default=sys.stdout,
parser.add_option("-f", "--file", dest="filename", default=None,
help="Write output to OUTPUT_FILE. Defaults to STDOUT.",
metavar="OUTPUT_FILE")
parser.add_option("-e", "--encoding", dest="encoding",
Expand Down
28 changes: 28 additions & 0 deletions markdown_python/__version__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#
# markdown/__version__.py
#
# version_info should conform to PEP 386
# (major, minor, micro, alpha/beta/rc/final, #)
# (1, 1, 2, 'alpha', 0) => "1.1.2.dev"
# (1, 2, 0, 'beta', 2) => "1.2b2"
version_info = (2, 3, 1, 'final', 0)

def _get_version():
" Returns a PEP 386-compliant version number from version_info. "
assert len(version_info) == 5
assert version_info[3] in ('alpha', 'beta', 'rc', 'final')

parts = 2 if version_info[2] == 0 else 3
main = '.'.join(map(str, version_info[:parts]))

sub = ''
if version_info[3] == 'alpha' and version_info[4] == 0:
# TODO: maybe append some sort of git info here??
sub = '.dev'
elif version_info[3] != 'final':
mapping = {'alpha': 'a', 'beta': 'b', 'rc': 'c'}
sub = mapping[version_info[3]] + str(version_info[4])

return str(main + sub)

version = _get_version()
Loading