Moved settings handling over to django-appconf and fixed coverage.

This commit is contained in:
Jannis Leidel
2011-08-25 17:51:47 +02:00
parent fc5c2dd7ba
commit c16e6a8df2
25 changed files with 90 additions and 162 deletions

View File

@@ -2,12 +2,13 @@ from __future__ import with_statement
import os import os
import codecs import codecs
from django.conf import settings
from django.core.files.base import ContentFile from django.core.files.base import ContentFile
from django.template.loader import render_to_string from django.template.loader import render_to_string
from django.utils.encoding import smart_unicode from django.utils.encoding import smart_unicode
from compressor.cache import get_hexdigest, get_mtime from compressor.cache import get_hexdigest, get_mtime
from compressor.conf import settings
from compressor.exceptions import CompressorError, UncompressableFileError from compressor.exceptions import CompressorError, UncompressableFileError
from compressor.filters import CompilerFilter from compressor.filters import CompilerFilter
from compressor.storage import default_storage from compressor.storage import default_storage

View File

@@ -2,12 +2,12 @@ import os
import socket import socket
import time import time
from django.conf import settings
from django.core.cache import get_cache from django.core.cache import get_cache
from django.utils.encoding import smart_str from django.utils.encoding import smart_str
from django.utils.hashcompat import md5_constructor from django.utils.hashcompat import md5_constructor
from django.utils.importlib import import_module from django.utils.importlib import import_module
from compressor.conf import settings
from compressor.utils import get_mod_func from compressor.utils import get_mod_func

View File

@@ -1,4 +1,5 @@
from compressor.conf import settings from django.conf import settings
from compressor.base import Compressor, SOURCE_HUNK, SOURCE_FILE from compressor.base import Compressor, SOURCE_HUNK, SOURCE_FILE
from compressor.exceptions import UncompressableFileError from compressor.exceptions import UncompressableFileError

View File

@@ -3,11 +3,11 @@ import os
import logging import logging
import subprocess import subprocess
import tempfile import tempfile
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from django.core.files.temp import NamedTemporaryFile from django.core.files.temp import NamedTemporaryFile
from django.utils.importlib import import_module from django.utils.importlib import import_module
from compressor.conf import settings
from compressor.exceptions import FilterError from compressor.exceptions import FilterError
from compressor.utils import get_mod_func from compressor.utils import get_mod_func
from compressor.utils.stringformat import FormattableString as fstr from compressor.utils.stringformat import FormattableString as fstr

View File

@@ -1,4 +1,5 @@
from compressor.conf import settings from django.conf import settings
from compressor.filters import CompilerFilter from compressor.filters import CompilerFilter

View File

@@ -2,8 +2,9 @@ import os
import re import re
import posixpath import posixpath
from django.conf import settings
from compressor.cache import get_hexdigest, get_hashed_mtime from compressor.cache import get_hexdigest, get_hashed_mtime
from compressor.conf import settings
from compressor.filters import FilterBase from compressor.filters import FilterBase
from compressor.utils import staticfiles from compressor.utils import staticfiles

View File

@@ -1,4 +1,5 @@
from compressor.conf import settings from django.conf import settings
from compressor.filters import CompilerFilter from compressor.filters import CompilerFilter

View File

@@ -3,7 +3,8 @@ import re
import mimetypes import mimetypes
from base64 import b64encode from base64 import b64encode
from compressor.conf import settings from django.conf import settings
from compressor.filters import FilterBase from compressor.filters import FilterBase

View File

@@ -1,4 +1,5 @@
from compressor.conf import settings from django.conf import settings
from compressor.filters import CompilerFilter from compressor.filters import CompilerFilter

View File

@@ -1,4 +1,5 @@
from compressor.conf import settings from django.conf import settings
from compressor.base import Compressor, SOURCE_HUNK, SOURCE_FILE from compressor.base import Compressor, SOURCE_HUNK, SOURCE_FILE
from compressor.exceptions import UncompressableFileError from compressor.exceptions import UncompressableFileError

View File

@@ -9,6 +9,7 @@ try:
except ImportError: except ImportError:
from StringIO import StringIO from StringIO import StringIO
from django.conf import settings
from django.core.management.base import NoArgsCommand, CommandError from django.core.management.base import NoArgsCommand, CommandError
from django.template import Context, Template, TemplateDoesNotExist, TemplateSyntaxError from django.template import Context, Template, TemplateDoesNotExist, TemplateSyntaxError
from django.utils.datastructures import SortedDict from django.utils.datastructures import SortedDict
@@ -22,7 +23,6 @@ except ImportError:
CachedLoader = None CachedLoader = None
from compressor.cache import cache, get_offline_cachekey from compressor.cache import cache, get_offline_cachekey
from compressor.conf import settings
from compressor.exceptions import OfflineGenerationError from compressor.exceptions import OfflineGenerationError
from compressor.templatetags.compress import CompressorNode from compressor.templatetags.compress import CompressorNode
from compressor.utils import walk, any from compressor.utils import walk, any

View File

@@ -2,10 +2,10 @@ import fnmatch
import os import os
from optparse import make_option from optparse import make_option
from django.conf import settings
from django.core.management.base import NoArgsCommand, CommandError from django.core.management.base import NoArgsCommand, CommandError
from compressor.cache import cache, get_mtime, get_mtime_cachekey from compressor.cache import cache, get_mtime, get_mtime_cachekey
from compressor.conf import settings
from compressor.utils import walk from compressor.utils import walk

View File

@@ -1,11 +1,12 @@
import os import os
from django import VERSION as DJANGO_VERSION from django import VERSION as DJANGO_VERSION
from django.conf import settings as global_settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from compressor.utils.settings import AppSettings from appconf import AppConf
class CompressorSettings(AppSettings):
class CompressorConf(AppConf):
# Main switch # Main switch
ENABLED = False ENABLED = False
# Allows changing verbosity from the settings. # Allows changing verbosity from the settings.
@@ -60,14 +61,17 @@ class CompressorSettings(AppSettings):
# The context to be used when compressing the files "offline" # The context to be used when compressing the files "offline"
OFFLINE_CONTEXT = {} OFFLINE_CONTEXT = {}
class Meta:
prefix = 'compress'
def configure_enabled(self, value): def configure_enabled(self, value):
return value or not global_settings.DEBUG return value or not settings.DEBUG
def configure_root(self, value): def configure_root(self, value):
if value is None: if value is None:
value = getattr(global_settings, 'STATIC_ROOT', None) value = getattr(settings, 'STATIC_ROOT', None)
if not value: if not value:
value = global_settings.MEDIA_ROOT value = settings.MEDIA_ROOT
if not value: if not value:
raise ImproperlyConfigured( raise ImproperlyConfigured(
"The COMPRESS_ROOT setting must be set.") "The COMPRESS_ROOT setting must be set.")
@@ -76,9 +80,9 @@ class CompressorSettings(AppSettings):
def configure_url(self, value): def configure_url(self, value):
# Uses Django 1.3's STATIC_URL by default or falls back to MEDIA_URL # Uses Django 1.3's STATIC_URL by default or falls back to MEDIA_URL
if value is None: if value is None:
value = getattr(global_settings, "STATIC_URL", None) value = getattr(settings, "STATIC_URL", None)
if not value: if not value:
value = global_settings.MEDIA_URL value = settings.MEDIA_URL
if not value.endswith("/"): if not value.endswith("/"):
raise ImproperlyConfigured("The URL settings (e.g. COMPRESS_URL) " raise ImproperlyConfigured("The URL settings (e.g. COMPRESS_URL) "
"must have a trailing slash.") "must have a trailing slash.")
@@ -87,11 +91,11 @@ class CompressorSettings(AppSettings):
def configure_cache_backend(self, value): def configure_cache_backend(self, value):
if value is None: if value is None:
# If we are on Django 1.3 AND using the new CACHES setting... # If we are on Django 1.3 AND using the new CACHES setting...
if DJANGO_VERSION[:2] >= (1, 3) and hasattr(global_settings, "CACHES"): if DJANGO_VERSION[:2] >= (1, 3) and hasattr(settings, "CACHES"):
value = "default" value = "default"
else: else:
# falling back to the old CACHE_BACKEND setting # falling back to the old CACHE_BACKEND setting
value = getattr(global_settings, "CACHE_BACKEND", None) value = getattr(settings, "CACHE_BACKEND", None)
if not value: if not value:
raise ImproperlyConfigured( raise ImproperlyConfigured(
"Please specify a cache backend in your settings.") "Please specify a cache backend in your settings.")
@@ -100,11 +104,11 @@ class CompressorSettings(AppSettings):
def configure_offline_context(self, value): def configure_offline_context(self, value):
if not value: if not value:
value = { value = {
"MEDIA_URL": global_settings.MEDIA_URL, "MEDIA_URL": settings.MEDIA_URL,
} }
# Adds the 1.3 STATIC_URL setting to the context if available # Adds the 1.3 STATIC_URL setting to the context if available
if getattr(global_settings, "STATIC_URL", None): if getattr(settings, "STATIC_URL", None):
value["STATIC_URL"] = global_settings.STATIC_URL value["STATIC_URL"] = settings.STATIC_URL
return value return value
def configure_precompilers(self, value): def configure_precompilers(self, value):
@@ -112,5 +116,3 @@ class CompressorSettings(AppSettings):
raise ImproperlyConfigured("The COMPRESS_PRECOMPILERS setting " raise ImproperlyConfigured("The COMPRESS_PRECOMPILERS setting "
"must be a list or tuple. Check for missing commas.") "must be a list or tuple. Check for missing commas.")
return value return value
settings = CompressorSettings(prefix="COMPRESS")

View File

@@ -2,11 +2,10 @@ import gzip
from os import path from os import path
from datetime import datetime from datetime import datetime
from django.conf import settings
from django.core.files.storage import FileSystemStorage, get_storage_class from django.core.files.storage import FileSystemStorage, get_storage_class
from django.utils.functional import LazyObject from django.utils.functional import LazyObject
from compressor.conf import settings
class CompressorFileStorage(FileSystemStorage): class CompressorFileStorage(FileSystemStorage):
""" """

View File

@@ -1,9 +1,9 @@
from django import template from django import template
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
from compressor.cache import (cache, cache_get, cache_set, from compressor.cache import (cache, cache_get, cache_set,
get_offline_cachekey, get_templatetag_cachekey) get_offline_cachekey, get_templatetag_cachekey)
from compressor.conf import settings
from compressor.utils import get_class from compressor.utils import get_class
register = template.Library() register = template.Library()

View File

@@ -1,107 +0,0 @@
from inspect import getmembers
from django.conf import settings
class AppSettings(object):
"""
An app setting object to be used for handling app setting defaults
gracefully and providing a nice API for them. Say you have an app
called ``myapp`` and want to define a few defaults, and refer to the
defaults easily in the apps code. Add a ``settings.py`` to your app::
from path.to.utils import AppSettings
class MyAppSettings(AppSettings):
SETTING_1 = "one"
SETTING_2 = (
"two",
)
Then initialize the setting with the correct prefix in the location of
of your choice, e.g. ``conf.py`` of the app module::
settings = MyAppSettings(prefix="MYAPP")
The ``MyAppSettings`` instance will automatically look at Django's
global setting to determine each of the settings and respect the
provided ``prefix``. E.g. adding this to your site's ``settings.py``
will set the ``SETTING_1`` setting accordingly::
MYAPP_SETTING_1 = "uno"
Usage
-----
Instead of using ``from django.conf import settings`` as you would
usually do, you can switch to using your apps own settings module
to access the app settings::
from myapp.conf import settings
print myapp_settings.MYAPP_SETTING_1
``AppSettings`` instances also work as pass-throughs for other
global settings that aren't related to the app. For example the
following code is perfectly valid::
from myapp.conf import settings
if "myapp" in settings.INSTALLED_APPS:
print "yay, myapp is installed!"
Custom handling
---------------
Each of the settings can be individually configured with callbacks.
For example, in case a value of a setting depends on other settings
or other dependencies. The following example sets one setting to a
different value depending on a global setting::
from django.conf import settings
class MyCustomAppSettings(AppSettings):
ENABLED = True
def configure_enabled(self, value):
return value and not self.DEBUG
custom_settings = MyCustomAppSettings("MYAPP")
The value of ``custom_settings.MYAPP_ENABLED`` will vary depending on the
value of the global ``DEBUG`` setting.
Each of the app settings can be customized by providing
a method ``configure_<lower_setting_name>`` that takes the default
value as defined in the class attributes as the only parameter.
The method needs to return the value to be use for the setting in
question.
"""
def __dir__(self):
return sorted(list(set(self.__dict__.keys() + dir(settings))))
__members__ = lambda self: self.__dir__()
def __getattr__(self, name):
if name.startswith(self._prefix):
raise AttributeError("%r object has no attribute %r" %
(self.__class__.__name__, name))
return getattr(settings, name)
def __setattr__(self, name, value):
super(AppSettings, self).__setattr__(name, value)
if name in dir(settings):
setattr(settings, name, value)
def __init__(self, prefix):
super(AppSettings, self).__setattr__('_prefix', prefix)
for name, value in filter(self.issetting, getmembers(self.__class__)):
prefixed_name = "%s_%s" % (prefix.upper(), name.upper())
value = getattr(settings, prefixed_name, value)
callback = getattr(self, "configure_%s" % name.lower(), None)
if callable(callback):
value = callback(value)
delattr(self.__class__, name)
setattr(self, prefixed_name, value)
def issetting(self, (name, value)):
return name == name.upper()

View File

@@ -121,4 +121,7 @@ setup(
'Topic :: Internet :: WWW/HTTP', 'Topic :: Internet :: WWW/HTTP',
], ],
zip_safe = False, zip_safe = False,
install_requires=[
'django-appconf >= 0.4',
],
) )

24
tests/settings.py Normal file
View File

@@ -0,0 +1,24 @@
import os
TEST_DIR = os.path.dirname(os.path.abspath(__file__))
COMPRESS_CACHE_BACKEND = 'locmem://'
DATABASE_ENGINE = 'sqlite3'
INSTALLED_APPS = [
'django.contrib.contenttypes',
'django.contrib.sites',
'django.contrib.auth',
'django.contrib.admin',
'compressor',
'tests',
]
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(TEST_DIR, 'media')
TEMPLATE_DIRS = (
os.path.join(TEST_DIR, 'templates'),
)

View File

@@ -4,12 +4,11 @@ import re
from BeautifulSoup import BeautifulSoup from BeautifulSoup import BeautifulSoup
from django.conf import settings
from django.core.cache.backends import locmem from django.core.cache.backends import locmem
from django.test import TestCase from django.test import TestCase
from compressor.base import SOURCE_HUNK, SOURCE_FILE from compressor.base import SOURCE_HUNK, SOURCE_FILE
from compressor.cache import get_hexdigest
from compressor.conf import settings
from compressor.css import CssCompressor from compressor.css import CssCompressor
from compressor.js import JsCompressor from compressor.js import JsCompressor
@@ -22,6 +21,7 @@ def css_tag(href, **kwargs):
test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..')) test_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), '..'))
class CompressorTestCase(TestCase): class CompressorTestCase(TestCase):
def setUp(self): def setUp(self):
@@ -81,7 +81,7 @@ class CompressorTestCase(TestCase):
def test_js_split(self): def test_js_split(self):
out = [ out = [
(SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', '<script src="/media/js/one.js" type="text/javascript"></script>'), (SOURCE_FILE, os.path.join(settings.COMPRESS_ROOT, u'js/one.js'), u'js/one.js', '<script src="/media/js/one.js" type="text/javascript"></script>'),
(SOURCE_HUNK, u'obj.value = "value";', None, '<script type="text/javascript">obj.value = "value";</script>') (SOURCE_HUNK, u'obj.value = "value";', None, '<script type="text/javascript">obj.value = "value";</script>'),
] ]
split = self.js_node.split_contents() split = self.js_node.split_contents()
split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split] split = [(x[0], x[1], x[2], self.js_node.parser.elem_str(x[3])) for x in split]
@@ -127,7 +127,6 @@ class CompressorTestCase(TestCase):
settings.COMPRESS_OUTPUT_DIR = old_output_dir settings.COMPRESS_OUTPUT_DIR = old_output_dir
class CssMediaTestCase(TestCase): class CssMediaTestCase(TestCase):
def setUp(self): def setUp(self):
self.css = """\ self.css = """\
@@ -151,8 +150,6 @@ class CssMediaTestCase(TestCase):
self.assertEqual(media, [l.get('media', None) for l in links]) self.assertEqual(media, [l.get('media', None) for l in links])
class VerboseTestCase(CompressorTestCase): class VerboseTestCase(CompressorTestCase):
def setUp(self): def setUp(self):
@@ -165,5 +162,3 @@ class CacheBackendTestCase(CompressorTestCase):
def test_correct_backend(self): def test_correct_backend(self):
from compressor.cache import cache from compressor.cache import cache
self.assertEqual(cache.__class__, locmem.CacheClass) self.assertEqual(cache.__class__, locmem.CacheClass)

View File

@@ -3,10 +3,10 @@ import os
import sys import sys
from unittest2 import skipIf from unittest2 import skipIf
from django.conf import settings
from django.test import TestCase from django.test import TestCase
from compressor.cache import get_hashed_mtime from compressor.cache import get_hashed_mtime
from compressor.conf import settings
from compressor.css import CssCompressor from compressor.css import CssCompressor
from compressor.utils import find_command from compressor.utils import find_command
from compressor.filters.base import CompilerFilter from compressor.filters.base import CompilerFilter

View File

@@ -1,10 +1,10 @@
from __future__ import with_statement from __future__ import with_statement
import os import os
from django.conf import settings
from django.template import Template, Context from django.template import Template, Context
from django.test import TestCase from django.test import TestCase
from compressor.conf import settings
from compressor.management.commands.compress import Command as CompressCommand from compressor.management.commands.compress import Command as CompressCommand
from .base import test_dir, css_tag from .base import test_dir, css_tag

View File

@@ -2,8 +2,6 @@ from __future__ import with_statement
import os import os
from unittest2 import skipIf from unittest2 import skipIf
from BeautifulSoup import BeautifulSoup
try: try:
import lxml import lxml
except ImportError: except ImportError:
@@ -20,7 +18,8 @@ except ImportError:
BeautifulSoup = None BeautifulSoup = None
from compressor.conf import settings from django.conf import settings
from compressor.base import SOURCE_HUNK, SOURCE_FILE from compressor.base import SOURCE_HUNK, SOURCE_FILE
from .base import CompressorTestCase from .base import CompressorTestCase

View File

@@ -1,10 +1,10 @@
from __future__ import with_statement from __future__ import with_statement
from django.conf import settings
from django.core.files.storage import get_storage_class from django.core.files.storage import get_storage_class
from django.test import TestCase from django.test import TestCase
from compressor import base from compressor import base
from compressor.conf import settings
from .base import css_tag from .base import css_tag
from .templatetags import render from .templatetags import render

View File

@@ -1,10 +1,9 @@
from __future__ import with_statement from __future__ import with_statement
from django.conf import settings
from django.template import Template, Context, TemplateSyntaxError from django.template import Template, Context, TemplateSyntaxError
from django.test import TestCase from django.test import TestCase
from compressor.conf import settings
from .base import css_tag from .base import css_tag

View File

@@ -1,12 +1,18 @@
[tox] [tox]
setupdir = .. setupdir = ..
distribute = false distribute = false
downloadcache = {toxinidir}/_download/
[testenv] [testenv]
commands = commands =
{envpython} {toxinidir}/runtests.py [] {envbindir}/coverage erase
coverage html -d {envtmpdir}/coverage {envbindir}/coverage run --branch --include=*compressor* --omit=*test*,*rjsmin*,*cssmin*,*stringformat*,*models* {envbindir}/django-admin.py test {posargs:tests} --settings=tests.settings
{envbindir}/coverage report
{envbindir}/coverage html -d {envtmpdir}
echo "Type the following to open the coverage report: python -m webbrowser -t file://{envtmpdir}/index.html"
setenv =
PYTHONPATH = {toxinidir}/..
downloadcache = {toxworkdir}/_download/
[testenv:docs] [testenv:docs]
changedir = ../docs changedir = ../docs
@@ -20,7 +26,7 @@ commands =
basepython = python2.5 basepython = python2.5
deps = deps =
unittest2 unittest2
BeautifulSoup BeautifulSoup==3.2.0
html5lib html5lib
coverage coverage
django==1.2.5 django==1.2.5
@@ -29,7 +35,7 @@ deps =
basepython = python2.6 basepython = python2.6
deps = deps =
unittest2 unittest2
BeautifulSoup BeautifulSoup==3.2.0
html5lib html5lib
coverage coverage
django==1.2.5 django==1.2.5
@@ -38,7 +44,7 @@ deps =
basepython = python2.7 basepython = python2.7
deps = deps =
unittest2 unittest2
BeautifulSoup BeautifulSoup==3.2.0
html5lib html5lib
coverage coverage
django==1.2.5 django==1.2.5
@@ -48,7 +54,7 @@ deps =
basepython = python2.5 basepython = python2.5
deps = deps =
unittest2 unittest2
BeautifulSoup BeautifulSoup==3.2.0
html5lib html5lib
coverage coverage
django==1.3 django==1.3
@@ -57,7 +63,7 @@ deps =
basepython = python2.6 basepython = python2.6
deps = deps =
unittest2 unittest2
BeautifulSoup BeautifulSoup==3.2.0
html5lib html5lib
coverage coverage
django==1.3 django==1.3
@@ -66,7 +72,7 @@ deps =
basepython = python2.7 basepython = python2.7
deps = deps =
unittest2 unittest2
BeautifulSoup BeautifulSoup==3.2.0
html5lib html5lib
coverage coverage
django==1.3 django==1.3