Merge pull request #547 from bbinet/refactor-settings

Refactor how settings are handled
This commit is contained in:
Bruno Binet 2012-10-21 15:37:02 -07:00
commit 4f1e3293ff
4 changed files with 80 additions and 66 deletions

View file

@ -1,4 +1,3 @@
import copy
import os import os
import re import re
import sys import sys
@ -12,7 +11,7 @@ from pelican.generators import (ArticlesGenerator, PagesGenerator,
StaticGenerator, PdfGenerator, StaticGenerator, PdfGenerator,
LessCSSGenerator, SourceFileGenerator) LessCSSGenerator, SourceFileGenerator)
from pelican.log import init from pelican.log import init
from pelican.settings import read_settings, _DEFAULT_CONFIG from pelican.settings import read_settings
from pelican.utils import (clean_output_dir, files_changed, file_changed, from pelican.utils import (clean_output_dir, files_changed, file_changed,
NoFilesError) NoFilesError)
from pelican.writers import Writer from pelican.writers import Writer
@ -26,42 +25,21 @@ logger = logging.getLogger(__name__)
class Pelican(object): class Pelican(object):
def __init__(self, settings=None, path=None, theme=None, output_path=None, def __init__(self, settings):
markup=None, delete_outputdir=False, plugin_path=None): """
"""Read the settings, and performs some checks on the environment Pelican initialisation, performs some checks on the environment before
before doing anything else. doing anything else.
""" """
if settings is None:
settings = copy.deepcopy(_DEFAULT_CONFIG)
self.path = path or settings['PATH']
if not self.path:
raise Exception('You need to specify a path containing the content'
' (see pelican --help for more information)')
if self.path.endswith('/'):
self.path = self.path[:-1]
# define the default settings # define the default settings
self.settings = settings self.settings = settings
self._handle_deprecation() self._handle_deprecation()
self.theme = theme or settings['THEME'] self.path = settings['PATH']
output_path = output_path or settings['OUTPUT_PATH'] self.theme = settings['THEME']
self.output_path = os.path.realpath(output_path) self.output_path = settings['OUTPUT_PATH']
self.markup = markup or settings['MARKUP'] self.markup = settings['MARKUP']
self.delete_outputdir = delete_outputdir \ self.delete_outputdir = settings['DELETE_OUTPUT_DIRECTORY']
or settings['DELETE_OUTPUT_DIRECTORY']
# find the theme in pelican.theme if the given one does not exists
if not os.path.exists(self.theme):
theme_path = os.sep.join([os.path.dirname(
os.path.abspath(__file__)), "themes/%s" % self.theme])
if os.path.exists(theme_path):
self.theme = theme_path
else:
raise Exception("Impossible to find the theme %s" % theme)
self.init_path() self.init_path()
self.init_plugins() self.init_plugins()
@ -259,11 +237,26 @@ def parse_arguments():
return parser.parse_args() return parser.parse_args()
def get_instance(args): def get_config(args):
markup = [a.strip().lower() for a in args.markup.split(',')]\ config = {}
if args.markup else None if args.path:
config['PATH'] = os.path.abspath(os.path.expanduser(args.path))
if args.output:
config['OUTPUT_PATH'] = \
os.path.abspath(os.path.expanduser(args.output))
if args.markup:
config['MARKUP'] = [a.strip().lower() for a in args.markup.split(',')]
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.delete_outputdir is not None:
config['DELETE_OUTPUT_DIRECTORY'] = args.delete_outputdir
return config
settings = read_settings(args.settings)
def get_instance(args):
settings = read_settings(args.settings, override=get_config(args))
cls = settings.get('PELICAN_CLASS') cls = settings.get('PELICAN_CLASS')
if isinstance(cls, basestring): if isinstance(cls, basestring):
@ -271,15 +264,12 @@ def get_instance(args):
module = __import__(module) module = __import__(module)
cls = getattr(module, cls_name) cls = getattr(module, cls_name)
return cls(settings, args.path, args.theme, args.output, markup, return cls(settings)
args.delete_outputdir)
def main(): def main():
args = parse_arguments() args = parse_arguments()
init(args.verbosity) init(args.verbosity)
# Split the markup languages only if some have been given. Otherwise,
# populate the variable with None.
pelican = get_instance(args) pelican = get_instance(args)
try: try:

View file

@ -82,13 +82,24 @@ _DEFAULT_CONFIG = {'PATH': '.',
} }
def read_settings(filename=None): def read_settings(filename=None, override=None):
if filename: if filename:
local_settings = get_settings_from_file(filename) local_settings = get_settings_from_file(filename)
# Make the paths relative to the settings file
for p in ['PATH', 'OUTPUT_PATH', 'THEME']:
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(filename), local_settings[p])))
if p != 'THEME' or os.path.exists(p):
local_settings[p] = absp
else: else:
local_settings = copy.deepcopy(_DEFAULT_CONFIG) local_settings = copy.deepcopy(_DEFAULT_CONFIG)
configured_settings = configure_settings(local_settings, None, filename)
return configured_settings if override:
local_settings.update(override)
return configure_settings(local_settings)
def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG): def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
@ -98,9 +109,8 @@ def get_settings_from_module(module=None, default_settings=_DEFAULT_CONFIG):
context = copy.deepcopy(default_settings) context = copy.deepcopy(default_settings)
if module is not None: if module is not None:
context.update( context.update(
(k, v) for k, v in inspect.getmembers(module) if k.isupper() (k, v) for k, v in inspect.getmembers(module) if k.isupper())
)
return context return context
@ -115,19 +125,23 @@ def get_settings_from_file(filename, default_settings=_DEFAULT_CONFIG):
return get_settings_from_module(module, default_settings=default_settings) return get_settings_from_module(module, default_settings=default_settings)
def configure_settings(settings, default_settings=None, filename=None): def configure_settings(settings):
"""Provide optimizations, error checking, and warnings for loaded settings""" """
if default_settings is None: Provide optimizations, error checking, and warnings for loaded settings
default_settings = copy.deepcopy(_DEFAULT_CONFIG) """
if not 'PATH' in settings or not os.path.isdir(settings['PATH']):
raise Exception('You need to specify a path containing the content'
' (see pelican --help for more information)')
# Make the paths relative to the settings file # find the theme in pelican.theme if the given one does not exists
if filename: if not os.path.isdir(settings['THEME']):
for path in ['PATH', 'OUTPUT_PATH']: theme_path = os.sep.join([os.path.dirname(
if path in settings: os.path.abspath(__file__)), "themes/%s" % settings['THEME']])
if settings[path] is not None and not isabs(settings[path]): if os.path.exists(theme_path):
settings[path] = os.path.abspath(os.path.normpath( settings['THEME'] = theme_path
os.path.join(os.path.dirname(filename), settings[path])) else:
) raise Exception("Impossible to find the theme %s"
% settings['THEME'])
# if locales is not a list, make it one # if locales is not a list, make it one
locales = settings['LOCALE'] locales = settings['LOCALE']

View file

@ -55,7 +55,11 @@ class TestPelican(unittest.TestCase):
with patch("pelican.contents.getenv") as mock_getenv: with patch("pelican.contents.getenv") as mock_getenv:
# force getenv('USER') to always return the same value # force getenv('USER') to always return the same value
mock_getenv.return_value = "Dummy Author" mock_getenv.return_value = "Dummy Author"
pelican = Pelican(path=INPUT_PATH, output_path=self.temp_path) settings = read_settings(filename=None, override={
'PATH': INPUT_PATH,
'OUTPUT_PATH': self.temp_path,
})
pelican = Pelican(settings=settings)
pelican.run() pelican.run()
diff = dircmp( diff = dircmp(
self.temp_path, os.sep.join((OUTPUT_PATH, "basic"))) self.temp_path, os.sep.join((OUTPUT_PATH, "basic")))
@ -63,8 +67,11 @@ class TestPelican(unittest.TestCase):
def test_custom_generation_works(self): def test_custom_generation_works(self):
# the same thing with a specified set of settings should work # the same thing with a specified set of settings should work
pelican = Pelican(path=INPUT_PATH, output_path=self.temp_path, settings = read_settings(filename=SAMPLE_CONFIG, override={
settings=read_settings(SAMPLE_CONFIG)) 'PATH': INPUT_PATH,
'OUTPUT_PATH': self.temp_path,
})
pelican = Pelican(settings=settings)
pelican.run() pelican.run()
diff = dircmp(self.temp_path, os.sep.join((OUTPUT_PATH, "custom"))) diff = dircmp(self.temp_path, os.sep.join((OUTPUT_PATH, "custom")))
self.assertFilesEqual(diff) self.assertFilesEqual(diff)

View file

@ -56,16 +56,19 @@ class TestSettingsConfiguration(unittest.TestCase):
def test_configure_settings(self): def test_configure_settings(self):
"""Manipulations to settings should be applied correctly.""" """Manipulations to settings should be applied correctly."""
# SITEURL should not have a trailing slash settings = {
settings = {'SITEURL': 'http://blog.notmyidea.org/', 'LOCALE': ''} 'SITEURL': 'http://blog.notmyidea.org/',
'LOCALE': '',
'PATH': '.',
'THEME': DEFAULT_THEME,
}
configure_settings(settings) configure_settings(settings)
# SITEURL should not have a trailing slash
self.assertEqual(settings['SITEURL'], 'http://blog.notmyidea.org') self.assertEqual(settings['SITEURL'], 'http://blog.notmyidea.org')
# FEED_DOMAIN, if undefined, should default to SITEURL # FEED_DOMAIN, if undefined, should default to SITEURL
settings = {'SITEURL': 'http://blog.notmyidea.org', 'LOCALE': ''}
configure_settings(settings)
self.assertEqual(settings['FEED_DOMAIN'], 'http://blog.notmyidea.org') self.assertEqual(settings['FEED_DOMAIN'], 'http://blog.notmyidea.org')
settings = {'FEED_DOMAIN': 'http://feeds.example.com', 'LOCALE': ''} settings['FEED_DOMAIN'] = 'http://feeds.example.com'
configure_settings(settings) configure_settings(settings)
self.assertEqual(settings['FEED_DOMAIN'], 'http://feeds.example.com') self.assertEqual(settings['FEED_DOMAIN'], 'http://feeds.example.com')