Permalink
Browse files

Merge branch 'release/0.6'

  • Loading branch information...
2 parents 01f2d55 + d6219be commit 0c9835f95b44db1b3eb1a9409f95a3ecd63b8ff5 @jezdez jezdez committed Jan 28, 2013
Showing with 135 additions and 93 deletions.
  1. +0 −3 .gitignore
  2. +35 −0 .travis.yml
  3. +5 −1 AUTHORS
  4. +1 −1 LICENSE
  5. +5 −1 README.rst
  6. +1 −1 appconf/__init__.py
  7. +16 −6 appconf/base.py
  8. +2 −7 appconf/test_settings.py
  9. +1 −1 appconf/tests/models.py
  10. +32 −5 appconf/tests/tests.py
  11. +12 −0 docs/changelog.rst
  12. +1 −1 docs/conf.py
  13. +8 −0 docs/reference.rst
  14. +7 −1 docs/usage.rst
  15. +3 −0 requirements/tests.txt
  16. +6 −1 setup.py
  17. +0 −64 tox.ini
View
@@ -3,8 +3,5 @@ dist
MANIFEST
*.pyc
*.egg-info
-.tox/
*.egg
docs/_build/
-reports/
-.tox
View
@@ -0,0 +1,35 @@
+language: python
+python:
+ - "2.5"
+ - "2.6"
+ - "2.7"
+ - "3.2"
+before_install:
+ - export PIP_USE_MIRRORS=true
+ - export PIP_INDEX_URL=https://simple.crate.io/
+ - export DJANGO_SETTINGS_MODULE=appconf.test_settings
+install:
+ - pip install -e .
+ - pip install https://github.com/django/django/archive/${DJANGO}.zip#egg=django
+ - pip install -r requirements/tests.txt
+before_script:
+ - flake8 appconf --ignore=E501
+script:
+ - coverage run --branch --source=appconf `which django-admin.py` test appconf
+ - coverage report --omit=appconf/test*
+env:
+ - DJANGO=1.3.5
+ - DJANGO=1.4.3
+ - DJANGO=1.5b2
+branches:
+ except:
+ - master
+
+matrix:
+ exclude:
+ - python: "2.5"
+ env: DJANGO=1.5b2
+ - python: "3.2"
+ env: DJANGO=1.3.5
+ - python: "3.2"
+ env: DJANGO=1.4.3
View
@@ -1 +1,5 @@
-Jannis Leidel <jannis@leidel.info>
+Christopher Grebs
+Jannis Leidel
+Matthew Tretter
+Rafal Stozek
+Chris Streeter
View
@@ -1,4 +1,4 @@
-Copyright (c) 2011-2012, Jannis Leidel and individual contributors.
+Copyright (c) 2011-2013, Jannis Leidel and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without
View
@@ -1,6 +1,10 @@
django-appconf
==============
+.. image:: https://secure.travis-ci.org/jezdez/django-appconf.png?branch=develop
+ :alt: Build Status
+ :target: http://travis-ci.org/jezdez/django-appconf
+
A helper class for handling configuration defaults of packaged Django
apps gracefully.
@@ -69,7 +73,7 @@ In case you want to use a different settings object instead of the default
holder = 'acme.conf.settings'
If you ship an ``AppConf`` class with your reusable Django app, it's
-recommended to put it in a ``conf.py`` file of you app package and
+recommended to put it in a ``conf.py`` file of your app package and
import ``django.conf.settings`` in it, too::
from django.conf import settings
View
@@ -2,4 +2,4 @@
from .base import AppConf # noqa
# following PEP 386
-__version__ = "0.5"
+__version__ = "0.6"
View
@@ -1,4 +1,6 @@
+from django.core.exceptions import ImproperlyConfigured
import sys
+import six
from .utils import import_attribute
@@ -9,6 +11,7 @@ def __init__(self, meta, prefix=None):
self.holder_path = getattr(meta, 'holder', 'django.conf.settings')
self.holder = import_attribute(self.holder_path)
self.proxy = getattr(meta, 'proxy', False)
+ self.required = getattr(meta, 'required', [])
self.configured_data = {}
def prefixed_name(self, name):
@@ -57,7 +60,7 @@ def __new__(cls, name, bases, attrs):
new_class._meta.configured_data.update(
parent._meta.configured_data)
- for name in filter(lambda name: name == name.upper(), attrs):
+ for name in filter(str.isupper, list(attrs.keys())):
prefixed_name = new_class._meta.prefixed_name(name)
new_class._meta.names[name] = prefixed_name
new_class._meta.defaults[prefixed_name] = attrs.pop(name)
@@ -67,10 +70,18 @@ def __new__(cls, name, bases, attrs):
new_class.add_to_class(name, value)
new_class._configure()
- for name, value in new_class._meta.configured_data.iteritems():
+ for name, value in six.iteritems(new_class._meta.configured_data):
prefixed_name = new_class._meta.prefixed_name(name)
setattr(new_class._meta.holder, prefixed_name, value)
new_class.add_to_class(name, value)
+
+ # Confirm presence of required settings.
+ for name in new_class._meta.required:
+ prefixed_name = new_class._meta.prefixed_name(name)
+ if not hasattr(new_class._meta.holder, prefixed_name):
+ raise ImproperlyConfigured('The required setting %s is'
+ ' missing.' % prefixed_name)
+
return new_class
def add_to_class(cls, name, value):
@@ -82,7 +93,7 @@ def add_to_class(cls, name, value):
def _configure(cls):
# the ad-hoc settings class instance used to configure each value
obj = cls()
- for name, prefixed_name in obj._meta.names.iteritems():
+ for name, prefixed_name in six.iteritems(obj._meta.names):
default_value = obj._meta.defaults.get(prefixed_name)
value = getattr(obj._meta.holder, prefixed_name, default_value)
callback = getattr(obj, "configure_%s" % name.lower(), None)
@@ -92,15 +103,14 @@ def _configure(cls):
cls._meta.configured_data = obj.configure()
-class AppConf(object):
+class AppConf(six.with_metaclass(AppConfMetaClass)):
"""
An app setting object to be used for handling app setting defaults
gracefully and providing a nice API for them.
"""
- __metaclass__ = AppConfMetaClass
def __init__(self, **kwargs):
- for name, value in kwargs.iteritems():
+ for name, value in six.iteritems(kwargs):
setattr(self, name, value)
def __dir__(self):
@@ -12,13 +12,8 @@
'django.contrib.sites',
'django.contrib.auth',
'django.contrib.admin',
- 'django_jenkins',
'appconf.tests',
]
-JENKINS_TASKS = (
- 'django_jenkins.tasks.run_pyflakes',
- 'django_jenkins.tasks.run_pep8',
- 'django_jenkins.tasks.with_coverage',
- 'django_jenkins.tasks.django_tests',
-)
+TEST_RUNNER = 'discover_runner.DiscoverRunner'
+SECRET_KEY = 'local'
@@ -2,7 +2,7 @@
class CustomHolder(object):
- pass
+ HOLDER_VALUE = True
custom_holder = CustomHolder()
@@ -1,10 +1,11 @@
from __future__ import absolute_import
from django.conf import settings
+from django.core.exceptions import ImproperlyConfigured
from django.test import TestCase
-from .models import (TestConf, PrefixConf, YetAnotherPrefixConf,
- SeparateConf, ProxyConf, CustomHolderConf,
- custom_holder)
+from appconf.tests.models import (AppConf, TestConf, PrefixConf,
+ YetAnotherPrefixConf, SeparateConf, ProxyConf,
+ CustomHolderConf, custom_holder)
class TestConfTests(TestCase):
@@ -49,14 +50,15 @@ def test_proxy(self):
def test_dir_members(self):
custom_conf = TestConf()
self.assertTrue('TESTS_SIMPLE_VALUE' in dir(settings))
- self.assertTrue('TESTS_SIMPLE_VALUE' in settings.__members__)
+ if hasattr(settings, '__members__'): # django 1.5 removed __members__
+ self.assertTrue('TESTS_SIMPLE_VALUE' in settings.__members__)
self.assertTrue('SIMPLE_VALUE' in dir(custom_conf))
self.assertTrue('SIMPLE_VALUE' in custom_conf.__members__)
self.assertFalse('TESTS_SIMPLE_VALUE' in dir(custom_conf))
self.assertFalse('TESTS_SIMPLE_VALUE' in custom_conf.__members__)
def test_custom_holder(self):
- custom_conf = CustomHolderConf()
+ CustomHolderConf()
self.assertTrue(hasattr(custom_holder, 'CUSTOM_HOLDER_SIMPLE_VALUE'))
self.assertEquals(custom_holder.CUSTOM_HOLDER_SIMPLE_VALUE, True)
@@ -115,3 +117,28 @@ def test_prefix(self):
def test_simple(self):
self.assertTrue(hasattr(settings, 'PREFIX_SEPARATE_VALUE'))
self.assertEquals(settings.PREFIX_SEPARATE_VALUE, True)
+
+
+class RequiredSettingsTests(TestCase):
+
+ def create_invalid_conf(self):
+ class RequirementConf(AppConf):
+ class Meta:
+ required = ['NOT_PRESENT']
+
+ def test_value_is_defined(self):
+ class RequirementConf(AppConf):
+ class Meta:
+ holder = 'appconf.tests.models.custom_holder'
+ prefix = 'holder'
+ required = ['VALUE']
+
+ def test_default_is_defined(self):
+ class RequirementConf(AppConf):
+ SIMPLE_VALUE = True
+
+ class Meta:
+ required = ['SIMPLE_VALUE']
+
+ def test_missing(self):
+ self.assertRaises(ImproperlyConfigured, self.create_invalid_conf)
View
@@ -1,6 +1,18 @@
Changelog
=========
+0.6 (2013-01-28)
+----------------
+
+* Added ``required`` attribute to ``Meta`` to be able to specify which
+ settings are required to be set.
+
+* Moved to Travis for the tests: http://travis-ci.org/jezdez/django-appconf
+
+* Stopped support for Django 1.2.X.
+
+* Introduced support for Python >= 3.2.
+
0.5 (2012-02-20)
----------------
View
@@ -41,7 +41,7 @@
# General information about the project.
project = u'django-appconf'
-copyright = u'2011-2012, Jannis Leidel and individual contributors'
+copyright = u'2011-2013, Jannis Leidel and individual contributors'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
View
@@ -52,6 +52,7 @@ Reference
class Meta:
proxy = False
prefix = 'myapp'
+ required = ['SETTING_3', 'SETTING_4']
holder = 'django.conf.settings'
.. attribute:: prefix
@@ -63,6 +64,13 @@ Reference
For example, ``acme`` would turn into settings like
``ACME_SETTING_1``.
+ .. attribute:: required
+
+ A list of settings that must be defined. If any of the specified
+ settings are not defined, ``ImproperlyConfigured`` will be raised.
+
+ .. versionadded:: 0.6
+
.. attribute:: holder
The global settings holder to use when looking for overrides and
View
@@ -43,7 +43,7 @@ simply pass the value when instantiating the ``AppConf`` class::
myapp_settings = MyAppConf(SETTING_1='something completely different')
if 'different' in myapp_settings.SETTINGS_1:
- print 'yay, I'm different!'
+ print "yay, I'm different!"
Custom configuration
--------------------
@@ -93,3 +93,9 @@ is provided in the setting instance::
enabled = self.configured_data['ENABLED']
if not enabled and mode != 'development':
print "WARNING: app not enabled in %s mode!" % mode
+ return self.configured_data
+
+.. note::
+
+ Don't forget to return the configured data in your custom ``configure``
+ method if you edit it.
@@ -0,0 +1,3 @@
+flake8
+coverage
+django-discover-runner==0.3
View
@@ -6,7 +6,7 @@
def read(*parts):
file_path = path.join(path.dirname(__file__), *parts)
- return open(file_path).read()
+ return codecs.open(file_path, encoding='utf-8').read()
def find_version(*parts):
@@ -32,6 +32,9 @@ def find_version(*parts):
'appconf',
'appconf.tests',
],
+ install_requires=[
+ 'six'
+ ],
classifiers=[
'Development Status :: 4 - Beta',
'Environment :: Web Environment',
@@ -43,6 +46,8 @@ def find_version(*parts):
'Programming Language :: Python :: 2.5',
'Programming Language :: Python :: 2.6',
'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3.2',
+ 'Programming Language :: Python :: 3.3',
'Topic :: Utilities',
],
)
Oops, something went wrong. Retry.

0 comments on commit 0c9835f

Please sign in to comment.