mirror of
https://github.com/getpelican/pelican.git
synced 2025-10-15 20:28:56 +02:00
Add a Base Theme Setting
Adds a setting to specify the base theme you want to use. No longer do you have to use simple. Can be specified from your settings file and the command line. The base theme directory is also watched for changes so that autoreload works.
This commit is contained in:
parent
4c581cdcf2
commit
d7993c0e3f
5 changed files with 73 additions and 22 deletions
|
|
@ -44,6 +44,7 @@ class Pelican(object):
|
|||
|
||||
self.path = settings['PATH']
|
||||
self.theme = settings['THEME']
|
||||
self.base_theme = settings['BASE_THEME']
|
||||
self.output_path = settings['OUTPUT_PATH']
|
||||
self.ignore_files = settings['IGNORE_FILES']
|
||||
self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY']
|
||||
|
|
@ -148,6 +149,7 @@ class Pelican(object):
|
|||
settings=self.settings,
|
||||
path=self.path,
|
||||
theme=self.theme,
|
||||
base_theme=self.base_theme,
|
||||
output_path=self.output_path,
|
||||
) for cls in self.get_generator_classes()
|
||||
]
|
||||
|
|
@ -224,6 +226,11 @@ def parse_arguments():
|
|||
'specified, it will use the default one included with '
|
||||
'pelican.')
|
||||
|
||||
parser.add_argument('-b', '--base-theme-path', dest='base_theme',
|
||||
help='Path where to find the base theme templates. If not '
|
||||
'specified, it will use the default one included with '
|
||||
'pelican.')
|
||||
|
||||
parser.add_argument('-o', '--output', dest='output',
|
||||
help='Where to output the generated files. If not '
|
||||
'specified, a directory will be created, named '
|
||||
|
|
@ -270,6 +277,9 @@ def get_config(args):
|
|||
if args.theme:
|
||||
abstheme = os.path.abspath(os.path.expanduser(args.theme))
|
||||
config['THEME'] = abstheme if os.path.exists(abstheme) else args.theme
|
||||
if args.base_theme:
|
||||
absbasetheme = os.path.abspath(os.path.expanduser(args.base_theme))
|
||||
config['BASE_THEME'] = absbasetheme if os.path.exists(absbasetheme) else args.base_theme
|
||||
if args.delete_outputdir is not None:
|
||||
config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir
|
||||
|
||||
|
|
@ -280,7 +290,7 @@ def get_config(args):
|
|||
if not six.PY3:
|
||||
enc = locale.getpreferredencoding()
|
||||
for key in config:
|
||||
if key in ('PATH', 'OUTPUT_PATH', 'THEME'):
|
||||
if key in ('PATH', 'OUTPUT_PATH', 'THEME', 'BASE_THEME'):
|
||||
config[key] = config[key].decode(enc)
|
||||
return config
|
||||
|
||||
|
|
@ -314,6 +324,9 @@ def main():
|
|||
'theme': folder_watcher(pelican.theme,
|
||||
[''],
|
||||
pelican.ignore_files),
|
||||
'base_theme': folder_watcher(pelican.base_theme,
|
||||
[''],
|
||||
pelican.ignore_files),
|
||||
'settings': file_watcher(args.settings)}
|
||||
|
||||
for static_path in settings.get("STATIC_PATHS", []):
|
||||
|
|
@ -321,13 +334,13 @@ def main():
|
|||
|
||||
try:
|
||||
if args.autoreload:
|
||||
print(' --- AutoReload Mode: Monitoring `content`, `theme` and'
|
||||
print(' --- AutoReload Mode: Monitoring `content`, `theme`, `base_theme` and'
|
||||
' `settings` for changes. ---')
|
||||
|
||||
while True:
|
||||
try:
|
||||
# Check source dir for changed files ending with the given
|
||||
# extension in the settings. In the theme dir is no such
|
||||
# extension in the settings. In the theme and base_theme dir there is no such
|
||||
# restriction; all files are recursively checked if they
|
||||
# have changed, no matter what extension the filenames
|
||||
# have.
|
||||
|
|
@ -347,6 +360,10 @@ def main():
|
|||
logger.warning('Empty theme folder. Using `basic` '
|
||||
'theme.')
|
||||
|
||||
if modified['base_theme'] is None:
|
||||
logger.warning('Empty base_theme folder. Using `simple` '
|
||||
'theme.')
|
||||
|
||||
pelican.run()
|
||||
|
||||
except KeyboardInterrupt:
|
||||
|
|
@ -370,6 +387,10 @@ def main():
|
|||
if next(watchers['theme']) is None:
|
||||
logger.warning('Empty theme folder. Using `basic` theme.')
|
||||
|
||||
if next(watchers['base_theme']) is None:
|
||||
logger.warning('Empty base_theme folder. Using `simple` '
|
||||
'theme.')
|
||||
|
||||
pelican.run()
|
||||
|
||||
except Exception as e:
|
||||
|
|
|
|||
|
|
@ -30,11 +30,12 @@ logger = logging.getLogger(__name__)
|
|||
class Generator(object):
|
||||
"""Baseclass generator"""
|
||||
|
||||
def __init__(self, context, settings, path, theme, output_path, **kwargs):
|
||||
def __init__(self, context, settings, path, theme, base_theme, output_path, **kwargs):
|
||||
self.context = context
|
||||
self.settings = settings
|
||||
self.path = path
|
||||
self.theme = theme
|
||||
self.base_theme = base_theme
|
||||
self.output_path = output_path
|
||||
|
||||
for arg, value in kwargs.items():
|
||||
|
|
@ -53,13 +54,19 @@ class Generator(object):
|
|||
|
||||
simple_loader = FileSystemLoader(os.path.join(theme_path,
|
||||
"themes", "simple", "templates"))
|
||||
|
||||
base_theme = self.settings['BASE_THEME']
|
||||
|
||||
base_loader = FileSystemLoader(os.path.join(theme_path,
|
||||
"themes", base_theme, "templates"))
|
||||
self.env = Environment(
|
||||
trim_blocks=True,
|
||||
lstrip_blocks=True,
|
||||
loader=ChoiceLoader([
|
||||
FileSystemLoader(self._templates_path),
|
||||
simple_loader, # implicit inheritance
|
||||
PrefixLoader({'!simple': simple_loader}) # explicit one
|
||||
base_loader, # implicit inheritance
|
||||
PrefixLoader({'!simple': simple_loader}), # explicit one
|
||||
PrefixLoader({'!base': base_loader}) # explicit one
|
||||
]),
|
||||
extensions=self.settings['JINJA_EXTENSIONS'],
|
||||
)
|
||||
|
|
@ -606,6 +613,10 @@ class StaticGenerator(Generator):
|
|||
self._update_context(('staticfiles',))
|
||||
|
||||
def generate_output(self, writer):
|
||||
|
||||
self._copy_paths(self.settings['BASE_THEME_STATIC_PATHS'], self.base_theme,
|
||||
self.settings['THEME_STATIC_DIR'], self.output_path,
|
||||
os.curdir)
|
||||
self._copy_paths(self.settings['THEME_STATIC_PATHS'], self.theme,
|
||||
self.settings['THEME_STATIC_DIR'], self.output_path,
|
||||
os.curdir)
|
||||
|
|
|
|||
|
|
@ -25,18 +25,23 @@ logger = logging.getLogger(__name__)
|
|||
|
||||
DEFAULT_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
'themes', 'notmyidea')
|
||||
DEFAULT_BASE_THEME = os.path.join(os.path.dirname(os.path.abspath(__file__)),
|
||||
'themes', 'simple')
|
||||
|
||||
DEFAULT_CONFIG = {
|
||||
'PATH': os.curdir,
|
||||
'ARTICLE_DIR': '',
|
||||
'ARTICLE_EXCLUDES': ('pages',),
|
||||
'PAGE_DIR': 'pages',
|
||||
'PAGE_EXCLUDES': (),
|
||||
'BASE_THEME': DEFAULT_BASE_THEME,
|
||||
'THEME': DEFAULT_THEME,
|
||||
'OUTPUT_PATH': 'output',
|
||||
'READERS': {},
|
||||
'STATIC_PATHS': ['images', ],
|
||||
'THEME_STATIC_DIR': 'theme',
|
||||
'THEME_STATIC_PATHS': ['static', ],
|
||||
'BASE_THEME_STATIC_PATHS': ['static', ],
|
||||
'FEED_ALL_ATOM': os.path.join('feeds', 'all.atom.xml'),
|
||||
'CATEGORY_FEED_ATOM': os.path.join('feeds', '%s.atom.xml'),
|
||||
'TRANSLATION_FEED_ATOM': os.path.join('feeds', 'all-%s.atom.xml'),
|
||||
|
|
@ -126,12 +131,12 @@ def read_settings(path=None, override=None):
|
|||
if path:
|
||||
local_settings = get_settings_from_file(path)
|
||||
# Make the paths relative to the settings file
|
||||
for p in ['PATH', 'OUTPUT_PATH', 'THEME', 'PLUGIN_PATH']:
|
||||
for p in ['PATH', 'OUTPUT_PATH', 'THEME', 'BASE_THEME', 'PLUGIN_PATH']:
|
||||
if p in local_settings and local_settings[p] is not None \
|
||||
and not isabs(local_settings[p]):
|
||||
absp = os.path.abspath(os.path.normpath(os.path.join(
|
||||
os.path.dirname(path), local_settings[p])))
|
||||
if p not in ('THEME', 'PLUGIN_PATH') or os.path.exists(absp):
|
||||
if p not in ('THEME', 'BASE_THEME', 'PLUGIN_PATH') or os.path.exists(absp):
|
||||
local_settings[p] = absp
|
||||
else:
|
||||
local_settings = copy.deepcopy(DEFAULT_CONFIG)
|
||||
|
|
@ -188,6 +193,18 @@ def configure_settings(settings):
|
|||
raise Exception("Could not find the theme %s"
|
||||
% settings['THEME'])
|
||||
|
||||
# lookup the base theme in "pelican/themes" if the given one doesn't exist
|
||||
if not os.path.isdir(settings['BASE_THEME']):
|
||||
theme_path = os.path.join(
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
'themes',
|
||||
settings['BASE_THEME'])
|
||||
if os.path.exists(theme_path):
|
||||
settings['BASE_THEME'] = theme_path
|
||||
else:
|
||||
raise Exception("Could not find the base theme %s"
|
||||
% settings['BASE_THEME'])
|
||||
|
||||
# standardize strings to lowercase strings
|
||||
for key in [
|
||||
'DEFAULT_LANG',
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ class TestGenerator(unittest.TestCase):
|
|||
self.settings = get_settings()
|
||||
self.settings['READERS'] = {'asc': None}
|
||||
self.generator = Generator(self.settings.copy(), self.settings,
|
||||
CUR_DIR, self.settings['THEME'], None)
|
||||
CUR_DIR, self.settings['THEME'], self.settings['BASE_THEME'], None)
|
||||
|
||||
def test_include_path(self):
|
||||
filename = os.path.join(CUR_DIR, 'content', 'article.rst')
|
||||
|
|
@ -45,7 +45,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
|
||||
cls.generator = ArticlesGenerator(
|
||||
context=settings.copy(), settings=settings,
|
||||
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
|
||||
path=CONTENT_DIR, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
cls.generator.generate_context()
|
||||
cls.articles = [[page.title, page.status, page.category.name,
|
||||
page.template] for page in cls.generator.articles]
|
||||
|
|
@ -54,7 +54,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings = get_settings()
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=settings,
|
||||
path=None, theme=settings['THEME'], output_path=None)
|
||||
path=None, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
writer = MagicMock()
|
||||
generator.generate_feeds(writer)
|
||||
writer.write_feed.assert_called_with([], settings,
|
||||
|
|
@ -62,7 +62,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=get_settings(FEED_ALL_ATOM=None),
|
||||
path=None, theme=settings['THEME'], output_path=None)
|
||||
path=None, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
writer = MagicMock()
|
||||
generator.generate_feeds(writer)
|
||||
self.assertFalse(writer.write_feed.called)
|
||||
|
|
@ -131,7 +131,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings['filenames'] = {}
|
||||
generator = ArticlesGenerator(
|
||||
context=settings.copy(), settings=settings,
|
||||
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
|
||||
path=CONTENT_DIR, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
generator.generate_context()
|
||||
# test for name
|
||||
# categories are grouped by slug; if two categories have the same slug
|
||||
|
|
@ -153,7 +153,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings = get_settings(filenames={})
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=settings,
|
||||
path=None, theme=settings['THEME'], output_path=None)
|
||||
path=None, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
write = MagicMock()
|
||||
generator.generate_direct_templates(write)
|
||||
write.assert_called_with("archives.html",
|
||||
|
|
@ -167,7 +167,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings['ARCHIVES_SAVE_AS'] = 'archives/index.html'
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=settings,
|
||||
path=None, theme=settings['THEME'], output_path=None)
|
||||
path=None, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
write = MagicMock()
|
||||
generator.generate_direct_templates(write)
|
||||
write.assert_called_with("archives/index.html",
|
||||
|
|
@ -182,7 +182,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings['ARCHIVES_SAVE_AS'] = 'archives/index.html'
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=settings,
|
||||
path=None, theme=settings['THEME'], output_path=None)
|
||||
path=None, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
write = MagicMock()
|
||||
generator.generate_direct_templates(write)
|
||||
write.assert_called_count == 0
|
||||
|
|
@ -208,7 +208,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings['YEAR_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/index.html'
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=settings,
|
||||
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
|
||||
path=CONTENT_DIR, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
generator.generate_context()
|
||||
write = MagicMock()
|
||||
generator.generate_period_archives(write)
|
||||
|
|
@ -225,7 +225,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings['MONTH_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/{date:%b}/index.html'
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=settings,
|
||||
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
|
||||
path=CONTENT_DIR, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
generator.generate_context()
|
||||
write = MagicMock()
|
||||
generator.generate_period_archives(write)
|
||||
|
|
@ -243,7 +243,7 @@ class TestArticlesGenerator(unittest.TestCase):
|
|||
settings['DAY_ARCHIVE_SAVE_AS'] = 'posts/{date:%Y}/{date:%b}/{date:%d}/index.html'
|
||||
generator = ArticlesGenerator(
|
||||
context=settings, settings=settings,
|
||||
path=CONTENT_DIR, theme=settings['THEME'], output_path=None)
|
||||
path=CONTENT_DIR, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
generator.generate_context()
|
||||
write = MagicMock()
|
||||
generator.generate_period_archives(write)
|
||||
|
|
@ -285,7 +285,7 @@ class TestPageGenerator(unittest.TestCase):
|
|||
|
||||
generator = PagesGenerator(
|
||||
context=settings.copy(), settings=settings,
|
||||
path=CUR_DIR, theme=settings['THEME'], output_path=None)
|
||||
path=CUR_DIR, theme=settings['THEME'], base_theme=settings['BASE_THEME'], output_path=None)
|
||||
generator.generate_context()
|
||||
pages = self.distill_pages(generator.pages)
|
||||
hidden_pages = self.distill_pages(generator.hidden_pages)
|
||||
|
|
@ -329,7 +329,7 @@ class TestTemplatePagesGenerator(unittest.TestCase):
|
|||
|
||||
generator = TemplatePagesGenerator(
|
||||
context={'foo': 'bar'}, settings=settings,
|
||||
path=self.temp_content, theme='', output_path=self.temp_output)
|
||||
path=self.temp_content, theme='', base_theme=settings['BASE_THEME'], output_path=self.temp_output)
|
||||
|
||||
# create a dummy template file
|
||||
template_dir = os.path.join(self.temp_content, 'template')
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import locale
|
|||
from os.path import dirname, abspath, join
|
||||
|
||||
from pelican.settings import (read_settings, configure_settings,
|
||||
DEFAULT_CONFIG, DEFAULT_THEME)
|
||||
DEFAULT_CONFIG, DEFAULT_THEME, DEFAULT_BASE_THEME)
|
||||
from pelican.tests.support import unittest
|
||||
|
||||
|
||||
|
|
@ -65,6 +65,7 @@ class TestSettingsConfiguration(unittest.TestCase):
|
|||
# These 4 settings are required to run configure_settings
|
||||
'PATH': '.',
|
||||
'THEME': DEFAULT_THEME,
|
||||
'BASE_THEME': DEFAULT_BASE_THEME,
|
||||
'SITEURL': 'http://blog.notmyidea.org/',
|
||||
'LOCALE': '',
|
||||
}
|
||||
|
|
@ -82,6 +83,7 @@ class TestSettingsConfiguration(unittest.TestCase):
|
|||
'LOCALE': '',
|
||||
'PATH': os.curdir,
|
||||
'THEME': DEFAULT_THEME,
|
||||
'BASE_THEME': DEFAULT_BASE_THEME,
|
||||
}
|
||||
configure_settings(settings)
|
||||
# SITEURL should not have a trailing slash
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue