mirror of
https://github.com/mkdocs/mkdocs.git
synced 2026-03-27 09:58:31 +07:00
Change the builtin themes to packages and update theme loading
This commit is contained in:
@@ -260,7 +260,7 @@ class ThemeDir(Dir):
|
||||
|
||||
package_dir = os.path.abspath(
|
||||
os.path.join(os.path.dirname(__file__), '..'))
|
||||
theme_dir = [os.path.join(package_dir, 'themes', config['theme']), ]
|
||||
theme_dir = [utils.get_themes()[config['theme']], ]
|
||||
config['mkdocs_templates'] = os.path.join(package_dir, 'templates')
|
||||
|
||||
if config['theme_dir'] is not None:
|
||||
|
||||
@@ -2,10 +2,12 @@
|
||||
# coding: utf-8
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import mock
|
||||
import os
|
||||
import unittest
|
||||
|
||||
from mkdocs import nav, utils
|
||||
from mkdocs import nav, utils, exceptions
|
||||
from mkdocs.tests.base import dedent
|
||||
|
||||
|
||||
@@ -112,6 +114,44 @@ class UtilsTests(unittest.TestCase):
|
||||
sorted(utils.get_theme_names()),
|
||||
sorted(['mkdocs', 'readthedocs', ]))
|
||||
|
||||
@mock.patch('pkg_resources.iter_entry_points', autospec=True)
|
||||
def test_get_themes_warning(self, mock_iter):
|
||||
|
||||
theme1 = mock.Mock()
|
||||
theme1.name = 'mkdocs2'
|
||||
theme1.dist.key = 'mkdocs2'
|
||||
theme1.load().__file__ = "some/path1"
|
||||
|
||||
theme2 = mock.Mock()
|
||||
theme2.name = 'mkdocs2'
|
||||
theme2.dist.key = 'mkdocs3'
|
||||
theme2.load().__file__ = "some/path2"
|
||||
|
||||
mock_iter.return_value = iter([theme1, theme2])
|
||||
|
||||
self.assertEqual(
|
||||
sorted(utils.get_theme_names()),
|
||||
sorted(['mkdocs2', ]))
|
||||
|
||||
@mock.patch('pkg_resources.iter_entry_points', autospec=True)
|
||||
@mock.patch('pkg_resources.get_entry_map', autospec=True)
|
||||
def test_get_themes_error(self, mock_get, mock_iter):
|
||||
|
||||
theme1 = mock.Mock()
|
||||
theme1.name = 'mkdocs'
|
||||
theme1.dist.key = 'mkdocs'
|
||||
theme1.load().__file__ = "some/path1"
|
||||
|
||||
theme2 = mock.Mock()
|
||||
theme2.name = 'mkdocs'
|
||||
theme2.dist.key = 'mkdocs2'
|
||||
theme2.load().__file__ = "some/path2"
|
||||
|
||||
mock_iter.return_value = iter([theme1, theme2])
|
||||
mock_get.return_value = {'mkdocs': theme1, }
|
||||
|
||||
self.assertRaises(exceptions.ConfigurationError, utils.get_theme_names)
|
||||
|
||||
def test_nest_paths(self):
|
||||
|
||||
j = os.path.join
|
||||
|
||||
0
mkdocs/themes/__init__.py
Normal file
0
mkdocs/themes/__init__.py
Normal file
0
mkdocs/themes/mkdocs/__init__.py
Normal file
0
mkdocs/themes/mkdocs/__init__.py
Normal file
0
mkdocs/themes/readthedocs/__init__.py
Normal file
0
mkdocs/themes/readthedocs/__init__.py
Normal file
@@ -8,13 +8,16 @@ and structure of the site and pages in the site.
|
||||
"""
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import os
|
||||
import sys
|
||||
import shutil
|
||||
|
||||
import logging
|
||||
import markdown
|
||||
import os
|
||||
import pkg_resources
|
||||
import shutil
|
||||
import sys
|
||||
import yaml
|
||||
|
||||
from mkdocs import toc
|
||||
from mkdocs import toc, exceptions
|
||||
|
||||
try: # pragma: no cover
|
||||
from urllib.parse import urlparse, urlunparse, urljoin # noqa
|
||||
@@ -35,6 +38,8 @@ else: # pragma: no cover
|
||||
string_types = basestring, # noqa
|
||||
text_type = unicode # noqa
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
|
||||
def yaml_load(source, loader=yaml.Loader):
|
||||
"""
|
||||
@@ -45,24 +50,32 @@ def yaml_load(source, loader=yaml.Loader):
|
||||
"""
|
||||
|
||||
def construct_yaml_str(self, node):
|
||||
"""Override the default string handling function to always return unicode objects."""
|
||||
"""
|
||||
Override the default string handling function to always return
|
||||
unicode objects.
|
||||
"""
|
||||
return self.construct_scalar(node)
|
||||
|
||||
class Loader(loader):
|
||||
"""Define a custom loader derived from the global loader to leave the global loader unaltered."""
|
||||
"""
|
||||
Define a custom loader derived from the global loader to leave the
|
||||
global loader unaltered.
|
||||
"""
|
||||
|
||||
# Attach our unicode constructor to our custom loader ensuring all strings will be unicode on translation.
|
||||
# Attach our unicode constructor to our custom loader ensuring all strings
|
||||
# will be unicode on translation.
|
||||
Loader.add_constructor('tag:yaml.org,2002:str', construct_yaml_str)
|
||||
|
||||
try:
|
||||
return yaml.load(source, Loader)
|
||||
finally:
|
||||
# TODO: Remove this when external calls are properly cleaning up file objects.
|
||||
# Some mkdocs internal calls, sometimes in test lib, will load configs
|
||||
# with a file object but never close it. On some systems, if a delete
|
||||
# action is performed on that file without Python closing that object,
|
||||
# there will be an access error. This will process the file and close it
|
||||
# as there should be no more use for the file once we process the yaml content.
|
||||
# TODO: Remove this when external calls are properly cleaning up file
|
||||
# objects. Some mkdocs internal calls, sometimes in test lib, will
|
||||
# load configs with a file object but never close it. On some
|
||||
# systems, if a delete action is performed on that file without Python
|
||||
# closing that object, there will be an access error. This will
|
||||
# process the file and close it as there should be no more use for the
|
||||
# file once we process the yaml content.
|
||||
if hasattr(source, 'close'):
|
||||
source.close()
|
||||
|
||||
@@ -70,7 +83,8 @@ def yaml_load(source, loader=yaml.Loader):
|
||||
def reduce_list(data_set):
|
||||
""" Reduce duplicate items in a list and preserve order """
|
||||
seen = set()
|
||||
return [item for item in data_set if item not in seen and not seen.add(item)]
|
||||
return [item for item in data_set if
|
||||
item not in seen and not seen.add(item)]
|
||||
|
||||
|
||||
def copy_file(source_path, output_path):
|
||||
@@ -335,10 +349,37 @@ def convert_markdown(markdown_source, extensions=None, extension_configs=None):
|
||||
return (html_content, table_of_contents, meta)
|
||||
|
||||
|
||||
def get_themes():
|
||||
"""Return a dict of theme names and their locations"""
|
||||
|
||||
themes = {}
|
||||
builtins = pkg_resources.get_entry_map(dist='mkdocs', group='mkdocs.themes')
|
||||
|
||||
for theme in pkg_resources.iter_entry_points(group='mkdocs.themes'):
|
||||
|
||||
if theme.name in builtins and theme.dist.key != 'mkdocs':
|
||||
raise exceptions.ConfigurationError(
|
||||
"The theme {0} is a builtin theme but {1} provides a theme "
|
||||
"with the same name".format(theme.name, theme.dist.key))
|
||||
|
||||
elif theme.name in themes:
|
||||
multiple_packages = [themes[theme.name].dist.key, theme.dist.key]
|
||||
log.warning("The theme %s is provided by the Python packages "
|
||||
"'%s'. The one in %s will be used.",
|
||||
theme.name, ','.join(multiple_packages), theme.dist.key)
|
||||
|
||||
themes[theme.name] = theme
|
||||
|
||||
themes = dict((name, os.path.dirname(os.path.abspath(theme.load().__file__)))
|
||||
for name, theme in themes.items())
|
||||
|
||||
return themes
|
||||
|
||||
|
||||
def get_theme_names():
|
||||
"""Return a list containing all the names of all the builtin themes."""
|
||||
|
||||
return os.listdir(os.path.join(os.path.dirname(__file__), '..', 'themes'))
|
||||
return get_themes().keys()
|
||||
|
||||
|
||||
def filename_to_title(filename):
|
||||
|
||||
Reference in New Issue
Block a user