mirror of
https://github.com/mkdocs/mkdocs.git
synced 2026-04-12 07:06:28 +07:00
refactoring: moving markdown parsing to utils
This commit is contained in:
@@ -2,59 +2,22 @@
|
||||
from __future__ import print_function
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
import mkdocs
|
||||
from mkdocs import nav, toc, utils
|
||||
from mkdocs.compat import urljoin
|
||||
from mkdocs.relative_path_ext import RelativePathExtension
|
||||
import jinja2
|
||||
import json
|
||||
import markdown
|
||||
import os
|
||||
import logging
|
||||
from io import open
|
||||
|
||||
from jinja2.exceptions import TemplateNotFound
|
||||
import jinja2
|
||||
|
||||
import mkdocs
|
||||
from mkdocs import nav, utils
|
||||
from mkdocs.compat import urljoin
|
||||
from mkdocs.utils import convert_markdown
|
||||
|
||||
log = logging.getLogger('mkdocs')
|
||||
|
||||
|
||||
def convert_markdown(markdown_source, site_navigation=None, extensions=(), strict=False):
|
||||
"""
|
||||
Convert the Markdown source file to HTML content, and additionally
|
||||
return the parsed table of contents, and a dictionary of any metadata
|
||||
that was specified in the Markdown file.
|
||||
|
||||
`extensions` is an optional sequence of Python Markdown extensions to add
|
||||
to the default set.
|
||||
"""
|
||||
|
||||
# Generate the HTML from the markdown source
|
||||
if isinstance(extensions, dict):
|
||||
user_extensions = list(extensions.keys())
|
||||
extension_configs = dict([(k, v) for k, v in extensions.items() if isinstance(v, dict)])
|
||||
else:
|
||||
user_extensions = list(extensions)
|
||||
extension_configs = {}
|
||||
builtin_extensions = ['meta', 'toc', 'tables', 'fenced_code']
|
||||
mkdocs_extensions = [RelativePathExtension(site_navigation, strict), ]
|
||||
extensions = set(builtin_extensions + mkdocs_extensions + user_extensions)
|
||||
md = markdown.Markdown(
|
||||
extensions=extensions,
|
||||
extension_configs=extension_configs
|
||||
)
|
||||
html_content = md.convert(markdown_source)
|
||||
|
||||
# On completely blank markdown files, no Meta or tox properties are added
|
||||
# to the generated document.
|
||||
meta = getattr(md, 'Meta', {})
|
||||
toc_html = getattr(md, 'toc', '')
|
||||
|
||||
# Post process the generated table of contents into a data structure
|
||||
table_of_contents = toc.TableOfContents(toc_html)
|
||||
|
||||
return (html_content, table_of_contents, meta)
|
||||
|
||||
|
||||
def get_global_context(nav, config):
|
||||
"""
|
||||
Given the SiteNavigation and config, generate the context which is relevant
|
||||
|
||||
@@ -17,12 +17,13 @@ log = logging.getLogger(__name__)
|
||||
def file_to_tile(filename):
|
||||
"""
|
||||
Automatically generate a default title, given a filename.
|
||||
|
||||
The method parses the file to check for a title, uses the filename
|
||||
as a title otherwise.
|
||||
"""
|
||||
if utils.is_homepage(filename):
|
||||
return 'Home'
|
||||
|
||||
#todo check if there's a header in the doc first
|
||||
|
||||
title = os.path.splitext(filename)[0]
|
||||
title = title.replace('-', ' ').replace('_', ' ')
|
||||
# Capitalize if the filename was all lowercase, otherwise leave it as-is.
|
||||
|
||||
@@ -10,12 +10,13 @@ from mkdocs import build, nav, config
|
||||
from mkdocs.compat import zip
|
||||
from mkdocs.exceptions import MarkdownNotFound
|
||||
from mkdocs.tests.base import dedent
|
||||
from mkdocs.utils import convert_markdown
|
||||
|
||||
|
||||
class BuildTests(unittest.TestCase):
|
||||
|
||||
def test_empty_document(self):
|
||||
html, toc, meta = build.convert_markdown("")
|
||||
html, toc, meta = convert_markdown("")
|
||||
|
||||
self.assertEqual(html, '')
|
||||
self.assertEqual(len(list(toc)), 0)
|
||||
@@ -26,7 +27,7 @@ class BuildTests(unittest.TestCase):
|
||||
Ensure that basic Markdown -> HTML and TOC works.
|
||||
"""
|
||||
|
||||
html, toc, meta = build.convert_markdown(dedent("""
|
||||
html, toc, meta = convert_markdown(dedent("""
|
||||
page_title: custom title
|
||||
|
||||
# Heading 1
|
||||
@@ -59,25 +60,25 @@ class BuildTests(unittest.TestCase):
|
||||
def test_convert_internal_link(self):
|
||||
md_text = 'An [internal link](internal.md) to another document.'
|
||||
expected = '<p>An <a href="internal/">internal link</a> to another document.</p>'
|
||||
html, toc, meta = build.convert_markdown(md_text)
|
||||
html, toc, meta = convert_markdown(md_text)
|
||||
self.assertEqual(html.strip(), expected.strip())
|
||||
|
||||
def test_convert_multiple_internal_links(self):
|
||||
md_text = '[First link](first.md) [second link](second.md).'
|
||||
expected = '<p><a href="first/">First link</a> <a href="second/">second link</a>.</p>'
|
||||
html, toc, meta = build.convert_markdown(md_text)
|
||||
html, toc, meta = convert_markdown(md_text)
|
||||
self.assertEqual(html.strip(), expected.strip())
|
||||
|
||||
def test_convert_internal_link_differing_directory(self):
|
||||
md_text = 'An [internal link](../internal.md) to another document.'
|
||||
expected = '<p>An <a href="../internal/">internal link</a> to another document.</p>'
|
||||
html, toc, meta = build.convert_markdown(md_text)
|
||||
html, toc, meta = convert_markdown(md_text)
|
||||
self.assertEqual(html.strip(), expected.strip())
|
||||
|
||||
def test_convert_internal_link_with_anchor(self):
|
||||
md_text = 'An [internal link](internal.md#section1.1) to another document.'
|
||||
expected = '<p>An <a href="internal/#section1.1">internal link</a> to another document.</p>'
|
||||
html, toc, meta = build.convert_markdown(md_text)
|
||||
html, toc, meta = convert_markdown(md_text)
|
||||
self.assertEqual(html.strip(), expected.strip())
|
||||
|
||||
def test_convert_internal_media(self):
|
||||
@@ -100,7 +101,7 @@ class BuildTests(unittest.TestCase):
|
||||
|
||||
for (page, expected) in zip(site_navigation.walk_pages(), expected_results):
|
||||
md_text = ''
|
||||
html, _, _ = build.convert_markdown(md_text, site_navigation=site_navigation)
|
||||
html, _, _ = convert_markdown(md_text, site_navigation=site_navigation)
|
||||
self.assertEqual(html, template % expected)
|
||||
|
||||
def test_convert_internal_asbolute_media(self):
|
||||
@@ -123,7 +124,7 @@ class BuildTests(unittest.TestCase):
|
||||
|
||||
for (page, expected) in zip(site_navigation.walk_pages(), expected_results):
|
||||
md_text = ''
|
||||
html, _, _ = build.convert_markdown(md_text, site_navigation=site_navigation)
|
||||
html, _, _ = convert_markdown(md_text, site_navigation=site_navigation)
|
||||
self.assertEqual(html, template % expected)
|
||||
|
||||
def test_dont_convert_code_block_urls(self):
|
||||
@@ -143,7 +144,7 @@ class BuildTests(unittest.TestCase):
|
||||
|
||||
for page in site_navigation.walk_pages():
|
||||
markdown = 'An HTML Anchor::\n\n <a href="index.md">My example link</a>\n'
|
||||
html, _, _ = build.convert_markdown(markdown, site_navigation=site_navigation)
|
||||
html, _, _ = convert_markdown(markdown, site_navigation=site_navigation)
|
||||
self.assertEqual(dedent(html), expected)
|
||||
|
||||
def test_anchor_only_link(self):
|
||||
@@ -158,13 +159,13 @@ class BuildTests(unittest.TestCase):
|
||||
|
||||
for page in site_navigation.walk_pages():
|
||||
markdown = '[test](#test)'
|
||||
html, _, _ = build.convert_markdown(markdown, site_navigation=site_navigation)
|
||||
html, _, _ = convert_markdown(markdown, site_navigation=site_navigation)
|
||||
self.assertEqual(html, '<p><a href="#test">test</a></p>')
|
||||
|
||||
def test_ignore_external_link(self):
|
||||
md_text = 'An [external link](http://example.com/external.md).'
|
||||
expected = '<p>An <a href="http://example.com/external.md">external link</a>.</p>'
|
||||
html, toc, meta = build.convert_markdown(md_text)
|
||||
html, toc, meta = convert_markdown(md_text)
|
||||
self.assertEqual(html.strip(), expected.strip())
|
||||
|
||||
def test_not_use_directory_urls(self):
|
||||
@@ -174,7 +175,7 @@ class BuildTests(unittest.TestCase):
|
||||
('internal.md',)
|
||||
]
|
||||
site_navigation = nav.SiteNavigation(pages, use_directory_urls=False)
|
||||
html, toc, meta = build.convert_markdown(md_text, site_navigation=site_navigation)
|
||||
html, toc, meta = convert_markdown(md_text, site_navigation=site_navigation)
|
||||
self.assertEqual(html.strip(), expected.strip())
|
||||
|
||||
def test_markdown_table_extension(self):
|
||||
@@ -182,7 +183,7 @@ class BuildTests(unittest.TestCase):
|
||||
Ensure that the table extension is supported.
|
||||
"""
|
||||
|
||||
html, toc, meta = build.convert_markdown(dedent("""
|
||||
html, toc, meta = convert_markdown(dedent("""
|
||||
First Header | Second Header
|
||||
-------------- | --------------
|
||||
Content Cell 1 | Content Cell 2
|
||||
@@ -217,7 +218,7 @@ class BuildTests(unittest.TestCase):
|
||||
Ensure that the fenced code extension is supported.
|
||||
"""
|
||||
|
||||
html, toc, meta = build.convert_markdown(dedent("""
|
||||
html, toc, meta = convert_markdown(dedent("""
|
||||
```
|
||||
print 'foo'
|
||||
```
|
||||
@@ -238,12 +239,12 @@ class BuildTests(unittest.TestCase):
|
||||
|
||||
# Check that the plugin is not active when not requested.
|
||||
expected_without_smartstrong = "<p>foo<strong>bar</strong>baz</p>"
|
||||
html_base, _, _ = build.convert_markdown(md_input)
|
||||
html_base, _, _ = convert_markdown(md_input)
|
||||
self.assertEqual(html_base.strip(), expected_without_smartstrong)
|
||||
|
||||
# Check that the plugin is active when requested.
|
||||
expected_with_smartstrong = "<p>foo__bar__baz</p>"
|
||||
html_ext, _, _ = build.convert_markdown(md_input, extensions=['smart_strong'])
|
||||
html_ext, _, _ = convert_markdown(md_input, extensions=['smart_strong'])
|
||||
self.assertEqual(html_ext.strip(), expected_with_smartstrong)
|
||||
|
||||
def test_markdown_duplicate_custom_extension(self):
|
||||
@@ -251,7 +252,7 @@ class BuildTests(unittest.TestCase):
|
||||
Duplicated extension names should not cause problems.
|
||||
"""
|
||||
md_input = "foo"
|
||||
html_ext, _, _ = build.convert_markdown(md_input, ['toc'])
|
||||
html_ext, _, _ = convert_markdown(md_input, ['toc'])
|
||||
self.assertEqual(html_ext.strip(), '<p>foo</p>')
|
||||
|
||||
def test_copying_media(self):
|
||||
@@ -303,8 +304,8 @@ class BuildTests(unittest.TestCase):
|
||||
site_nav = nav.SiteNavigation(pages)
|
||||
|
||||
valid = "[test](internal.md)"
|
||||
build.convert_markdown(valid, site_nav, strict=False)
|
||||
build.convert_markdown(valid, site_nav, strict=True)
|
||||
convert_markdown(valid, site_nav, strict=False)
|
||||
convert_markdown(valid, site_nav, strict=True)
|
||||
|
||||
def test_strict_mode_invalid(self):
|
||||
pages = [
|
||||
@@ -315,11 +316,11 @@ class BuildTests(unittest.TestCase):
|
||||
site_nav = nav.SiteNavigation(pages)
|
||||
|
||||
invalid = "[test](bad_link.md)"
|
||||
build.convert_markdown(invalid, site_nav, strict=False)
|
||||
convert_markdown(invalid, site_nav, strict=False)
|
||||
|
||||
self.assertRaises(
|
||||
MarkdownNotFound,
|
||||
build.convert_markdown, invalid, site_nav, strict=True)
|
||||
convert_markdown, invalid, site_nav, strict=True)
|
||||
|
||||
def test_extension_config(self):
|
||||
"""
|
||||
@@ -330,7 +331,7 @@ class BuildTests(unittest.TestCase):
|
||||
'toc': {'permalink': True},
|
||||
'meta': None # This gets ignored as it is an invalid config
|
||||
}
|
||||
html, toc, meta = build.convert_markdown(dedent("""
|
||||
html, toc, meta = convert_markdown(dedent("""
|
||||
# A Header
|
||||
"""), extensions=markdown_extensions)
|
||||
|
||||
|
||||
@@ -9,8 +9,11 @@ and structure of the site and pages in the site.
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import markdown
|
||||
from mkdocs import toc
|
||||
|
||||
from mkdocs.compat import urlparse, pathname2url
|
||||
from mkdocs.relative_path_ext import RelativePathExtension
|
||||
|
||||
|
||||
def copy_file(source_path, output_path):
|
||||
@@ -226,3 +229,40 @@ def path_to_url(path):
|
||||
return path
|
||||
|
||||
return pathname2url(path)
|
||||
|
||||
|
||||
def convert_markdown(markdown_source, site_navigation=None, extensions=(), strict=False):
|
||||
"""
|
||||
Convert the Markdown source file to HTML content, and additionally
|
||||
return the parsed table of contents, and a dictionary of any metadata
|
||||
that was specified in the Markdown file.
|
||||
|
||||
`extensions` is an optional sequence of Python Markdown extensions to add
|
||||
to the default set.
|
||||
"""
|
||||
|
||||
# Generate the HTML from the markdown source
|
||||
if isinstance(extensions, dict):
|
||||
user_extensions = list(extensions.keys())
|
||||
extension_configs = dict([(k, v) for k, v in extensions.items() if isinstance(v, dict)])
|
||||
else:
|
||||
user_extensions = list(extensions)
|
||||
extension_configs = {}
|
||||
builtin_extensions = ['meta', 'toc', 'tables', 'fenced_code']
|
||||
mkdocs_extensions = [RelativePathExtension(site_navigation, strict), ]
|
||||
extensions = set(builtin_extensions + mkdocs_extensions + user_extensions)
|
||||
md = markdown.Markdown(
|
||||
extensions=extensions,
|
||||
extension_configs=extension_configs
|
||||
)
|
||||
html_content = md.convert(markdown_source)
|
||||
|
||||
# On completely blank markdown files, no Meta or tox properties are added
|
||||
# to the generated document.
|
||||
meta = getattr(md, 'Meta', {})
|
||||
toc_html = getattr(md, 'toc', '')
|
||||
|
||||
# Post process the generated table of contents into a data structure
|
||||
table_of_contents = toc.TableOfContents(toc_html)
|
||||
|
||||
return (html_content, table_of_contents, meta)
|
||||
Reference in New Issue
Block a user