Applied a few PEP8 style modifications.

This commit is contained in:
Jannis Leidel
2011-03-30 12:08:49 +02:00
parent 015b28cf39
commit 3ef9789589
16 changed files with 294 additions and 252 deletions

View File

@@ -26,8 +26,11 @@ class Compressor(object):
self.precompilers = settings.COMPRESS_PRECOMPILERS self.precompilers = settings.COMPRESS_PRECOMPILERS
def split_contents(self): def split_contents(self):
raise NotImplementedError( """
"split_contents must be defined in a subclass") To be implemented in a subclass, should return an
iterable with three values: kind, value, element
"""
raise NotImplementedError
def get_filename(self, url): def get_filename(self, url):
try: try:
@@ -76,7 +79,8 @@ class Compressor(object):
if kind == "hunk": if kind == "hunk":
# Let's cast BeautifulSoup element to unicode here since # Let's cast BeautifulSoup element to unicode here since
# it will try to encode using ascii internally later # it will try to encode using ascii internally later
yield unicode(self.filter(value, "input", elem=elem, kind=kind)) yield unicode(
self.filter(value, method="input", elem=elem, kind=kind))
elif kind == "file": elif kind == "file":
content = "" content = ""
try: try:
@@ -142,7 +146,6 @@ class Compressor(object):
source_file.close() source_file.close()
return content return content
def filter(self, content, method, **kwargs): def filter(self, content, method, **kwargs):
# run compiler # run compiler
if method == "input": if method == "input":
@@ -160,7 +163,7 @@ class Compressor(object):
@cached_property @cached_property
def combined(self): def combined(self):
return self.filter(self.concat(), 'output') return self.filter(self.concat(), method="output")
@cached_property @cached_property
def hash(self): def hash(self):

View File

@@ -7,18 +7,22 @@ from django.utils.hashcompat import sha_constructor
from compressor.conf import settings from compressor.conf import settings
def get_hexdigest(plaintext): def get_hexdigest(plaintext):
return sha_constructor(plaintext).hexdigest() return sha_constructor(plaintext).hexdigest()
def get_mtime_cachekey(filename): def get_mtime_cachekey(filename):
return "django_compressor.mtime.%s.%s" % (socket.gethostname(), return "django_compressor.mtime.%s.%s" % (socket.gethostname(),
get_hexdigest(filename)) get_hexdigest(filename))
def get_offline_cachekey(source): def get_offline_cachekey(source):
return ("django_compressor.offline.%s.%s" % return ("django_compressor.offline.%s.%s" %
(socket.gethostname(), (socket.gethostname(),
get_hexdigest("".join(smart_str(s) for s in source)))) get_hexdigest("".join(smart_str(s) for s in source))))
def get_mtime(filename): def get_mtime(filename):
if settings.COMPRESS_MTIME_DELAY: if settings.COMPRESS_MTIME_DELAY:
key = get_mtime_cachekey(filename) key = get_mtime_cachekey(filename)
@@ -29,9 +33,11 @@ def get_mtime(filename):
return mtime return mtime
return os.path.getmtime(filename) return os.path.getmtime(filename)
def get_hashed_mtime(filename, length=12): def get_hashed_mtime(filename, length=12):
filename = os.path.realpath(filename) filename = os.path.realpath(filename)
mtime = str(int(get_mtime(filename))) mtime = str(int(get_mtime(filename)))
return get_hexdigest(mtime)[:length] return get_hexdigest(mtime)[:length]
cache = get_cache(settings.COMPRESS_CACHE_BACKEND) cache = get_cache(settings.COMPRESS_CACHE_BACKEND)

View File

@@ -2,12 +2,13 @@ from compressor.conf import settings
from compressor.base import Compressor from compressor.base import Compressor
from compressor.exceptions import UncompressableFileError from compressor.exceptions import UncompressableFileError
class CssCompressor(Compressor): class CssCompressor(Compressor):
template_name = "compressor/css.html"
template_name_inline = "compressor/css_inline.html"
def __init__(self, content=None, output_prefix="css"): def __init__(self, content=None, output_prefix="css"):
super(CssCompressor, self).__init__(content, output_prefix) super(CssCompressor, self).__init__(content, output_prefix)
self.template_name = "compressor/css.html"
self.template_name_inline = "compressor/css_inline.html"
self.filters = list(settings.COMPRESS_CSS_FILTERS) self.filters = list(settings.COMPRESS_CSS_FILTERS)
self.type = 'css' self.type = 'css'
@@ -21,8 +22,8 @@ class CssCompressor(Compressor):
elem_attribs = self.parser.elem_attribs(elem) elem_attribs = self.parser.elem_attribs(elem)
if elem_name == 'link' and elem_attribs['rel'] == 'stylesheet': if elem_name == 'link' and elem_attribs['rel'] == 'stylesheet':
try: try:
content = self.parser.elem_content(elem) data = (
data = ('file', self.get_filename(elem_attribs['href']), elem) 'file', self.get_filename(elem_attribs['href']), elem)
except UncompressableFileError: except UncompressableFileError:
if settings.DEBUG: if settings.DEBUG:
raise raise

View File

@@ -17,12 +17,14 @@ class FilterError(Exception):
""" """
pass pass
class ParserError(Exception): class ParserError(Exception):
""" """
This exception is raised when the parser fails This exception is raised when the parser fails
""" """
pass pass
class OfflineGenerationError(Exception): class OfflineGenerationError(Exception):
""" """
Offline compression generation related exceptions Offline compression generation related exceptions

View File

@@ -8,6 +8,7 @@ from compressor.utils import cmd_split
logger = logging.getLogger("compressor.filters") logger = logging.getLogger("compressor.filters")
class FilterBase(object): class FilterBase(object):
def __init__(self, content, filter_type=None, verbose=0): def __init__(self, content, filter_type=None, verbose=0):

View File

@@ -6,6 +6,7 @@ from base64 import b64encode
from compressor.conf import settings from compressor.conf import settings
from compressor.filters import FilterBase from compressor.filters import FilterBase
class DataUriFilter(FilterBase): class DataUriFilter(FilterBase):
"""Filter for embedding media as data: URIs. """Filter for embedding media as data: URIs.
@@ -28,7 +29,8 @@ class DataUriFilter(FilterBase):
# strip query string of file paths # strip query string of file paths
if "?" in url: if "?" in url:
url = url.split("?")[0] url = url.split("?")[0]
return os.path.join(settings.COMPRESS_ROOT, url[len(settings.COMPRESS_URL):]) return os.path.join(
settings.COMPRESS_ROOT, url[len(settings.COMPRESS_URL):])
def data_uri_converter(self, matchobj): def data_uri_converter(self, matchobj):
url = matchobj.group(1).strip(' \'"') url = matchobj.group(1).strip(' \'"')
@@ -36,7 +38,8 @@ class DataUriFilter(FilterBase):
path = self.get_file_path(url) path = self.get_file_path(url)
if os.stat(path).st_size <= settings.COMPRESS_DATA_URI_MIN_SIZE: if os.stat(path).st_size <= settings.COMPRESS_DATA_URI_MIN_SIZE:
data = b64encode(open(path, 'rb').read()) data = b64encode(open(path, 'rb').read())
return 'url("data:%s;base64,%s")' % (mimetypes.guess_type(path)[0], data) return 'url("data:%s;base64,%s")' % (
mimetypes.guess_type(path)[0], data)
return 'url("%s")' % url return 'url("%s")' % url

View File

@@ -13,6 +13,7 @@ else:
"standalone version django-staticfiles needs " "standalone version django-staticfiles needs "
"to be installed.") "to be installed.")
class CompressorFinder(BaseStorageFinder): class CompressorFinder(BaseStorageFinder):
""" """
A staticfiles finder that looks in COMPRESS_ROOT A staticfiles finder that looks in COMPRESS_ROOT

View File

@@ -4,11 +4,11 @@ from compressor.exceptions import UncompressableFileError
class JsCompressor(Compressor): class JsCompressor(Compressor):
template_name = "compressor/js.html"
template_name_inline = "compressor/js_inline.html"
def __init__(self, content=None, output_prefix="js"): def __init__(self, content=None, output_prefix="js"):
super(JsCompressor, self).__init__(content, output_prefix) super(JsCompressor, self).__init__(content, output_prefix)
self.template_name = "compressor/js.html"
self.template_name_inline = "compressor/js_inline.html"
self.filters = list(settings.COMPRESS_JS_FILTERS) self.filters = list(settings.COMPRESS_JS_FILTERS)
self.type = 'js' self.type = 'js'
@@ -19,7 +19,8 @@ class JsCompressor(Compressor):
attribs = self.parser.elem_attribs(elem) attribs = self.parser.elem_attribs(elem)
if 'src' in attribs: if 'src' in attribs:
try: try:
self.split_content.append(('file', self.get_filename(attribs['src']), elem)) self.split_content.append(
('file', self.get_filename(attribs['src']), elem))
except UncompressableFileError: except UncompressableFileError:
if settings.DEBUG: if settings.DEBUG:
raise raise

View File

@@ -21,28 +21,30 @@ from compressor.utils import walk, any, import_module
class Command(NoArgsCommand): class Command(NoArgsCommand):
help = "Generate the compressor content outside of the request/response cycle" help = "Compress content outside of the request/response cycle"
option_list = NoArgsCommand.option_list + ( option_list = NoArgsCommand.option_list + (
make_option('--extension', '-e', action='append', dest='extensions', make_option('--extension', '-e', action='append', dest='extensions',
help='The file extension(s) to examine (default: ".html", ' help='The file extension(s) to examine (default: ".html", '
'separate multiple extensions with commas, or use -e ' 'separate multiple extensions with commas, or use -e '
'multiple times)'), 'multiple times)'),
make_option('-f', '--force', default=False, action='store_true', dest='force', make_option('-f', '--force', default=False, action='store_true',
help="Force generation of compressor content even if " help="Force generation of compressor content even if "
"COMPRESS setting is not True."), "COMPRESS setting is not True.", dest='force'),
make_option('--follow-links', default=False, action='store_true', dest='follow_links', make_option('--follow-links', default=False, action='store_true',
help="Follow symlinks when traversing the COMPRESS_ROOT " help="Follow symlinks when traversing the COMPRESS_ROOT "
"(which defaults to MEDIA_ROOT). Be aware that using this " "(which defaults to MEDIA_ROOT). Be aware that using this "
"can lead to infinite recursion if a link points to a parent " "can lead to infinite recursion if a link points to a parent "
"directory of itself."), "directory of itself.", dest='follow_links'),
) )
def get_loaders(self): def get_loaders(self):
from django.template.loader import template_source_loaders from django.template.loader import template_source_loaders
if template_source_loaders is None: if template_source_loaders is None:
try: try:
from django.template.loader import find_template as finder_func from django.template.loader import (
find_template as finder_func)
except ImportError: except ImportError:
from django.template.loader import find_template_source as finder_func from django.template.loader import (
find_template_source as finder_func)
try: try:
source, name = finder_func('test') source, name = finder_func('test')
except TemplateDoesNotExist: except TemplateDoesNotExist:
@@ -71,7 +73,8 @@ class Command(NoArgsCommand):
for loader in self.get_loaders(): for loader in self.get_loaders():
try: try:
module = import_module(loader.__module__) module = import_module(loader.__module__)
get_template_sources = getattr(module, 'get_template_sources', None) get_template_sources = getattr(module,
'get_template_sources', None)
if get_template_sources is None: if get_template_sources is None:
get_template_sources = loader.get_template_sources get_template_sources = loader.get_template_sources
paths.update(list(get_template_sources(''))) paths.update(list(get_template_sources('')))
@@ -89,7 +92,8 @@ class Command(NoArgsCommand):
log.write("Considering paths:\n\t" + "\n\t".join(paths) + "\n") log.write("Considering paths:\n\t" + "\n\t".join(paths) + "\n")
templates = set() templates = set()
for path in paths: for path in paths:
for root, dirs, files in walk(path, followlinks=options.get('followlinks', False)): for root, dirs, files in walk(path,
followlinks=options.get('followlinks', False)):
templates.update(os.path.join(root, name) templates.update(os.path.join(root, name)
for name in files if any(fnmatch(name, "*%s" % glob) for name in files if any(fnmatch(name, "*%s" % glob)
for glob in extensions)) for glob in extensions))
@@ -126,7 +130,8 @@ class Command(NoArgsCommand):
compressor_nodes.setdefault(template_name, []).extend(nodes) compressor_nodes.setdefault(template_name, []).extend(nodes)
if not compressor_nodes: if not compressor_nodes:
raise OfflineGenerationError("No 'compress' template tags found in templates.") raise OfflineGenerationError(
"No 'compress' template tags found in templates.")
if verbosity > 0: if verbosity > 0:
log.write("Found 'compress' tags in:\n\t" + log.write("Found 'compress' tags in:\n\t" +
@@ -175,18 +180,19 @@ class Command(NoArgsCommand):
for i, ext in enumerate(ext_list): for i, ext in enumerate(ext_list):
if not ext.startswith('.'): if not ext.startswith('.'):
ext_list[i] = '.%s' % ext_list[i] ext_list[i] = '.%s' % ext_list[i]
return set(ext_list)
# we don't want *.py files here because of the way non-*.py files
# are handled in make_messages() (they are copied to file.ext.py files to
# trick xgettext to parse them as Python files)
return set([x for x in ext_list if x != '.py'])
def handle_noargs(self, **options): def handle_noargs(self, **options):
if not settings.COMPRESS_ENABLED and not options.get("force"): if not settings.COMPRESS_ENABLED and not options.get("force"):
raise CommandError("Compressor is disabled. Set COMPRESS settting or use --force to override.") raise CommandError(
"Compressor is disabled. Set COMPRESS "
"settting or use --force to override.")
if not settings.COMPRESS_OFFLINE: if not settings.COMPRESS_OFFLINE:
if not options.get("force"): if not options.get("force"):
raise CommandError("Offline compressiong is disabled. Set COMPRESS_OFFLINE or use the --force to override.") raise CommandError(
warnings.warn("COMPRESS_OFFLINE is not set. Offline generated " "Offline compressiong is disabled. Set "
"cache will not be used.") "COMPRESS_OFFLINE or use the --force to override.")
warnings.warn(
"COMPRESS_OFFLINE is not set to True. "
"Offline generated cache will not be used.")
self.compress(sys.stdout, **options) self.compress(sys.stdout, **options)

View File

@@ -2,8 +2,11 @@ from django.utils.encoding import smart_unicode
from compressor.exceptions import ParserError from compressor.exceptions import ParserError
class ParserBase(object):
class ParserBase(object):
"""
Base parser to be subclassed when creating an own parser.
"""
def __init__(self, content): def __init__(self, content):
self.content = content self.content = content
@@ -43,6 +46,7 @@ class ParserBase(object):
""" """
raise NotImplementedError raise NotImplementedError
class BeautifulSoupParser(ParserBase): class BeautifulSoupParser(ParserBase):
_soup = None _soup = None
@@ -74,6 +78,7 @@ class BeautifulSoupParser(ParserBase):
def elem_str(self, elem): def elem_str(self, elem):
return smart_unicode(elem) return smart_unicode(elem)
class LxmlParser(ParserBase): class LxmlParser(ParserBase):
_tree = None _tree = None
@@ -110,4 +115,5 @@ class LxmlParser(ParserBase):
def elem_str(self, elem): def elem_str(self, elem):
from lxml import etree from lxml import etree
return smart_unicode(etree.tostring(elem, method='html', encoding=unicode)) return smart_unicode(
etree.tostring(elem, method='html', encoding=unicode))

View File

@@ -1,5 +1,3 @@
import os
from django import VERSION as DJANGO_VERSION from django import VERSION as DJANGO_VERSION
from django.conf import settings from django.conf import settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
@@ -44,7 +42,7 @@ class CompressorSettings(AppSettings):
MINT_DELAY = 30 # seconds MINT_DELAY = 30 # seconds
# check for file changes only after a delay # check for file changes only after a delay
MTIME_DELAY = 10 # seconds MTIME_DELAY = 10 # seconds
# enables the offline cache -- a cache that is filled by the compress management command # enables the offline cache -- also filled by the compress command
OFFLINE = False OFFLINE = False
# invalidates the offline cache after one year # invalidates the offline cache after one year
OFFLINE_TIMEOUT = 60 * 60 * 24 * 365 # 1 year OFFLINE_TIMEOUT = 60 * 60 * 24 * 365 # 1 year
@@ -60,7 +58,8 @@ class CompressorSettings(AppSettings):
if not value: if not value:
value = settings.MEDIA_ROOT value = settings.MEDIA_ROOT
if not value: if not value:
raise ImproperlyConfigured("The COMPRESS_ROOT setting must be set.") raise ImproperlyConfigured(
"The COMPRESS_ROOT setting must be set.")
# In case staticfiles is used, make sure the FileSystemFinder is # In case staticfiles is used, make sure the FileSystemFinder is
# installed, and if it is, check if COMPRESS_ROOT is listed in # installed, and if it is, check if COMPRESS_ROOT is listed in
# STATICFILES_DIRS to allow finding compressed files # STATICFILES_DIRS to allow finding compressed files
@@ -79,14 +78,14 @@ class CompressorSettings(AppSettings):
return value return value
def configure_url(self, value): def configure_url(self, value):
# Falls back to the 1.3 STATIC_URL setting 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(settings, 'STATIC_URL', None) value = getattr(settings, "STATIC_URL", None)
if not value: if not value:
value = 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.")
return value return value
def configure_cache_backend(self, value): def configure_cache_backend(self, value):
@@ -105,9 +104,9 @@ 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': 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(settings, 'STATIC_URL', None): if getattr(settings, "STATIC_URL", None):
value['STATIC_URL'] = settings.STATIC_URL value["STATIC_URL"] = settings.STATIC_URL
return value return value

View File

@@ -7,6 +7,7 @@ from django.utils.functional import LazyObject
from compressor.conf import settings from compressor.conf import settings
class CompressorFileStorage(FileSystemStorage): class CompressorFileStorage(FileSystemStorage):
""" """
Standard file system storage for files handled by django-compressor. Standard file system storage for files handled by django-compressor.
@@ -40,6 +41,7 @@ class CompressorFileStorage(FileSystemStorage):
self.delete(name) self.delete(name)
return name return name
class GzipCompressorFileStorage(CompressorFileStorage): class GzipCompressorFileStorage(CompressorFileStorage):
""" """
The standard compressor file system storage that gzips storage files The standard compressor file system storage that gzips storage files

View File

@@ -33,11 +33,13 @@ class CompressorNode(template.Node):
if (time.time() > refresh_time) and not refreshed: if (time.time() > refresh_time) and not refreshed:
# Store the stale value while the cache # Store the stale value while the cache
# revalidates for another MINT_DELAY seconds. # revalidates for another MINT_DELAY seconds.
self.cache_set(key, val, timeout=settings.COMPRESS_MINT_DELAY, refreshed=True) self.cache_set(key, val, refreshed=True,
timeout=settings.COMPRESS_MINT_DELAY)
return None return None
return val return val
def cache_set(self, key, val, timeout=settings.COMPRESS_REBUILD_TIMEOUT, refreshed=False): def cache_set(self, key, val, refreshed=False,
timeout=settings.COMPRESS_REBUILD_TIMEOUT):
refresh_time = timeout + time.time() refresh_time = timeout + time.time()
real_timeout = timeout + settings.COMPRESS_MINT_DELAY real_timeout = timeout + settings.COMPRESS_MINT_DELAY
packed_val = (val, refresh_time, refreshed) packed_val = (val, refresh_time, refreshed)
@@ -47,13 +49,15 @@ class CompressorNode(template.Node):
return "%s.%s.%s" % (compressor.cachekey, self.mode, self.kind) return "%s.%s.%s" % (compressor.cachekey, self.mode, self.kind)
def render(self, context, forced=False): def render(self, context, forced=False):
if (settings.COMPRESS_ENABLED and settings.COMPRESS_OFFLINE) and not forced: if (settings.COMPRESS_ENABLED and
settings.COMPRESS_OFFLINE) and not forced:
key = get_offline_cachekey(self.nodelist) key = get_offline_cachekey(self.nodelist)
content = cache.get(key) content = cache.get(key)
if content: if content:
return content return content
content = self.nodelist.render(context) content = self.nodelist.render(context)
if (not settings.COMPRESS_ENABLED or not len(content.strip())) and not forced: if (not settings.COMPRESS_ENABLED or
not len(content.strip())) and not forced:
return content return content
compressor = self.compressor_cls(content) compressor = self.compressor_cls(content)
cachekey = self.cache_key(compressor) cachekey = self.cache_key(compressor)

View File

@@ -8,14 +8,17 @@ from django.conf import settings
from compressor.exceptions import FilterError from compressor.exceptions import FilterError
try: try:
any = any any
except NameError: except NameError:
def any(seq): def any(seq):
for item in seq: for item in seq:
if item: if item:
return True return True
return False return False
def get_class(class_string, exception=FilterError): def get_class(class_string, exception=FilterError):
""" """
Convert a string version of a function name to the callable object. Convert a string version of a function name to the callable object.
@@ -32,6 +35,7 @@ def get_class(class_string, exception=FilterError):
return cls return cls
raise exception('Failed to import %s' % class_string) raise exception('Failed to import %s' % class_string)
def get_mod_func(callback): def get_mod_func(callback):
""" """
Converts 'django.views.news.stories.story_detail' to Converts 'django.views.news.stories.story_detail' to
@@ -43,6 +47,7 @@ def get_mod_func(callback):
return callback, '' return callback, ''
return callback[:dot], callback[dot + 1:] return callback[:dot], callback[dot + 1:]
def walk(root, topdown=True, onerror=None, followlinks=False): def walk(root, topdown=True, onerror=None, followlinks=False):
""" """
A version of os.walk that can follow symlinks for Python < 2.6 A version of os.walk that can follow symlinks for Python < 2.6
@@ -56,7 +61,9 @@ def walk(root, topdown=True, onerror=None, followlinks=False):
for link_dirpath, link_dirnames, link_filenames in walk(p): for link_dirpath, link_dirnames, link_filenames in walk(p):
yield (link_dirpath, link_dirnames, link_filenames) yield (link_dirpath, link_dirnames, link_filenames)
# Taken from Django 1.3-beta1 and before that from Python 2.7 with permission from/by the original author.
# Taken from Django 1.3 and before that from Python 2.7
# with permission from the original author.
def _resolve_name(name, package, level): def _resolve_name(name, package, level):
"""Return the absolute name of the module to be imported.""" """Return the absolute name of the module to be imported."""
if not hasattr(package, 'rindex'): if not hasattr(package, 'rindex'):
@@ -70,6 +77,7 @@ def _resolve_name(name, package, level):
"package") "package")
return "%s.%s" % (package[:dot], name) return "%s.%s" % (package[:dot], name)
def import_module(name, package=None): def import_module(name, package=None):
"""Import a module. """Import a module.
@@ -220,7 +228,6 @@ class cached_property(object):
if value is not None: if value is not None:
print("Connection %r deleted" % (value, )) print("Connection %r deleted" % (value, ))
""" """
def __init__(self, fget=None, fset=None, fdel=None, doc=None): def __init__(self, fget=None, fset=None, fdel=None, doc=None):
self.__get = fget self.__get = fget
self.__set = fset self.__set = fset