From dcba91c0c1c9aa3daee75a99047a6ff4a4d45f3d Mon Sep 17 00:00:00 2001 From: Noah Petherbridge Date: Thu, 9 Jul 2015 22:46:51 -0700 Subject: [PATCH] Migrate config system to YamlSettings --- .gitignore | 1 + defaults.ini | 195 -------------------------------------------- defaults.yml | 161 ++++++++++++++++++++++++++++++++++++ requirements.txt | 1 + rophako/settings.py | 53 +++++------- 5 files changed, 185 insertions(+), 226 deletions(-) delete mode 100644 defaults.ini create mode 100644 defaults.yml diff --git a/.gitignore b/.gitignore index 636d872..e941606 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ # Don't check in site specific settings. settings.ini +settings.yml # Compiled Python *.pyc diff --git a/defaults.ini b/defaults.ini deleted file mode 100644 index 01aada2..0000000 --- a/defaults.ini +++ /dev/null @@ -1,195 +0,0 @@ -# Default configuration settings for Rophako - DO NOT EDIT THIS FILE! -# -# To configure your site, create a new file named "settings.ini" and override -# settings defined in this file. Your settings.ini is masked on top of the -# settings in defaults.ini. -# -# String values can substitute the following special variables: -# %(_basedir): The absolute path to the root of this git repository, such that -# ./rophako/app.py exists. -# %(_year): inserts the current year (for the RSS feed copyright setting) -# -# You can also define custom global variables in the [DEFAULT] section. -# Variables in this section are injected into ALL other sections, so it's -# recommended to prefix these with an underscore to avoid any conflicting names. -# -# See the Python documentation for ConfigParser if you have any questions -# on the syntax of this file. - -# Constants that may be useful in this file. -[DEFAULT] -_admin_email = root@localhost -_date_format = %A, %B %d %Y @ %I:%M:%S %p -# "Weekday, Month dd yyyy @ hh:mm:ss AM" - -#------------------------------------------------------------------------------# -# General Website Settings # -#------------------------------------------------------------------------------# -[site] - -# Debug mode for development only! -debug = false - -# Unique name of your site, e.g. "kirsle.net" -site_name = example.com - -# Path to your site's HTML root. Whenever Rophako tries to render a -# template, it will check in your site's root for the template first before -# defaulting to the default fallback pages in the rophako/www folder. All -# of the core Rophako pages, e.g. for account, blog, photo albums and so on, -# have templates in the default site. You can override those templates by -# creating files with the same paths in your site's HTML folder. -site_root = %(_basedir)s/site/www - -# E-mail address for site notifications (e.g. new comments and exceptions) -notify_address = %(_admin_email)s - -# Where to save temp files for photo uploads etc. -tempdir = /tmp - -#------------------------------------------------------------------------------# -# Database settings # -#------------------------------------------------------------------------------# -[db] - -# Rophako uses a flat file JSON database system, and a Redis server sits -# between Rophako and the filesystem. The db_root is the path on the -# filesystem to store documents in (can be relative, default "./db") -db_root = db - -redis_host = localhost -redis_port = 6379 -redis_db = 0 -redis_prefix = rophako: - -#------------------------------------------------------------------------------# -# Security Settings # -#------------------------------------------------------------------------------# -[security] - -# Set this value to true to force SSL/TLS use on your web app. Turning -# this on will do the following: -# - Send HTTP Strict-Transport-Security header -# - Use secure session cookies -force_ssl = false - -# Secret key used for session cookie signing. Make this long and hard to -# guess. -# -# Tips for creating a strong secret key: -# $ python -# >>> import os -# >>> os.urandom(24) -# '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O - -#------------------------------------------------------------------------------# -# Plugin Configurations # -#------------------------------------------------------------------------------# - -### -# Emoticons -### -# Emoticon theme used for blog posts and comments. Should exist at the URL -# "/static/smileys" from your document root, and have a file named -# "emoticons.json" inside. If you add a custom theme to your private site -# folder, then also change EMOTICON_ROOT_PRIVATE to look there instead. -[emoticons] -theme = tango -root_private = %(_basedir)s/rophako/www/static/smileys - -### -# Blog -### -[blog] -default_category = Uncategorized -default_privacy = public -time_format = %(_date_format)s -allow_comments = true -entries_per_page = 5 - -# RSS feed settings. -title = Rophako CMS Blog -link = http://rophako.kirsle.net/ -language = en -description = The web blog of the Rophako CMS. -copyright = Copyright %(_year)s -webmaster = %(_admin_email)s -image_title = Rophako CMS Blog -image_url = //www.kirsle.net/static/avatars/default.png -image_width = 100 -image_height = 100 -image_description = Rophako CMS -entries_per_feed = 5 - -### -# Photo -### -[photo] -# The path to where uploaded photos will be stored. -# The PRIVATE path is from the perspective of the server file system. -# The PUBLIC path is from the perspective of the web browser via HTTP. -root_private = %(_basedir)s/site/www/static/photos -root_public = /static/photos -default_album = My Photos -time_format = %(_date_format)s -# Max widths for photo sizes -width_large = 800 -width_thumb = 256 -width_avatar = 96 - -### -# Comment -### -[comment] -time_format = %(_date_format)s -# We use Gravatar for comments if the user provides an e-mail address. -# Specify the URL to a fallback image to use in case they don't have -# a gravatar. -default_avatar = - -### -# Wiki -### -[wiki] -default_page = Main Page -time_format = %(_date_format)s - -#------------------------------------------------------------------------------# -# List of Enabled Plugins # -#------------------------------------------------------------------------------# -[plugins] - -# Which plugins to enable? List each plugin by module name. The plugins -# will be assumed to be blueprints that can be attached to the main app -# object. If you instead want to load an arbitrary Python module (i.e. to -# define custom routes at the app layer, not in a blueprint) list those -# under the "custom" section (remove the empty array [] and list them -# like shown in the plugins section). -blueprints = - rophako.modules.blog - rophako.modules.wiki - rophako.modules.photo - rophako.modules.comment - rophako.modules.emoticons - rophako.modules.contact -# rophako.modules.tracking - -custom = diff --git a/defaults.yml b/defaults.yml new file mode 100644 index 0000000..43b9b3f --- /dev/null +++ b/defaults.yml @@ -0,0 +1,161 @@ +# Default configuration settings for Rophako -- DO NOT EDIT THIS FILE! +# +# To configure your site, create a new file named "settings.yml" and override +# settings defined in this file. Your settings.yml is masked on top of the +# settings in defaults.yml. + +rophako: + ### + # General Website Settings + ### + site: + debug: false + + # Unique name of your site, e.g. "kirsle.net" + site_name: example.com + + # Path to your site's HTML root. Whenever Rophako tries to render a + # template, it will check in your site's root for the template first + # before defaulting to the fallback pages in the rophako/www folder. + # All of the core Rophako pages, e.g. for account, blog, photo albums + # and so on, have templates in the default site. You can override those + # templates by creating files with the same paths in your site's root. + site_root: "{basedir}/site/www" + + # E-mail address for site notifications (e.g. new comments and errors) + notify_address: &ADMIN_EMAIL root@localhost + + # Default date/time format (not used by the Rophako app but referenced + # by other spots in this config file, for easy overriding). + _date_format: &DATE_FORMAT '%A, %B %d %Y @ %I:%M:%S %p' + + # Where to save temp files for photo uploads etc. + tempdir: /tmp + + ### + # Database settings + ### + db: + # Rophako uses a flat file JSON database system, and a Redis server sits + # between Rophako and the filesystem. The db_root is the path on the + # filesystem to store documents in (can be relative, default "./db") + db_root: db + + # Redis connection settings + redis_host: localhost + redis_port: 6379 + redis_db: 0 + redis_prefix: "rophako:" + + ### + # Security Settings + ### + security: + # Set this value to true to force SSL/TLS on your web app. Turning this on + # will do the following: + # - Send HTTP Strict-Transport-Security header + # - Use secure session cookies (SSL-only) + force_ssl: true + + # Secret key used for session cookie signing. Make this long and hard to + # guess. + # + # Tips for creating a strong secret key: + # $ python + # >>> import os + # >>> os.urandom(24) + # '\xfd{H\xe5<\x95\xf9\xe3\x96.5\xd1\x01O + + ### + # Plugin Configurations + ### + emoticons: + # Emoticon theme used for blog posts and comments. Should exist at the URL + # "/static/smileys" from your document root, and have a file named + # "emoticons.json" inside. If you add a custom theme to your private site + # folder, then also change the root_private to look there instead. + theme: tango + root_private: "{basedir}/rophako/www/static/smileys" + + blog: + default_category: Uncategorized + default_privacy: public + time_format: *DATE_FORMAT + allow_comments: true + entries_per_page: 5 + + # RSS feed settings + title: Rophako CMS Blog + link: http://rophako.kirsle.net/ + language: en + description: The web blog of the Rophako CMS. + copyright: "Copyright {year}" + webmaster: *ADMIN_EMAIL + image_title: Rophako CMS Blog + image_url: https://www.kirsle.net/static/avatars/default.png + image_width: 100 + image_height: 100 + image_description: Rophako CMS + entries_per_feed: 5 + + photo: + # The path to where the uploaded photos will be stored. + # The PRIVATE path is from the perspective of the server file system. + # The PUBLIC path is from the perspective of the web browser via HTTP. + root_private: "{basedir}/site/www/static/photos" + root_public: /static/photos + default_album: My Photos + time_format: *DATE_FORMAT + + # Max widths for photo sizes + width_large: 800 + width_thumb: 256 + width_avatar: 96 + + comment: + time_format: *DATE_FORMAT + # We use Gravatar for comments if the user provides an e-mail address. + # Specify the URL to a fallback image to use in case they don't have + # a gravatar. + default_avatar: + + wiki: + default_page: Main Page + time_format: *DATE_FORMAT + + ### + # List of Enabled Plugins + ### + + # Which plugins to enable? List each plugin by module name. The plugins + # will be assumed to be blueprints that can be attached to the main app + # object. If you instead want to load an arbitrary Python module (i.e. to + # define custom routes at the app layer, not in a blueprint) list those + # under the "custom" section. + blueprints: + - rophako.modules.blog + - rophako.modules.wiki + - rophako.modules.photo + - rophako.modules.comment + - rophako.modules.emoticons + - rophako.modules.contact + + # If adding custom scripts, remove the empty array and define a list like + # in the above blueprints example. + custom: [] diff --git a/requirements.txt b/requirements.txt index 2787c6c..3fa57f0 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ Markdown Pygments attrdict gunicorn +yamlsettings diff --git a/rophako/settings.py b/rophako/settings.py index 1c3b2a5..cbb2fe9 100644 --- a/rophako/settings.py +++ b/rophako/settings.py @@ -5,58 +5,49 @@ import os import datetime from attrdict import AttrDict from ConfigParser import ConfigParser +from yamlsettings import YamlSettings from rophako.plugin import load_plugin # Get the base directory of the git root. basedir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.path.pardir)) -# https://github.com/bcj/AttrDict/issues/20 -if not hasattr(AttrDict, "copy"): - setattr(AttrDict, "copy", lambda self: self._mapping.copy()) - class ConfigHandler(object): settings = None def load_settings(self): - """Load the settings files and make them available in the global config.""" - self.settings = ConfigParser(dict_type=AttrDict) + """Load the settings and make them available in the global config.""" + settings_file = os.environ.get("ROPHAKO_SETTINGS", "settings.yml") + project_settings = YamlSettings("defaults.yml", settings_file, + default_section="rophako") + self.settings = project_settings.get_settings() - # Set dynamic default variables. - self.settings.set("DEFAULT", "_basedir", basedir) - self.settings.set("DEFAULT", "_year", str(datetime.datetime.now().strftime("%Y"))) - - # Read the defaults and then apply the custom settings on top. - settings_file = os.environ.get("ROPHAKO_SETTINGS", "settings.ini") - self.settings.read(["defaults.ini", settings_file]) + # Extrapolate {basedir} in certain keys. + # TODO: find a better way... + self.site.site_root = self.site.site_root.format(basedir=basedir) + self.emoticons.root_private = self.emoticons.root_private.format( + basedir=basedir + ) + self.photo.root_private = self.photo.root_private.format(basedir=basedir) def print_settings(self): - """Pretty-print the contents of the configuration as JSON.""" - for section in self.settings.sections(): - print "[{}]".format(section) - for opt in self.settings.options(section): - print "{} = {}".format(opt, repr(self.settings.get(section, opt))) - print "" + """Pretty-print the contents of the configuration.""" + print self.settings def load_plugins(self): """Load all the plugins specified by the config file.""" - for plugin in self.plugins.blueprints.split("\n"): + for plugin in self.blueprints: plugin = plugin.strip() - if not plugin: - continue + if not plugin: continue load_plugin(plugin) - for custom in self.plugins.custom.split("\n"): + for custom in self.custom: custom = custom.strip() - if not custom: - continue + if not custom: continue load_plugin(custom, as_blueprint=False) def __getattr__(self, section): - """Attribute access for the config object. - - You can access config settings via Config.
., for example - Config.site.notify_email and Config.blog.posts_per_page. All results are - returned as strings per ConfigParser, so cast them if you need to.""" - return AttrDict(dict(self.settings.items(section))) + """Attribute accessor for the config object. Acts as a simple pass-thru + to YamlSettings.""" + return getattr(self.settings, section) Config = ConfigHandler()