Created
December 6, 2013 16:27
-
-
Save benoitbryon/7827678 to your computer and use it in GitHub Desktop.
Experimental utilities about local settings management with Django.
Inspired by django-configglue... but using colander.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# -*- coding: utf-8 -*- | |
"""Helpers to manage local (project-level, environment-level) settings.""" | |
from django.conf import global_settings | |
import colander | |
import json | |
import yaml | |
def settings_from_string_mapping(input): | |
"""Convert mapping of {key: string} to {key: complex type}. | |
Simple key-value stores (flat mappings) are supported: | |
>>> flat_mapping = {'DEBUG': 'True', 'SECRET_KEY': 'not a secret'} | |
>>> output = settings_from_string_mapping(flat_mapping) | |
>>> output == flat_mapping | |
True | |
Values can be complex types (sequences, mappings) using JSON or YAML. | |
Keys using ".json" or ".yaml" suffix are automatically decoded: | |
>>> nested_mapping = { | |
... 'DATABASES.yaml': 'ENGINE: sqlite3', | |
... } | |
>>> output = settings_from_string_mapping(nested_mapping) | |
>>> output['DATABASES'] == {'ENGINE': 'sqlite3'} | |
True | |
""" | |
output = {} | |
for key, value in input.iteritems(): | |
if key.endswith('.json'): | |
output[key[:-5]] = json.loads(value) | |
elif key.endswith('.yaml'): | |
output[key[:-5]] = yaml.load(value) | |
else: | |
output[key] = value | |
return output | |
def settings_from_file(file_obj): | |
"""Return mapping from filename. | |
Supported file formats are JSON and YAML. The lowercase extension is used | |
to guess the file type. | |
>>> from StringIO import StringIO | |
>>> file_obj = StringIO('SOME_LIST: [a, b, c]') | |
>>> file_obj.name = 'something.yaml' | |
>>> settings_from_file(file_obj) == { | |
... 'SOME_LIST': ['a', 'b', 'c'], | |
... } | |
True | |
""" | |
file_name = file_obj.name | |
if file_name.endswith('.yaml'): | |
return yaml.load(file_obj) | |
elif file_name.endswith('.json'): | |
return json.load(file_obj) | |
else: | |
raise ValueError( | |
'Cannot guess format of configuration file "{name}". ' | |
'Expected one of these extensions: "{extensions}".'.format( | |
name=file_name, | |
extensions='", "'.join('.yaml', '.json'))) | |
def settings_from_module(module_path): | |
"""Import settings from module's globals and return them as a dict. | |
>>> settings = settings_from_module('django.conf.global_settings') | |
>>> settings['DATABASES'] | |
{} | |
>>> '__name__' in settings | |
False | |
""" | |
module = __import__(module_path, fromlist='*', level=0) | |
is_uppercase = lambda x: x.upper() == x | |
is_special = lambda x: x.startswith('_') | |
return dict([(key, value) for key, value in module.__dict__.items() | |
if is_uppercase(key) and not is_special(key)]) | |
class Django155ConfigurationSchema(colander.MappingSchema): | |
"""Schema for Django 1.5.5 built-in settings.""" | |
ABSOLUTE_URL_OVERRIDES = colander.SchemaNode( | |
colander.Mapping(unknown='preserve'), | |
missing=None, | |
) | |
ADMIN_FOR = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
ADMINS = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode( | |
colander.Tuple(), | |
*[ | |
colander.SchemaNode(colander.String()), | |
colander.SchemaNode( | |
colander.String(), | |
validator=colander.Email(), | |
), | |
] | |
), | |
] | |
) | |
ALLOWED_HOSTS = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
ALLOWED_INCLUDE_ROOTS = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
APPEND_SLASH = colander.SchemaNode( | |
colander.Boolean(), | |
missing=None, | |
) | |
AUTHENTICATION_BACKENDS = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
AUTH_USER_MODEL = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
# The settings above have been from Django documentation... | |
# ... The following settings are settings which are required in project. | |
# The list is not complete yet. | |
# The idea is to complete the list above, moving items below and creating | |
# new items. | |
DATABASES = colander.SchemaNode( | |
colander.Mapping(unknown='preserve'), | |
missing=colander.required, | |
*[ | |
colander.SchemaNode( | |
colander.Mapping(unknown='raise'), | |
name='default', | |
missing=None, | |
*[ | |
colander.SchemaNode( | |
colander.String(), | |
name='ENGINE', | |
missing=colander.required, | |
default='', | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='HOST', | |
missing='', | |
default='', | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='NAME', | |
missing=colander.required, | |
default='', | |
), | |
colander.SchemaNode( | |
colander.Mapping(unknown='preserve'), | |
name='OPTIONS', | |
missing={}, | |
default={}, | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='PASSWORD', | |
missing='', | |
default='', | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='PORT', | |
missing='', | |
default='', | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='USER', | |
missing='', | |
default='', | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_CHARSET', | |
missing=None, | |
default=None, | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_COLLATION', | |
missing=None, | |
default=None, | |
), | |
colander.SchemaNode( | |
colander.Sequence(), | |
name='TEST_DEPENDENCIES', | |
missing=['default'], | |
default=['default'], | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_MIRROR', | |
missing=None, | |
default=None, | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_NAME', | |
missing=None, | |
default=None, | |
), | |
colander.SchemaNode( | |
colander.Boolean(), | |
name='TEST_CREATE', | |
missing=True, | |
default=True, | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_USER', | |
missing=None, | |
default=None, | |
), | |
colander.SchemaNode( | |
colander.Boolean(), | |
name='TEST_USER_CREATE', | |
missing=True, | |
default=True, | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_PASSWD', | |
missing=None, | |
default=None, | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_TBLSPACE', | |
missing=None, | |
default=None, | |
), | |
colander.SchemaNode( | |
colander.String(), | |
name='TEST_TBLSPACE_TMP', | |
missing=None, | |
default=None, | |
), | |
] | |
), | |
] | |
) | |
DEBUG = colander.SchemaNode( | |
colander.Boolean(), | |
missing=None, | |
) | |
DEFAULT_FROM_EMAIL = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
validator=colander.Email(), | |
) | |
INTERNAL_IPS = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
INSTALLED_APPS = colander.SchemaNode( | |
colander.Sequence(), | |
missing=colander.required, | |
default=colander.null, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
LANGUAGE_CODE = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
LANGUAGES = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode( | |
colander.Tuple(), | |
*[ | |
colander.SchemaNode(colander.String()), | |
colander.SchemaNode(colander.String()), | |
] | |
), | |
] | |
) | |
LOGIN_URL = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
MEDIA_ROOT = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
MEDIA_URL = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
MIDDLEWARE_CLASSES = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
ROOT_URLCONF = colander.SchemaNode( | |
colander.String(), | |
missing=colander.required, | |
default=colander.null, | |
) | |
SECRET_KEY = colander.SchemaNode( | |
colander.String(), | |
missing=colander.required, | |
default=colander.null, | |
) | |
STATIC_ROOT = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
STATICFILES_STORAGE = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
STATIC_URL = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
TEMPLATE_CONTEXT_PROCESSORS = colander.SchemaNode( | |
colander.Sequence(), | |
missing=None, | |
*[ | |
colander.SchemaNode(colander.String()), | |
] | |
) | |
TEMPLATE_DEBUG = colander.SchemaNode( | |
colander.Boolean(), | |
missing=None, | |
) | |
TIME_ZONE = colander.SchemaNode( | |
colander.String(), | |
missing=None, | |
) | |
USE_I18N = colander.SchemaNode( | |
colander.Boolean(), | |
missing=None, | |
) | |
USE_L10N = colander.SchemaNode( | |
colander.Boolean(), | |
missing=None, | |
) | |
USE_TZ = colander.SchemaNode( | |
colander.Boolean(), | |
missing=None, | |
) | |
#: Schema for Django settings (current/installed version).""" | |
DjangoConfigurationSchema = Django155ConfigurationSchema | |
# Automatically assign default values from django.conf.global_settings. | |
for child in DjangoConfigurationSchema(): | |
if not child.default is colander.null: | |
child.default = getattr(global_settings, child.name) | |
if child.missing is None: | |
child.missing = getattr(global_settings, child.name) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment