5 from django.conf import settings as django_settings
6 from django.utils.http import urlquote
7 from django.dispatch import dispatcher
9 from compress.conf import settings
10 from compress.signals import css_filtered, js_filtered
13 def get_class(class_string):
15 Convert a string version of a function name to the callable object.
18 if not hasattr(class_string, '__bases__'):
21 class_string = class_string.encode('ascii')
22 mod_name, class_name = get_mod_func(class_string)
24 class_string = getattr(__import__(mod_name, {}, {}, ['']), class_name)
25 except (ImportError, AttributeError):
26 raise Exception('Failed to import filter %s' % class_string)
31 def get_mod_func(callback):
33 Converts 'django.views.news.stories.story_detail' to
34 ('django.views.news.stories', 'story_detail')
38 dot = callback.rindex('.')
41 return callback[:dot], callback[dot + 1:]
44 def needs_update(output_file, source_files, verbosity=0):
46 Scan the source files for changes and returns True if the output_file needs to be updated.
49 version = get_version(source_files)
51 on = get_output_filename(output_file, version)
52 compressed_file_full = media_root(on)
54 if not os.path.exists(compressed_file_full):
57 update_needed = getattr(get_class(settings.COMPRESS_VERSIONING)(), 'needs_update')(output_file, source_files, version)
61 def media_root(filename):
63 Return the full path to ``filename``. ``filename`` is a relative path name in MEDIA_ROOT
65 return os.path.join(django_settings.STATIC_ROOT, filename)
68 def media_url(url, prefix=None):
70 return prefix + urlquote(url)
71 return django_settings.STATIC_URL + urlquote(url)
74 def concat(filenames, separator=''):
76 Concatenate the files from the list of the ``filenames``, ouput separated with ``separator``.
79 for filename in filenames:
80 fd = open(media_root(filename), 'rb')
88 return int(max([os.stat(media_root(f)).st_mtime for f in files]))
91 def save_file(filename, contents):
92 dirname = os.path.dirname(media_root(filename))
93 if not os.path.exists(dirname):
95 fd = open(media_root(filename), 'wb+')
100 def get_output_filename(filename, version):
101 if settings.COMPRESS_VERSION and version is not None:
102 return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, version)
104 return filename.replace(settings.COMPRESS_VERSION_PLACEHOLDER, settings.COMPRESS_VERSION_DEFAULT)
107 def get_version(source_files, verbosity=0):
108 version = getattr(get_class(settings.COMPRESS_VERSIONING)(), 'get_version')(source_files)
112 def get_version_from_file(path, filename):
113 regex = re.compile(r'^%s$' % (get_output_filename(settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)]), r'([A-Za-z0-9]+)')))
114 for f in os.listdir(path):
115 result = regex.match(f)
116 if result and result.groups():
117 return result.groups()[0]
120 def remove_files(path, filename, verbosity=0):
121 regex = re.compile(r'^%s$' % (os.path.basename(get_output_filename(settings.COMPRESS_VERSION_PLACEHOLDER.join([re.escape(part) for part in filename.split(settings.COMPRESS_VERSION_PLACEHOLDER)]), r'[A-Za-z0-9]+'))))
122 if os.path.exists(path):
123 for f in os.listdir(path):
126 print "Removing outdated file %s" % f
128 os.unlink(os.path.join(path, f))
131 def filter_common(obj, verbosity, filters, attr, separator, signal):
132 output = concat(obj['source_filenames'], separator)
134 filename = get_output_filename(obj['output_filename'], get_version(obj['source_filenames']))
136 if settings.COMPRESS_VERSION:
137 remove_files(os.path.dirname(media_root(filename)), obj['output_filename'], verbosity)
140 print "Saving %s" % filename
143 output = getattr(get_class(f)(verbose=(verbosity >= 2)), attr)(output)
145 save_file(filename, output)
149 def filter_css(css, verbosity=0):
150 return filter_common(css, verbosity, filters=settings.COMPRESS_CSS_FILTERS, attr='filter_css', separator='', signal=css_filtered)
153 def filter_js(js, verbosity=0):
154 return filter_common(js, verbosity, filters=settings.COMPRESS_JS_FILTERS, attr='filter_js', separator='', signal=js_filtered)