Ticket #12323: 12323.3.diff
File 12323.3.diff, 61.7 KB (added by , 14 years ago) |
---|
-
django/conf/global_settings.py
diff --git a/django/conf/global_settings.py b/django/conf/global_settings.py index 2714bfb..6d156ec 100644
a b TEMPLATE_CONTEXT_PROCESSORS = ( 193 193 'django.contrib.auth.context_processors.auth', 194 194 'django.core.context_processors.debug', 195 195 'django.core.context_processors.i18n', 196 'django.co re.context_processors.media',196 'django.contrib.staticfiles.context_processors.media', 197 197 # 'django.core.context_processors.request', 198 198 'django.contrib.messages.context_processors.messages', 199 199 ) … … TEMPLATE_CONTEXT_PROCESSORS = ( 201 201 # Output to use in template system for invalid (e.g. misspelled) variables. 202 202 TEMPLATE_STRING_IF_INVALID = '' 203 203 204 # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a205 # trailing slash.206 # Examples: "http://foo.com/media/", "/media/".207 ADMIN_MEDIA_PREFIX = '/media/'208 209 204 # Default e-mail address to use for various automated correspondence from 210 205 # the site managers. 211 206 DEFAULT_FROM_EMAIL = 'webmaster@localhost' … … TEST_DATABASE_COLLATION = None 522 517 523 518 # The list of directories to search for fixtures 524 519 FIXTURE_DIRS = () 520 521 ############### 522 # STATICFILES # 523 ############### 524 525 # Absolute path to the directory that holds media. 526 # Example: "/home/media/media.lawrence.com/static/" 527 STATICFILES_ROOT = '' 528 529 # URL that handles the static files served from STATICFILES_ROOT. 530 # Example: "http://media.lawrence.com/static/" 531 STATICFILES_URL = '/static/' 532 533 # A tuple of two-tuples with a name and the path of additional directories 534 # which hold static files and should be taken into account during resolving 535 STATICFILES_DIRS = () 536 537 # The default file storage backend used during the build process 538 STATICFILES_STORAGE = 'django.contrib.staticfiles.storage.StaticFilesStorage' 539 540 # List of finder classes that know how to find static files in 541 # various locations. 542 STATICFILES_FINDERS = ( 543 'django.contrib.staticfiles.finders.FileSystemFinder', 544 'django.contrib.staticfiles.finders.AppDirectoriesFinder', 545 'django.contrib.staticfiles.finders.StorageFinder', 546 ) 547 548 # URL prefix for admin media -- CSS, JavaScript and images. Make sure to use a 549 # trailing slash. 550 # Examples: "http://foo.com/media/admin/", "/media/admin/". 551 ADMIN_MEDIA_PREFIX = '/static/admin/' -
new file django/contrib/staticfiles/context_processors.py
diff --git a/django/contrib/staticfiles/__init__.py b/django/contrib/staticfiles/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/context_processors.py b/django/contrib/staticfiles/context_processors.py new file mode 100644 index 0000000..e162861
- + 1 from django.conf import settings 2 3 def media(request): 4 return { 5 'STATICFILES_URL': settings.STATICFILES_URL, 6 'MEDIA_URL': settings.MEDIA_URL, 7 } -
new file django/contrib/staticfiles/finders.py
diff --git a/django/contrib/staticfiles/finders.py b/django/contrib/staticfiles/finders.py new file mode 100644 index 0000000..9c7cb3b
- + 1 import os 2 from django.conf import settings 3 from django.db import models 4 from django.core.exceptions import ImproperlyConfigured 5 from django.utils.importlib import import_module 6 from django.core.files.storage import default_storage 7 from django.utils.functional import memoize 8 9 from django.contrib.staticfiles import utils 10 11 _finders = {} 12 13 14 class BaseFinder(object): 15 """ 16 A base file finder to be used for custom staticfiles finder classes. 17 18 """ 19 def find(self, path, all=False): 20 """ 21 Given a relative file path this ought to find an 22 absolute file path. 23 24 If the ``all`` parameter is ``False`` (default) only 25 the first found file path will be returned; if set 26 to ``True`` a list of all found files paths is returned. 27 """ 28 raise NotImplementedError("Finder subclasses need to implement find()") 29 30 31 class FileSystemFinder(BaseFinder): 32 """ 33 A static files finder that uses the ``STATICFILES_DIRS`` setting 34 to locate files. 35 """ 36 def find(self, path, all=False): 37 """ 38 Looks for files in the extra media locations 39 as defined in ``STATICFILES_DIRS``. 40 """ 41 matches = [] 42 for root in settings.STATICFILES_DIRS: 43 if isinstance(root, (list, tuple)): 44 prefix, root = root 45 else: 46 prefix = '' 47 matched_path = self.find_location(root, path, prefix) 48 if matched_path: 49 if not all: 50 return matched_path 51 matches.append(matched_path) 52 return matches 53 54 def find_location(self, root, path, prefix=None): 55 """ 56 Find a requested static file in a location, returning the found 57 absolute path (or ``None`` if no match). 58 """ 59 if prefix: 60 prefix = '%s/' % prefix 61 if not path.startswith(prefix): 62 return None 63 path = path[len(prefix):] 64 path = os.path.join(root, path) 65 if os.path.exists(path): 66 return path 67 68 69 class AppDirectoriesFinder(BaseFinder): 70 """ 71 A static files finder that looks in the ``media`` directory of each app. 72 """ 73 def find(self, path, all=False): 74 """ 75 Looks for files in the app directories. 76 """ 77 matches = [] 78 for app in models.get_apps(): 79 app_matches = self.find_in_app(app, path, all=all) 80 if app_matches: 81 if not all: 82 return app_matches 83 matches.extend(app_matches) 84 return matches 85 86 def find_in_app(self, app, path, all=False): 87 """ 88 Find a requested static file in an app's media locations. 89 90 If ``all`` is ``False`` (default), return the first matching 91 absolute path (or ``None`` if no match). Otherwise return a list of 92 found absolute paths. 93 94 """ 95 prefix = utils.get_app_prefix(app) 96 if prefix: 97 prefix = '%s/' % prefix 98 if not path.startswith(prefix): 99 return [] 100 path = path[len(prefix):] 101 paths = [] 102 storage = utils.app_static_storage(app) 103 if storage and storage.exists(path): 104 matched_path = storage.path(path) 105 if not all: 106 return matched_path 107 paths.append(matched_path) 108 return paths 109 110 111 class StorageFinder(BaseFinder): 112 """ 113 A static files finder that uses the default storage backend. 114 """ 115 static_storage = default_storage 116 117 def __init__(self, storage=None, *args, **kwargs): 118 if storage is not None: 119 self.static_storage = storage 120 super(StorageFinder, self).__init__(*args, **kwargs) 121 122 def find(self, path, all=False): 123 """ 124 Last resort, looks for files in the 125 static files storage if it's local. 126 """ 127 try: 128 self.static_storage.path('') 129 except NotImplementedError: 130 pass 131 else: 132 if self.static_storage.exists(path): 133 match = self.static_storage.path(path) 134 if all: 135 match = [match] 136 return match 137 return [] 138 139 140 def find(path, all=False): 141 """ 142 Find a requested static file, first looking in any defined extra media 143 locations and next in any (non-excluded) installed apps. 144 145 If no matches are found and the static location is local, look for a match 146 there too. 147 148 If ``all`` is ``False`` (default), return the first matching 149 absolute path (or ``None`` if no match). Otherwise return a list of 150 found absolute paths. 151 152 """ 153 matches = [] 154 for finder_path in settings.STATICFILES_FINDERS: 155 finder = get_finder(finder_path) 156 result = finder.find(path, all=all) 157 if not all and result: 158 return result 159 if not isinstance(result, (list, tuple)): 160 result = [result] 161 matches.extend(result) 162 163 if matches: 164 return matches 165 166 # No match. 167 return all and [] or None 168 169 170 def _get_finder(import_path): 171 """ 172 Imports the message storage class described by import_path, where 173 import_path is the full Python path to the class. 174 """ 175 try: 176 dot = import_path.rindex('.') 177 except ValueError: 178 raise ImproperlyConfigured("%s isn't a Python path." % import_path) 179 module, classname = import_path[:dot], import_path[dot + 1:] 180 try: 181 mod = import_module(module) 182 except ImportError, e: 183 raise ImproperlyConfigured('Error importing module %s: "%s"' % 184 (module, e)) 185 try: 186 cls = getattr(mod, classname) 187 except AttributeError: 188 raise ImproperlyConfigured('Module "%s" does not define a "%s" ' 189 'class.' % (module, classname)) 190 if not issubclass(cls, BaseFinder): 191 raise ImproperlyConfigured('Finder "%s" is not a subclass of "%s"' % 192 (cls, BaseFinder)) 193 return cls() 194 195 get_finder = memoize(_get_finder, _finders, 1) -
new file django/contrib/staticfiles/handlers.py
diff --git a/django/contrib/staticfiles/handlers.py b/django/contrib/staticfiles/handlers.py new file mode 100644 index 0000000..4765591
- + 1 import os 2 import urllib 3 4 5 from django.core.handlers.wsgi import WSGIHandler, STATUS_CODE_TEXT 6 from django.http import Http404 7 from django.conf import settings 8 from django.views import static 9 10 class StaticFilesHandler(WSGIHandler): 11 """ 12 WSGI middleware that intercepts calls to the static files directory, as 13 defined by the STATICFILES_URL setting, and serves those files. 14 """ 15 media_dir = settings.STATICFILES_ROOT 16 media_url = settings.STATICFILES_URL 17 18 def __init__(self, application, media_dir=None): 19 self.application = application 20 if media_dir: 21 self.media_dir = media_dir 22 23 def file_path(self, url): 24 """ 25 Returns the relative path to the media file on disk for the given URL. 26 27 The passed URL is assumed to begin with ``media_url``. If the 28 resultant file path is outside the media directory, then a ValueError 29 is raised. 30 """ 31 # Remove ``media_url``. 32 relative_url = url[len(self.media_url):] 33 return urllib.url2pathname(relative_url) 34 35 def serve(self, request, path): 36 from django.contrib.staticfiles import finders 37 absolute_path = finders.find(path) 38 if not absolute_path: 39 raise Http404('%r could not be matched to a static file.' % path) 40 absolute_path, filename = os.path.split(absolute_path) 41 return static.serve(request, path=filename, document_root=absolute_path) 42 43 def __call__(self, environ, start_response): 44 # Ignore requests that aren't under the media prefix. 45 # Also ignore all requests if prefix isn't a relative URL. 46 if (self.media_url.startswith('http://') or 47 self.media_url.startswith('https://') or 48 not environ['PATH_INFO'].startswith(self.media_url)): 49 return self.application(environ, start_response) 50 request = self.application.request_class(environ) 51 try: 52 response = self.serve(request, self.file_path(environ['PATH_INFO'])) 53 except Http404: 54 status = '404 NOT FOUND' 55 start_response(status, {'Content-type': 'text/plain'}.items()) 56 return [str('Page not found: %s' % environ['PATH_INFO'])] 57 status_text = STATUS_CODE_TEXT[response.status_code] 58 status = '%s %s' % (response.status_code, status_text) 59 response_headers = [(str(k), str(v)) for k, v in response.items()] 60 for c in response.cookies.values(): 61 response_headers.append(('Set-Cookie', str(c.output(header='')))) 62 start_response(status, response_headers) 63 return response 64 -
new file django/contrib/staticfiles/management/commands/__init__.py
diff --git a/django/contrib/staticfiles/management/__init__.py b/django/contrib/staticfiles/management/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/management/commands/__init__.py b/django/contrib/staticfiles/management/commands/__init__.py new file mode 100644 index 0000000..1bca2d4
- + 1 import logging 2 3 -
new file django/contrib/staticfiles/management/commands/build_static.py
diff --git a/django/contrib/staticfiles/management/commands/build_static.py b/django/contrib/staticfiles/management/commands/build_static.py new file mode 100644 index 0000000..bb0b989
- + 1 import os 2 import sys 3 import shutil 4 from optparse import make_option 5 6 from django.conf import settings 7 from django.core.files.storage import FileSystemStorage, get_storage_class 8 from django.core.management.base import CommandError, OptionalAppCommand 9 10 from django.contrib.staticfiles import utils 11 12 13 class Command(OptionalAppCommand): 14 """ 15 Command that allows to copy or symlink media files from different 16 locations to the settings.STATICFILES_ROOT. 17 """ 18 option_list = OptionalAppCommand.option_list + ( 19 make_option('--noinput', action='store_false', dest='interactive', 20 default=True, help="Do NOT prompt the user for input of any " 21 "kind."), 22 make_option('-i', '--ignore', action='append', default=[], 23 dest='ignore_patterns', metavar='PATTERN', 24 help="Ignore files or directories matching this glob-style " 25 "pattern. Use multiple times to ignore more."), 26 make_option('-n', '--dry-run', action='store_true', dest='dry_run', 27 help="Do everything except modify the filesystem."), 28 make_option('-l', '--link', action='store_true', dest='link', 29 help="Create a symbolic link to each file instead of copying."), 30 make_option('--exclude-dirs', action='store_true', 31 help="Exclude additional static locations specified in the " 32 "STATICFILES_DIRS setting."), 33 make_option('--no-default-ignore', action='store_false', 34 dest='use_default_ignore_patterns', default=True, 35 help="Don't ignore the common private glob-style patterns 'CVS', " 36 "'.*' and '*~'."), 37 ) 38 help = ("Copy static media files from apps and other locations in a " 39 "single location.") 40 41 def handle(self, *app_labels, **options): 42 ignore_patterns = options['ignore_patterns'] 43 if options['use_default_ignore_patterns']: 44 ignore_patterns += ['CVS', '.*', '*~'] 45 options['ignore_patterns'] = ignore_patterns 46 options['skipped_files'] = [] 47 options['copied_files'] = [] 48 options['symlinked_files'] = [] 49 options['destination_storage'] = get_storage_class(settings.STATICFILES_STORAGE)() 50 try: 51 destination_paths = utils.get_files(options['destination_storage'], ignore_patterns) 52 except OSError: 53 # The destination storage location may not exist yet. It'll get 54 # created when the first file is copied. 55 destination_paths = [] 56 options['destination_paths'] = destination_paths 57 try: 58 options['destination_storage'].path('') 59 destination_local = True 60 except NotImplementedError: 61 destination_local = False 62 options['destination_local'] = destination_local 63 if options.get('link', False): 64 if sys.platform == 'win32': 65 message = "Symlinking is not supported by this platform (%s)." 66 raise CommandError(message % sys.platform) 67 if not destination_local: 68 raise CommandError("Can't symlink to a remote destination.") 69 # Warn before doing anything more. 70 if options.get('interactive'): 71 confirm = raw_input(""" 72 You have requested to collate static media files and copy them to the 73 destination location as specified in your settings file. 74 75 This will overwrite existing files. Are you sure you want to do this? 76 77 Type 'yes' to continue, or 'no' to cancel: """) 78 if confirm != 'yes': 79 raise CommandError("Static files build cancelled.") 80 return super(Command, self).handle(*app_labels, **options) 81 82 def pre_handle_apps(self, **options): 83 """ 84 Copy all files from a directory. 85 86 """ 87 if not options.get('exclude_dirs', False): 88 ignore_patterns = options['ignore_patterns'] 89 for root in settings.STATICFILES_DIRS: 90 if isinstance(root, (list, tuple)): 91 prefix, root = root 92 else: 93 prefix = '' 94 source_storage = FileSystemStorage(location=root) 95 for source in utils.get_files(source_storage, ignore_patterns): 96 self.copy_file(source, prefix, source_storage, **options) 97 98 def post_handle_apps(self, **options): 99 copied_files = options['copied_files'] 100 symlinked_files = options['symlinked_files'] 101 logger = self.get_logger() 102 count = len(copied_files) + len(symlinked_files) 103 logger.info("%s static file%s built." % (count, 104 count != 1 and 's' or '')) 105 106 def handle_app(self, app, **options): 107 """ 108 Copy all static media files from an application. 109 110 """ 111 ignore_patterns = options['ignore_patterns'] 112 prefix = utils.get_app_prefix(app) 113 storage = utils.app_static_storage(app) 114 if storage: 115 for path in utils.get_files(storage, ignore_patterns): 116 self.copy_file(path, prefix, storage, **options) 117 118 def excluded_app(self, app, **options): 119 """ 120 Gather all the static media files for excluded apps. 121 122 This is so a warning can be issued for later copied files which would 123 normally be copied by excluded apps. 124 125 """ 126 skipped_files = options['skipped_files'] 127 ignore_patterns = options['ignore_patterns'] 128 all_files = utils.get_files_for_app(app, ignore_patterns) 129 skipped_files.extend(all_files) 130 131 def copy_file(self, source, destination_prefix, source_storage, **options): 132 """ 133 Attempt to copy (or symlink) `source` to `destination`, returning True 134 if successful. 135 """ 136 destination_storage = options['destination_storage'] 137 dry_run = options.get('dry_run', False) 138 logger = self.get_logger() 139 if destination_prefix: 140 destination = '/'.join([destination_prefix, source]) 141 else: 142 destination = source 143 144 if destination in options['copied_files']: 145 logger.warning("Skipping duplicate file (already copied earlier):" 146 "\n %s" % destination) 147 return False 148 if destination in options['symlinked_files']: 149 logger.warning("Skipping duplicate file (already linked earlier):" 150 "\n %s" % destination) 151 return False 152 if destination in options['skipped_files']: 153 logger.warning("Copying file that would normally be provided by " 154 "an excluded application:\n %s" % destination) 155 source_path = source_storage.path(source) 156 if destination in options['destination_paths']: 157 if dry_run: 158 logger.info("Pretending to delete:\n %s" % destination) 159 else: 160 logger.debug("Deleting:\n %s" % destination) 161 destination_storage.delete(destination) 162 if options.get('link', False): 163 destination_path = destination_storage.path(destination) 164 if dry_run: 165 logger.info("Pretending to symlink:\n %s\nto:\n %s" 166 % (source_path, destination_path)) 167 else: 168 logger.debug("Symlinking:\n %s\nto:\n %s" 169 % (source_path, destination_path)) 170 try: 171 os.makedirs(os.path.dirname(destination_path)) 172 except OSError: 173 pass 174 os.symlink(source_path, destination_path) 175 options['symlinked_files'].append(destination) 176 else: 177 if dry_run: 178 logger.info("Pretending to copy:\n %s\nto:\n %s" 179 % (source_path, destination)) 180 else: 181 if options['destination_local']: 182 destination_path = destination_storage.path(destination) 183 try: 184 os.makedirs(os.path.dirname(destination_path)) 185 except OSError: 186 pass 187 shutil.copy2(source_path, destination_path) 188 logger.debug("Copying:\n %s\nto:\n %s" 189 % (source_path, destination_path)) 190 else: 191 source_file = source_storage.open(source) 192 destination_storage.write(destination, source_file) 193 logger.debug("Copying:\n %s\nto:\n %s" 194 % (source_path, destination)) 195 options['copied_files'].append(destination) 196 return True -
new file django/contrib/staticfiles/management/commands/find_static.py
diff --git a/django/contrib/staticfiles/management/commands/find_static.py b/django/contrib/staticfiles/management/commands/find_static.py new file mode 100644 index 0000000..9fdff6b
- + 1 import os 2 from optparse import make_option 3 from django.core.management.base import LabelCommand 4 5 from django.contrib.staticfiles import finders 6 7 class Command(LabelCommand): 8 help = "Finds the absolute paths for the given static file(s)." 9 args = "[static_file ...]" 10 label = 'static file' 11 option_list = LabelCommand.option_list + ( 12 make_option('--first', action='store_false', dest='all', default=True, 13 help="Only return the first match for each static file."), 14 ) 15 16 def handle_label(self, media_path, **options): 17 logger = self.get_logger() 18 all = options['all'] 19 result = finders.find(media_path, all=all) 20 if not result: 21 logger.warning("No matching file found for %r." % media_path) 22 return 23 output = '\n '.join((os.path.realpath(path) for path in result)) 24 logger.info("Found %r here:\n %s" % (media_path, output)) -
new file django/contrib/staticfiles/storage.py
diff --git a/django/contrib/staticfiles/models.py b/django/contrib/staticfiles/models.py new file mode 100644 index 0000000..e69de29 diff --git a/django/contrib/staticfiles/storage.py b/django/contrib/staticfiles/storage.py new file mode 100644 index 0000000..b8d883c
- + 1 import os, posixpath 2 3 from django.core.exceptions import ImproperlyConfigured 4 from django.conf import settings 5 from django.core.files.storage import FileSystemStorage, get_storage_class 6 from django.utils.functional import LazyObject 7 8 9 class StaticFilesStorage(FileSystemStorage): 10 """ 11 Standard file system storage for site media files. 12 13 The defaults for ``location`` and ``base_url`` are 14 ``STATICFILES_ROOT`` and ``STATICFILES_URL``. 15 """ 16 def __init__(self, location=None, base_url=None, *args, **kwargs): 17 if location is None: 18 location = settings.STATICFILES_ROOT 19 if base_url is None: 20 base_url = settings.STATICFILES_URL 21 if not location: 22 raise ImproperlyConfigured("You're using the staticfiles app " 23 "without having set the STATICFILES_ROOT setting. Set it to " 24 "the absolute path of the directory that holds static media.") 25 if not base_url: 26 raise ImproperlyConfigured("You're using the staticfiles app " 27 "without having set the STATICFILES_URL setting. Set it to " 28 "URL that handles the files served from STATICFILES_ROOT.") 29 super(StaticFilesStorage, self).__init__(location, base_url, *args, **kwargs) -
new file django/contrib/staticfiles/urls.py
diff --git a/django/contrib/staticfiles/urls.py b/django/contrib/staticfiles/urls.py new file mode 100644 index 0000000..f8a30ac
- + 1 from django.conf.urls.defaults import patterns, url 2 from django.conf import settings 3 4 urlpatterns = [] 5 6 # only serve non-fqdn URLs 7 if ':' not in settings.STATICFILES_URL: 8 urlpatterns += patterns('', 9 url(r'^(?P<path>.*)$', 'django.contrib.staticfiles.views.serve'), 10 ) -
new file django/contrib/staticfiles/utils.py
diff --git a/django/contrib/staticfiles/utils.py b/django/contrib/staticfiles/utils.py new file mode 100644 index 0000000..b533747
- + 1 import os 2 import fnmatch 3 4 from django.core.files.storage import FileSystemStorage 5 from django.utils.importlib import import_module 6 7 def get_files_for_app(app, ignore_patterns=[]): 8 """ 9 Return a list containing the relative source paths for all files that 10 should be copied for an app. 11 12 """ 13 prefix = get_app_prefix(app) 14 files = [] 15 storage = app_static_storage(app) 16 if storage: 17 for path in get_files(storage, ignore_patterns): 18 if prefix: 19 path = '/'.join([prefix, path]) 20 files.append(path) 21 return files 22 23 def app_static_storage(app): 24 """ 25 Returns a static file storage if available in the given app. 26 27 """ 28 # "app" is actually the models module of the app. Remove the '.models'. 29 app_module = '.'.join(app.__name__.split('.')[:-1]) 30 31 # The models module may be a package in which case dirname(app.__file__) 32 # would be wrong. Import the actual app as opposed to the models module. 33 app = import_module(app_module) 34 app_root = os.path.dirname(app.__file__) 35 location = os.path.join(app_root, 'media') 36 if not os.path.isdir(location): 37 return None 38 return FileSystemStorage(location=location) 39 40 def get_app_prefix(app): 41 """ 42 Return the path name that should be prepended to files for this app. 43 44 """ 45 # "app" is actually the models module of the app. Remove the '.models'. 46 bits = app.__name__.split('.')[:-1] 47 app_name = bits[-1] 48 app_module = '.'.join(bits) 49 if app_module == 'django.contrib.admin': 50 return app_name 51 else: 52 return None 53 54 def get_files(storage, ignore_patterns=[], location=''): 55 """ 56 Recursively walk the storage directories gathering a complete list of files 57 that should be copied, returning this list. 58 59 """ 60 def is_ignored(path): 61 """ 62 Return True or False depending on whether the ``path`` should be 63 ignored (if it matches any pattern in ``ignore_patterns``). 64 65 """ 66 for pattern in ignore_patterns: 67 if fnmatch.fnmatchcase(path, pattern): 68 return True 69 return False 70 71 directories, files = storage.listdir(location) 72 static_files = [location and '/'.join([location, fn]) or fn 73 for fn in files 74 if not is_ignored(fn)] 75 for dir in directories: 76 if is_ignored(dir): 77 continue 78 if location: 79 dir = '/'.join([location, dir]) 80 static_files.extend(get_files(storage, ignore_patterns, dir)) 81 return static_files -
new file django/contrib/staticfiles/views.py
diff --git a/django/contrib/staticfiles/views.py b/django/contrib/staticfiles/views.py new file mode 100644 index 0000000..ffd5264
- + 1 """ 2 Views and functions for serving static files. These are only to be used during 3 development, and SHOULD NOT be used in a production setting. 4 5 """ 6 import os 7 from django import http 8 from django.views import static 9 10 from django.contrib.staticfiles import finders 11 12 13 def serve(request, path, show_indexes=False): 14 """ 15 Serve static files from locations inferred from the static files finders. 16 17 To use, put a URL pattern such as:: 18 19 (r'^(?P<path>.*)$', 'django.contrib.staticfiles.views.serve') 20 21 in your URLconf. You may also set ``show_indexes`` to ``True`` if you'd 22 like to serve a basic index of the directory. This index view will use the 23 template hardcoded below, but if you'd like to override it, you can create 24 a template called ``static/directory_index``. 25 """ 26 absolute_path = finders.find(path) 27 if not absolute_path: 28 raise http.Http404('%r could not be matched to a static file.' % path) 29 absolute_path, filename = os.path.split(absolute_path) 30 return static.serve(request, path=filename, document_root=absolute_path, 31 show_indexes=show_indexes) -
django/core/context_processors.py
diff --git a/django/core/context_processors.py b/django/core/context_processors.py index a5f29df..08bdc75 100644
a b def media(request): 71 71 Adds media-related context variables to the context. 72 72 73 73 """ 74 return {'MEDIA_URL': settings.MEDIA_URL} 74 import warnings 75 warnings.warn( 76 "The context processor at `django.core.context_processors.media` is " \ 77 "deprecated; use the path `django.contrib.staticfiles.context_processors.media` " \ 78 "instead.", 79 PendingDeprecationWarning 80 ) 81 from django.contrib.staticfiles.context_processors import media as media_context_processor 82 return media_context_processor(request) 75 83 76 84 def request(request): 77 85 return {'request': request} -
django/core/management/base.py
diff --git a/django/core/management/base.py b/django/core/management/base.py index dcd83ff..e00ebc1 100644
a b be executed through ``django-admin.py`` or ``manage.py``). 5 5 """ 6 6 7 7 import os 8 import logging 8 9 import sys 9 10 from optparse import make_option, OptionParser 10 11 … … class BaseCommand(object): 135 136 requires_model_validation = True 136 137 output_transaction = False # Whether to wrap the output in a "BEGIN; COMMIT;" 137 138 139 # OMG FIXME -- what is wrong with you? we don't want to use our own logger here, use the real thing man! 140 logger_parent = 'django.core.management.base.commands' 141 logger_name = 'base' 142 logger_handler = logging.StreamHandler() 143 logger_format = '%(message)s' 144 logger_levels = { 145 0: logging.ERROR, 146 1: logging.INFO, 147 2: logging.DEBUG, 148 } 149 138 150 def __init__(self): 139 151 self.style = color_style() 140 152 153 def get_logger(self): 154 parts = [self.logger_parent, self.logger_name] 155 name = '.'.join([part for part in parts if part]) 156 return logging.getLogger(name) 157 141 158 def get_version(self): 142 159 """ 143 160 Return the Django version, which should be correct for all … … class BaseCommand(object): 199 216 stderr. 200 217 201 218 """ 219 verbosity = options.get('verbosity', 1) 220 logger = self.get_logger() 221 logger.setLevel(self.logger_levels[int(verbosity)]) 222 formatter = logging.Formatter(self.logger_format) 223 self.logger_handler.setFormatter(formatter) 224 logger.addHandler(self.logger_handler) 202 225 # Switch to English, because django-admin.py creates database content 203 226 # like permissions, and those shouldn't contain any translations. 204 227 # But only do this if we can assume we have a working settings file, … … class BaseCommand(object): 232 255 except CommandError, e: 233 256 self.stderr.write(smart_str(self.style.ERROR('Error: %s\n' % e))) 234 257 sys.exit(1) 258 finally: 259 logger.removeHandler(self.logger_handler) 235 260 236 261 def validate(self, app=None, display_num_errors=False): 237 262 """ … … class AppCommand(BaseCommand): 297 322 """ 298 323 raise NotImplementedError() 299 324 325 class OptionalAppCommand(BaseCommand): 326 """ 327 A management command which optionally takes one or more installed 328 application names as arguments, and does something with each of them. 329 330 If no application names are provided, all the applications are used. 331 332 The order in which applications are processed is determined by the order 333 given in INSTALLED_APPS. This differs from Django's AppCommand (it uses the 334 order the apps are given in the management command). 335 336 Rather than implementing ``handle()``, subclasses must implement 337 ``handle_app()``, which will be called once for each application. 338 339 Subclasses can also optionally implement ``excluded_app()`` to run 340 processes on apps which were excluded. 341 342 """ 343 args = '[appname appname ...]' 344 option_list = BaseCommand.option_list + ( 345 make_option('-e', '--exclude', dest='exclude', action='append', 346 default=[], help='App to exclude (use multiple --exclude to ' 347 'exclude multiple apps).'), 348 ) 349 350 def handle(self, *app_labels, **options): 351 from django.db import models 352 # Get all the apps, checking for common errors. 353 try: 354 all_apps = models.get_apps() 355 except (ImproperlyConfigured, ImportError), e: 356 raise CommandError("%s. Are you sure your INSTALLED_APPS setting " 357 "is correct?" % e) 358 # Build the app_list. 359 app_list = [] 360 used = 0 361 for app in all_apps: 362 app_label = app.__name__.split('.')[-2] 363 if not app_labels or app_label in app_labels: 364 used += 1 365 if app_label not in options['exclude']: 366 app_list.append(app) 367 # Check that all app_labels were used. 368 if app_labels and used != len(app_labels): 369 raise CommandError('Could not find the following app(s): %s' % 370 ', '.join(app_labels)) 371 # Handle all the apps (either via handle_app or excluded_app), 372 # collating any output. 373 output = [] 374 pre_output = self.pre_handle_apps(**options) 375 if pre_output: 376 output.append(pre_output) 377 for app in all_apps: 378 if app in app_list: 379 handle_method = self.handle_app 380 else: 381 handle_method = self.excluded_app 382 app_output = handle_method(app, **options) 383 if app_output: 384 output.append(app_output) 385 post_output = self.post_handle_apps(**options) 386 if post_output: 387 output.append(post_output) 388 return '\n'.join(output) 389 390 def handle_app(self, app, **options): 391 """ 392 Perform the command's actions for ``app``, which will be the 393 Python module corresponding to an application name given on 394 the command line. 395 396 """ 397 raise NotImplementedError() 398 399 def excluded_app(self, app, **options): 400 """ 401 A hook for commands to parse apps which were excluded. 402 403 """ 404 405 def pre_handle_apps(self, **options): 406 """ 407 A hook for commands to do something before the applications are 408 processed. 409 410 """ 411 412 def post_handle_apps(self, **options): 413 """ 414 A hook for commands to do something after all applications have been 415 processed. 416 417 """ 418 419 300 420 class LabelCommand(BaseCommand): 301 421 """ 302 422 A management command which takes one or more arbitrary arguments -
django/core/management/commands/runserver.py
diff --git a/django/core/management/commands/runserver.py b/django/core/management/commands/runserver.py index fc2c694..21391e8 100644
a b 1 from django.core.management.base import BaseCommand, CommandError2 1 from optparse import make_option 3 2 import os 4 3 import sys 4 import warnings 5 6 from django.core.management.base import BaseCommand, CommandError 5 7 6 8 class Command(BaseCommand): 7 9 option_list = BaseCommand.option_list + ( … … class Command(BaseCommand): 20 22 import django 21 23 from django.core.servers.basehttp import run, AdminMediaHandler, WSGIServerException 22 24 from django.core.handlers.wsgi import WSGIHandler 25 from django.contrib.staticfiles.handlers import StaticFilesHandler 23 26 if args: 24 27 raise CommandError('Usage is runserver %s' % self.args) 25 28 if not addrport: … … class Command(BaseCommand): 56 59 translation.activate(settings.LANGUAGE_CODE) 57 60 58 61 try: 59 handler = AdminMediaHandler(WSGIHandler(), admin_media_path) 62 handler = WSGIHandler() 63 handler = StaticFilesHandler(handler) 64 # serve admin media like old-school (deprecation pending) 65 handler = AdminMediaHandler(handler, admin_media_path) 60 66 run(addr, int(port), handler) 61 67 except WSGIServerException, e: 62 68 # Use helpful error messages instead of ugly tracebacks. -
django/core/servers/basehttp.py
diff --git a/django/core/servers/basehttp.py b/django/core/servers/basehttp.py index dae4297..2da05de 100644
a b been reviewed for security issues. Don't use it for production use. 8 8 """ 9 9 10 10 from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 11 import mimetypes12 11 import os 13 12 import re 14 import stat15 13 import sys 16 14 import urllib 15 import warnings 17 16 18 17 from django.core.management.color import color_style 19 18 from django.utils.http import http_date 20 19 from django.utils._os import safe_join 20 from django.contrib.staticfiles.handlers import StaticFilesHandler 21 from django.views import static 21 22 22 23 __version__ = "0.1" 23 24 __all__ = ['WSGIServer','WSGIRequestHandler'] … … class WSGIRequestHandler(BaseHTTPRequestHandler): 633 634 634 635 sys.stderr.write(msg) 635 636 636 class AdminMediaHandler(object): 637 638 class AdminMediaHandler(StaticFilesHandler): 637 639 """ 638 640 WSGI middleware that intercepts calls to the admin media directory, as 639 641 defined by the ADMIN_MEDIA_PREFIX setting, and serves those images. 640 642 Use this ONLY LOCALLY, for development! This hasn't been tested for 641 643 security and is not super efficient. 642 644 """ 643 def __init__(self, application, media_dir=None): 645 646 @property 647 def media_dir(self): 648 import django 649 return os.path.join(django.__path__[0], 'contrib', 'admin', 'media') 650 651 @property 652 def media_url(self): 644 653 from django.conf import settings 645 self.application = application 646 if not media_dir: 647 import django 648 self.media_dir = \ 649 os.path.join(django.__path__[0], 'contrib', 'admin', 'media') 650 else: 651 self.media_dir = media_dir 652 self.media_url = settings.ADMIN_MEDIA_PREFIX 654 return settings.ADMIN_MEDIA_PREFIX 655 656 def __init__(self, application, media_dir=None): 657 warnings.warn('The AdminMediaHandler handler is deprecated; use the ' 658 '`django.contrib.staticfiles.handlers.StaticFilesHandler` instead.', 659 PendingDeprecationWarning) 660 super(AdminMediaHandler, self).__init__(application, media_dir) 653 661 654 662 def file_path(self, url): 655 663 """ 656 664 Returns the path to the media file on disk for the given URL. 657 665 658 The passed URL is assumed to begin with ADMIN_MEDIA_PREFIX. If the666 The passed URL is assumed to begin with ``media_url``. If the 659 667 resultant file path is outside the media directory, then a ValueError 660 668 is raised. 661 669 """ 662 # Remove ADMIN_MEDIA_PREFIX.670 # Remove ``media_url``. 663 671 relative_url = url[len(self.media_url):] 664 672 relative_path = urllib.url2pathname(relative_url) 665 673 return safe_join(self.media_dir, relative_path) 666 674 667 def __call__(self, environ, start_response): 668 import os.path 669 670 # Ignore requests that aren't under ADMIN_MEDIA_PREFIX. Also ignore 671 # all requests if ADMIN_MEDIA_PREFIX isn't a relative URL. 672 if self.media_url.startswith('http://') or self.media_url.startswith('https://') \ 673 or not environ['PATH_INFO'].startswith(self.media_url): 674 return self.application(environ, start_response) 675 def serve(self, request, path): 676 document_root, path = os.path.split(path) 677 return static.serve(request, path, document_root=document_root) 675 678 676 # Find the admin file and serve it up, if it exists and is readable.677 try:678 file_path = self.file_path(environ['PATH_INFO'])679 except ValueError: # Resulting file path was not valid.680 status = '404 NOT FOUND'681 headers = {'Content-type': 'text/plain'}682 output = ['Page not found: %s' % environ['PATH_INFO']]683 start_response(status, headers.items())684 return output685 if not os.path.exists(file_path):686 status = '404 NOT FOUND'687 headers = {'Content-type': 'text/plain'}688 output = ['Page not found: %s' % environ['PATH_INFO']]689 else:690 try:691 fp = open(file_path, 'rb')692 except IOError:693 status = '401 UNAUTHORIZED'694 headers = {'Content-type': 'text/plain'}695 output = ['Permission denied: %s' % environ['PATH_INFO']]696 else:697 # This is a very simple implementation of conditional GET with698 # the Last-Modified header. It makes media files a bit speedier699 # because the files are only read off disk for the first700 # request (assuming the browser/client supports conditional701 # GET).702 mtime = http_date(os.stat(file_path)[stat.ST_MTIME])703 headers = {'Last-Modified': mtime}704 if environ.get('HTTP_IF_MODIFIED_SINCE', None) == mtime:705 status = '304 NOT MODIFIED'706 output = []707 else:708 status = '200 OK'709 mime_type = mimetypes.guess_type(file_path)[0]710 if mime_type:711 headers['Content-Type'] = mime_type712 output = [fp.read()]713 fp.close()714 start_response(status, headers.items())715 return output716 679 717 680 def run(addr, port, wsgi_handler): 718 681 server_address = (addr, port) -
tests/regressiontests/servers/tests.py
diff --git a/tests/regressiontests/servers/tests.py b/tests/regressiontests/servers/tests.py index 4763982..29f169c 100644
a b from django.test import TestCase 9 9 from django.core.handlers.wsgi import WSGIHandler 10 10 from django.core.servers.basehttp import AdminMediaHandler 11 11 12 from django.conf import settings 12 13 13 14 class AdminMediaHandlerTests(TestCase): 14 15 … … class AdminMediaHandlerTests(TestCase): 25 26 """ 26 27 # Cases that should work on all platforms. 27 28 data = ( 28 (' /media/css/base.css', ('css', 'base.css')),29 ('%scss/base.css' % settings.ADMIN_MEDIA_PREFIX, ('css', 'base.css')), 29 30 ) 30 31 # Cases that should raise an exception. 31 32 bad_data = () … … class AdminMediaHandlerTests(TestCase): 34 35 if os.sep == '/': 35 36 data += ( 36 37 # URL, tuple of relative path parts. 37 (' /media/\\css/base.css', ('\\css', 'base.css')),38 ('%s\\css/base.css' % settings.ADMIN_MEDIA_PREFIX, ('\\css', 'base.css')), 38 39 ) 39 40 bad_data += ( 40 ' /media//css/base.css',41 ' /media////css/base.css',42 ' /media/../css/base.css',41 '%s/css/base.css' % settings.ADMIN_MEDIA_PREFIX, 42 '%s///css/base.css' % settings.ADMIN_MEDIA_PREFIX, 43 '%s../css/base.css' % settings.ADMIN_MEDIA_PREFIX, 43 44 ) 44 45 elif os.sep == '\\': 45 46 bad_data += ( 46 ' /media/C:\css/base.css',47 ' /media//\\css/base.css',48 ' /media/\\css/base.css',49 ' /media/\\\\css/base.css'47 '%sC:\css/base.css' % settings.ADMIN_MEDIA_PREFIX, 48 '%s/\\css/base.css' % settings.ADMIN_MEDIA_PREFIX, 49 '%s\\css/base.css' % settings.ADMIN_MEDIA_PREFIX, 50 '%s\\\\css/base.css' % settings.ADMIN_MEDIA_PREFIX 50 51 ) 51 52 for url, path_tuple in data: 52 53 try: -
new file tests/regressiontests/staticfiles_tests/apps/no_label/media/file2.txt
diff --git a/tests/regressiontests/staticfiles_tests/__init__.py b/tests/regressiontests/staticfiles_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/__init__.py b/tests/regressiontests/staticfiles_tests/apps/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/no_label/__init__.py b/tests/regressiontests/staticfiles_tests/apps/no_label/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/no_label/media/file2.txt b/tests/regressiontests/staticfiles_tests/apps/no_label/media/file2.txt new file mode 100644 index 0000000..aa264ca
- + 1 file2 in no_label_app -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/.hidden
diff --git a/tests/regressiontests/staticfiles_tests/apps/no_label/models.py b/tests/regressiontests/staticfiles_tests/apps/no_label/models.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/test/__init__.py b/tests/regressiontests/staticfiles_tests/apps/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/.hidden b/tests/regressiontests/staticfiles_tests/apps/test/media/test/.hidden new file mode 100644 index 0000000..cef6c23
- + 1 This file should be ignored. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/CVS
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/CVS b/tests/regressiontests/staticfiles_tests/apps/test/media/test/CVS new file mode 100644 index 0000000..cef6c23
- + 1 This file should be ignored. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/backup~
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/backup~ b/tests/regressiontests/staticfiles_tests/apps/test/media/test/backup~ new file mode 100644 index 0000000..cef6c23
- + 1 This file should be ignored. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/file.txt
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/file.txt b/tests/regressiontests/staticfiles_tests/apps/test/media/test/file.txt new file mode 100644 index 0000000..169a206
- + 1 In app media directory. -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/file1.txt
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/file1.txt b/tests/regressiontests/staticfiles_tests/apps/test/media/test/file1.txt new file mode 100644 index 0000000..9f9a8d9
- + 1 file1 in the app dir 2 No newline at end of file -
new file tests/regressiontests/staticfiles_tests/apps/test/media/test/test.ignoreme
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/media/test/test.ignoreme b/tests/regressiontests/staticfiles_tests/apps/test/media/test/test.ignoreme new file mode 100644 index 0000000..d7df09c
- + 1 This file should be ignored. 2 No newline at end of file -
new file tests/regressiontests/staticfiles_tests/apps/test/otherdir/odfile.txt
diff --git a/tests/regressiontests/staticfiles_tests/apps/test/models.py b/tests/regressiontests/staticfiles_tests/apps/test/models.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/apps/test/otherdir/odfile.txt b/tests/regressiontests/staticfiles_tests/apps/test/otherdir/odfile.txt new file mode 100644 index 0000000..c62c93d
- + 1 File in otherdir. -
new file tests/regressiontests/staticfiles_tests/project/documents/subdir/test.txt
diff --git a/tests/regressiontests/staticfiles_tests/models.py b/tests/regressiontests/staticfiles_tests/models.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/regressiontests/staticfiles_tests/project/documents/subdir/test.txt b/tests/regressiontests/staticfiles_tests/project/documents/subdir/test.txt new file mode 100644 index 0000000..04326a2
- + 1 Can we find this file? -
new file tests/regressiontests/staticfiles_tests/project/documents/test.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/documents/test.txt b/tests/regressiontests/staticfiles_tests/project/documents/test.txt new file mode 100644 index 0000000..04326a2
- + 1 Can we find this file? -
new file tests/regressiontests/staticfiles_tests/project/documents/test/file.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/documents/test/file.txt b/tests/regressiontests/staticfiles_tests/project/documents/test/file.txt new file mode 100644 index 0000000..fdeaa23
- + 1 In STATICFILES_DIRS directory. 2 -
new file tests/regressiontests/staticfiles_tests/project/site_media/media/media-file.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/site_media/media/media-file.txt b/tests/regressiontests/staticfiles_tests/project/site_media/media/media-file.txt new file mode 100644 index 0000000..466922d
- + 1 Media file. -
new file tests/regressiontests/staticfiles_tests/project/site_media/static/test/storage.txt
diff --git a/tests/regressiontests/staticfiles_tests/project/site_media/static/test/storage.txt b/tests/regressiontests/staticfiles_tests/project/site_media/static/test/storage.txt new file mode 100644 index 0000000..2eda9ce
- + 1 Yeah! 2 No newline at end of file -
new file tests/regressiontests/staticfiles_tests/tests.py
diff --git a/tests/regressiontests/staticfiles_tests/tests.py b/tests/regressiontests/staticfiles_tests/tests.py new file mode 100644 index 0000000..0f29d60
- + 1 import tempfile 2 import shutil 3 import os 4 import sys 5 from cStringIO import StringIO 6 import posixpath 7 8 from django.test import TestCase, Client 9 from django.conf import settings 10 from django.core.exceptions import ImproperlyConfigured 11 from django.core.management import call_command 12 from django.db.models.loading import load_app 13 14 from django.contrib.staticfiles import finders, storage 15 16 TEST_ROOT = os.path.dirname(__file__) 17 18 19 class StaticFilesTestCase(TestCase): 20 """ 21 Test case with a couple utility assertions. 22 """ 23 def setUp(self): 24 self.old_staticfiles_url = settings.STATICFILES_URL 25 self.old_staticfiles_root = settings.STATICFILES_ROOT 26 self.old_staticfiles_dirs = settings.STATICFILES_DIRS 27 self.old_installed_apps = settings.INSTALLED_APPS 28 self.old_media_root = settings.MEDIA_ROOT 29 self.old_media_url = settings.MEDIA_URL 30 self.old_admin_media_prefix = settings.ADMIN_MEDIA_PREFIX 31 32 # We have to load these apps to test staticfiles. 33 load_app('regressiontests.staticfiles_tests.apps.test') 34 load_app('regressiontests.staticfiles_tests.apps.no_label') 35 site_media = os.path.join(TEST_ROOT, 'project', 'site_media') 36 settings.MEDIA_ROOT = os.path.join(site_media, 'media') 37 settings.MEDIA_URL = '/media/' 38 settings.ADMIN_MEDIA_PREFIX = posixpath.join(settings.STATICFILES_URL, 'admin/') 39 settings.STATICFILES_ROOT = os.path.join(site_media, 'static') 40 settings.STATICFILES_URL = '/static/' 41 settings.STATICFILES_DIRS = ( 42 os.path.join(TEST_ROOT, 'project', 'documents'), 43 ) 44 45 def tearDown(self): 46 settings.MEDIA_ROOT = self.old_media_root 47 settings.MEDIA_URL = self.old_media_url 48 settings.ADMIN_MEDIA_PREFIX = self.old_admin_media_prefix 49 settings.STATICFILES_ROOT = self.old_staticfiles_root 50 settings.STATICFILES_URL = self.old_staticfiles_url 51 settings.STATICFILES_DIRS = self.old_staticfiles_dirs 52 settings.INSTALLED_APPS = self.old_installed_apps 53 54 def assertFileContains(self, filepath, text): 55 self.failUnless(text in self._get_file(filepath), 56 "'%s' not in '%s'" % (text, filepath)) 57 58 def assertFileNotFound(self, filepath): 59 self.assertRaises(IOError, self._get_file, filepath) 60 61 62 class TestingStaticFilesStorage(storage.StaticFilesStorage): 63 64 def __init__(self, location=None, base_url=None, *args, **kwargs): 65 location = tempfile.mkdtemp() 66 settings.STATICFILES_ROOT = location 67 super(TestingStaticFilesStorage, self).__init__(location, base_url, *args, **kwargs) 68 69 70 class BuildStaticTestCase(StaticFilesTestCase): 71 """ 72 Tests shared by all file-resolving features (build_static, 73 find_static, and static serve view). 74 75 This relies on the asserts defined in UtilityAssertsTestCase, but 76 is separated because some test cases need those asserts without 77 all these tests. 78 """ 79 def setUp(self): 80 super(BuildStaticTestCase, self).setUp() 81 self.old_staticfiles_storage = settings.STATICFILES_STORAGE 82 self.old_root = settings.STATICFILES_ROOT 83 settings.STATICFILES_STORAGE = 'regressiontests.staticfiles_tests.tests.TestingStaticFilesStorage' 84 self.run_build_static() 85 86 def tearDown(self): 87 shutil.rmtree(settings.STATICFILES_ROOT) 88 settings.STATICFILES_ROOT = self.old_root 89 settings.STATICFILES_STORAGE = self.old_staticfiles_storage 90 super(BuildStaticTestCase, self).tearDown() 91 92 def run_build_static(self, **kwargs): 93 call_command('build_static', interactive=False, verbosity='0', 94 ignore_patterns=['*.ignoreme'], **kwargs) 95 96 def _get_file(self, filepath): 97 assert filepath, 'filepath is empty.' 98 filepath = os.path.join(settings.STATICFILES_ROOT, filepath) 99 return open(filepath).read() 100 101 102 class TestDefaults(object): 103 def test_staticfiles_dirs(self): 104 """ 105 Can find a file in a STATICFILES_DIRS directory. 106 107 """ 108 self.assertFileContains('test.txt', 'Can we find') 109 110 def test_staticfiles_dirs_subdir(self): 111 """ 112 Can find a file in a subdirectory of a STATICFILES_DIRS 113 directory. 114 115 """ 116 self.assertFileContains('subdir/test.txt', 'Can we find') 117 118 def test_staticfiles_dirs_priority(self): 119 """ 120 File in STATICFILES_DIRS has priority over file in app. 121 122 """ 123 self.assertFileContains('test/file.txt', 'STATICFILES_DIRS') 124 125 def test_app_files(self): 126 """ 127 Can find a file in an app media/ directory. 128 129 """ 130 self.assertFileContains('test/file1.txt', 'file1 in the app dir') 131 132 133 class TestBuildStatic(BuildStaticTestCase, TestDefaults): 134 """ 135 Test ``build_static`` management command. 136 """ 137 def test_ignore(self): 138 """ 139 Test that -i patterns are ignored. 140 """ 141 self.assertFileNotFound('test/test.ignoreme') 142 143 def test_common_ignore_patterns(self): 144 """ 145 Common ignore patterns (*~, .*, CVS) are ignored. 146 """ 147 self.assertFileNotFound('test/.hidden') 148 self.assertFileNotFound('test/backup~') 149 self.assertFileNotFound('test/CVS') 150 151 152 class TestBuildStaticExcludeNoDefaultIgnore(BuildStaticTestCase): 153 """ 154 Test ``--exclude-dirs`` and ``--no-default-ignore`` options for 155 ``build_static`` management command. 156 """ 157 def run_build_static(self): 158 super(TestBuildStaticExcludeNoDefaultIgnore, self).run_build_static( 159 exclude_dirs=True, use_default_ignore_patterns=False) 160 161 def test_exclude_dirs(self): 162 """ 163 With --exclude-dirs, cannot find file in 164 STATICFILES_DIRS. 165 166 """ 167 self.assertFileNotFound('test.txt') 168 169 def test_no_common_ignore_patterns(self): 170 """ 171 With --no-default-ignore, common ignore patterns (*~, .*, CVS) 172 are not ignored. 173 174 """ 175 self.assertFileContains('test/.hidden', 'should be ignored') 176 self.assertFileContains('test/backup~', 'should be ignored') 177 self.assertFileContains('test/CVS', 'should be ignored') 178 179 180 class TestBuildStaticDryRun(BuildStaticTestCase): 181 """ 182 Test ``--dry-run`` option for ``build_static`` management command. 183 """ 184 def run_build_static(self): 185 super(TestBuildStaticDryRun, self).run_build_static(dry_run=True) 186 187 def test_no_files_created(self): 188 """ 189 With --dry-run, no files created in destination dir. 190 """ 191 self.assertEquals(os.listdir(settings.STATICFILES_ROOT), []) 192 193 194 if sys.platform != 'win32': 195 class TestBuildStaticLinks(BuildStaticTestCase, TestDefaults): 196 """ 197 Test ``--link`` option for ``build_static`` management command. 198 199 Note that by inheriting ``BaseFileResolutionTests`` we repeat all 200 the standard file resolving tests here, to make sure using 201 ``--link`` does not change the file-selection semantics. 202 """ 203 def run_build_static(self): 204 super(TestBuildStaticLinks, self).run_build_static(link=True) 205 206 def test_links_created(self): 207 """ 208 With ``--link``, symbolic links are created. 209 210 """ 211 self.failUnless(os.path.islink(os.path.join(settings.STATICFILES_ROOT, 'test.txt'))) 212 213 214 class TestServeStatic(StaticFilesTestCase): 215 """ 216 Test static asset serving view. 217 """ 218 urls = "regressiontests.staticfiles_tests.urls" 219 220 def _response(self, filepath): 221 return self.client.get( 222 posixpath.join(settings.STATICFILES_URL, filepath)) 223 224 def assertFileContains(self, filepath, text): 225 self.assertContains(self._response(filepath), text) 226 227 def assertFileNotFound(self, filepath): 228 self.assertEquals(self._response(filepath).status_code, 404) 229 230 231 class TestServeAdminMedia(TestServeStatic): 232 """ 233 Test serving media from django.contrib.admin. 234 """ 235 def test_serve_admin_media(self): 236 media_file = posixpath.join( 237 settings.ADMIN_MEDIA_PREFIX, 'css/base.css') 238 response = self.client.get(media_file) 239 self.assertContains(response, 'body') 240 241 242 class FinderTestCase(object): 243 """ 244 Base finder test mixin 245 """ 246 def test_find_first(self): 247 src, dst = self.find_first 248 self.assertEquals(self.finder.find(src), dst) 249 250 def test_find_all(self): 251 src, dst = self.find_all 252 self.assertEquals(self.finder.find(src, all=True), dst) 253 254 255 class TestFileSystemFinder(StaticFilesTestCase, FinderTestCase): 256 """ 257 Test FileSystemFinder. 258 """ 259 def setUp(self): 260 super(TestFileSystemFinder, self).setUp() 261 self.finder = finders.FileSystemFinder() 262 test_file_path = os.path.join(TEST_ROOT, 'project/documents/test/file.txt') 263 self.find_first = ("test/file.txt", test_file_path) 264 self.find_all = ("test/file.txt", [test_file_path]) 265 266 267 class TestAppDirectoriesFinder(StaticFilesTestCase, FinderTestCase): 268 """ 269 Test AppDirectoriesFinder. 270 """ 271 def setUp(self): 272 super(TestAppDirectoriesFinder, self).setUp() 273 self.finder = finders.AppDirectoriesFinder() 274 test_file_path = os.path.join(TEST_ROOT, 'apps/test/media/test/file1.txt') 275 self.find_first = ("test/file1.txt", test_file_path) 276 self.find_all = ("test/file1.txt", [test_file_path]) 277 278 279 class TestStorageFinder(StaticFilesTestCase, FinderTestCase): 280 """ 281 Test StorageFinder. 282 """ 283 def setUp(self): 284 super(TestStorageFinder, self).setUp() 285 self.finder = finders.StorageFinder( 286 storage=storage.StaticFilesStorage(location=settings.MEDIA_ROOT)) 287 test_file_path = os.path.join(settings.MEDIA_ROOT, 'media-file.txt') 288 self.find_first = ("media-file.txt", test_file_path) 289 self.find_all = ("media-file.txt", [test_file_path]) 290 291 292 class TestMiscFinder(TestCase): 293 """ 294 A few misc finder tests. 295 """ 296 def test_get_finder(self): 297 self.assertTrue(isinstance(finders.get_finder( 298 "django.contrib.staticfiles.finders.FileSystemFinder"), 299 finders.FileSystemFinder)) 300 self.assertRaises(ImproperlyConfigured, 301 finders.get_finder, "django.contrib.staticfiles.finders.FooBarFinder") 302 self.assertRaises(ImproperlyConfigured, 303 finders.get_finder, "foo.bar.FooBarFinder") 304 -
new file tests/regressiontests/staticfiles_tests/urls.py
diff --git a/tests/regressiontests/staticfiles_tests/urls.py b/tests/regressiontests/staticfiles_tests/urls.py new file mode 100644 index 0000000..061ec64
- + 1 from django.conf import settings 2 from django.conf.urls.defaults import * 3 4 urlpatterns = patterns('', 5 url(r'^static/(?P<path>.*)$', 'django.contrib.staticfiles.views.serve'), 6 ) -
tests/runtests.py
diff --git a/tests/runtests.py b/tests/runtests.py index a52152b..c23f9fd 100755
a b ALWAYS_INSTALLED_APPS = [ 28 28 'django.contrib.comments', 29 29 'django.contrib.admin', 30 30 'django.contrib.admindocs', 31 'django.contrib.staticfiles', 31 32 ] 32 33 33 34 def get_test_models(): -
tests/urls.py
diff --git a/tests/urls.py b/tests/urls.py index 01d6408..e06dc33 100644
a b urlpatterns = patterns('', 41 41 42 42 # special headers views 43 43 (r'special_headers/', include('regressiontests.special_headers.urls')), 44 45 # static files handling 46 (r'^', include('regressiontests.staticfiles_tests.urls')), 44 47 )