# -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals, print_function
from markdown.preprocessors import Preprocessor
from markdown.extensions import Extension
import os
import codecs
import unicodecsv
[docs]class IncludePreprocessor(Preprocessor):
def __init__(self, root_directory='', current_directory='', asset_directory='', extension_map=None, **kwargs):
self.asset_directory = asset_directory
self.current_directory = current_directory
self.root_directory = root_directory
self.extension_map = extension_map or {}
super(IncludePreprocessor, self).__init__(**kwargs)
[docs] def find_file_path(self, file_name):
"""
Find path to the file within self.root_directory
Start at `self.current_directory/self.asset_directory/file_name` looking for `file_name`. Recursively
strip one directory level off of `self.current_directory` to see if `self.asset_directory/file_name`
exists there up until the path matched `self.root_directory`
"""
current_dir = self.current_directory
asset_directory = os.path.join(current_dir, self.asset_directory)
file_path = os.path.join(asset_directory, file_name)
root_directory = os.path.join(self.root_directory, self.asset_directory)
while not os.path.isfile(file_path):
if asset_directory == root_directory:
return None
head, tail = os.path.split(os.path.split(asset_directory)[0])
asset_directory = os.path.join(head, self.asset_directory)
file_path = os.path.join(asset_directory, file_name)
return file_path
[docs] def handle_code(self, file_path, file_extension):
"""
Parse source code lines
"""
md_lines = []
code_type = self.extension_map.get(file_extension, file_extension)
included_file = codecs.open(file_path, 'r')
# TODO: We are forcing the backticks in here. Should this extension
# ensure that the fenced_code extension is loaded and raise an exception
# if it is not?
md_lines.append('``` %s' % code_type)
md_lines[-1].strip()
for included_line in included_file:
md_lines.append(included_line.rstrip())
md_lines.append('```')
included_file.close()
return md_lines
[docs] def build_csv_table(self, file_path):
table_html = ['<table>']
with open(file_path, 'rb') as csvfile:
reader = unicodecsv.reader(csvfile)
for index, row in enumerate(reader):
table_html.append('<tr>')
for column in row:
table_html.append('<th>' if index == 0 else '<td>')
table_html.append(column)
table_html.append('</th>' if index == 0 else '</td>')
table_html.append('</tr>')
table_html.append('</table>')
return table_html
[docs] def handle_csv(self, file_path):
"""
Parse csv file and return as an html table
"""
return self.markdown.htmlStash.store(''.join(self.build_csv_table(file_path)), safe=True)
[docs] def run(self, lines):
md_lines = []
for line in lines:
if line.startswith('+++'):
file_name = line[3:].strip()
file_path = self.find_file_path(file_name)
if not file_path:
break
file_extension = os.path.splitext(file_name)[1]
if file_extension == '.csv':
md_lines.append(self.handle_csv(file_path))
else:
md_lines.extend(self.handle_code(file_path, file_extension))
else:
md_lines.append(line)
return md_lines
[docs]class IncludeExtension(Extension):
def __init__(self, **kwargs):
self.config = {
'asset_directory': ['', ('Name of the dirirectory for the assets to include via this extension within'
' current_directory and root_directory')],
'current_directory': ['', 'Root directory to stop searching for assets'],
'root_directory': ['', 'Root directory to stop searching for assets'],
'extension_map': [{}, ('Optional dictionary mapping one file extension to another'
' to override the default assigned by markdown.extensions.fenced_code')]
}
super(IncludeExtension, self).__init__(**kwargs)
[docs] def extendMarkdown(self, md, md_globals):
""" Add IncludePreprocessor to the Markdown instance. """
md.registerExtension(self)
asset_directory = self.getConfig('asset_directory')
root_directory = self.getConfig('root_directory')
current_directory = self.getConfig('current_directory')
extension_map = self.getConfig('extension_map')
md.preprocessors.add(
'include',
IncludePreprocessor(root_directory=root_directory,
current_directory=current_directory,
asset_directory=asset_directory,
extension_map=extension_map,
markdown_instance=md),
">normalize_whitespace")
[docs]def makeExtension(*args, **kwargs):
return IncludeExtension(*args, **kwargs)