From 6a9ed7d403a5f90a7c7354097aae99b74e8ce215 Mon Sep 17 00:00:00 2001 From: Vajrasky Kok Date: Sat, 16 Nov 2013 22:55:50 +0800 Subject: [PATCH] Fixed #19879 -- Have 'findstatic' says on which directories it searched the relative paths. Added searched_locations in finders module. Added verbosity flag level 2 on 'findstatic' command that will output the directories on which it searched the relative paths. Reported by ccurvey. Initial patch by Jonas Svensson and Vajrasky Kok. --- django/contrib/staticfiles/finders.py | 12 ++++++ .../management/commands/findstatic.py | 16 ++++++-- docs/ref/contrib/staticfiles.txt | 34 +++++++++++++++++ docs/releases/1.7.txt | 4 ++ tests/staticfiles_tests/tests.py | 37 +++++++++++++++++++ 5 files changed, 100 insertions(+), 3 deletions(-) diff --git a/django/contrib/staticfiles/finders.py b/django/contrib/staticfiles/finders.py index 86b620696f..3569f360b2 100644 --- a/django/contrib/staticfiles/finders.py +++ b/django/contrib/staticfiles/finders.py @@ -12,11 +12,15 @@ from django.utils import six, lru_cache from django.contrib.staticfiles import utils +# To keep track on which directories the finder has searched the static files. +searched_locations = [] + class BaseFinder(object): """ A base file finder to be used for custom staticfiles finder classes. """ + def find(self, path, all=False): """ Given a relative file path this ought to find an @@ -75,6 +79,8 @@ class FileSystemFinder(BaseFinder): """ matches = [] for prefix, root in self.locations: + if root not in searched_locations: + searched_locations.append(root) matched_path = self.find_location(root, path, prefix) if matched_path: if not all: @@ -147,6 +153,9 @@ class AppDirectoriesFinder(BaseFinder): """ matches = [] for app in self.apps: + app_location = self.storages[app].location + if app_location not in searched_locations: + searched_locations.append(app_location) match = self.find_in_app(app, path) if match: if not all: @@ -195,6 +204,8 @@ class BaseStorageFinder(BaseFinder): except NotImplementedError: pass else: + if self.storage.location not in searched_locations: + searched_locations.append(self.storage.location) if self.storage.exists(path): match = self.storage.path(path) if all: @@ -232,6 +243,7 @@ def find(path, all=False): If ``all`` is ``False`` (default), return the first matching absolute path (or ``None`` if no match). Otherwise return a list. """ + searched_locations[:] = [] matches = [] for finder in get_finders(): result = finder.find(path, all=all) diff --git a/django/contrib/staticfiles/management/commands/findstatic.py b/django/contrib/staticfiles/management/commands/findstatic.py index 6c12e48979..7da7041c9b 100644 --- a/django/contrib/staticfiles/management/commands/findstatic.py +++ b/django/contrib/staticfiles/management/commands/findstatic.py @@ -21,15 +21,25 @@ class Command(LabelCommand): verbosity = int(options.get('verbosity', 1)) result = finders.find(path, all=options['all']) path = force_text(path) + if verbosity >= 2: + searched_locations = ("Looking in the following locations:\n %s" % + "\n ".join(force_text(location) + for location in finders.searched_locations)) + else: + searched_locations = '' if result: if not isinstance(result, (list, tuple)): result = [result] result = (force_text(os.path.realpath(path)) for path in result) if verbosity >= 1: - output = '\n '.join(result) - return "Found '%s' here:\n %s" % (path, output) + file_list = '\n '.join(result) + return ("Found '%s' here:\n %s\n%s" % + (path, file_list, searched_locations)) else: return '\n'.join(result) else: + message = ["No matching file found for '%s'." % path] + if verbosity >= 2: + message.append(searched_locations) if verbosity >= 1: - self.stderr.write("No matching file found for '%s'." % path) + self.stderr.write('\n'.join(message)) diff --git a/docs/ref/contrib/staticfiles.txt b/docs/ref/contrib/staticfiles.txt index 74c6fe7e5b..214128e2cd 100644 --- a/docs/ref/contrib/staticfiles.txt +++ b/docs/ref/contrib/staticfiles.txt @@ -162,6 +162,23 @@ output and just get the path names:: /home/special.polls.com/core/static/css/base.css /home/polls.com/core/static/css/base.css +On the other hand, by setting the :djadminopt:`--verbosity` flag to 2, you can +get all the directories on which it searched the relative paths:: + + $ python manage.py findstatic css/base.css --verbosity 2 + Found 'css/base.css' here: + /home/special.polls.com/core/static/css/base.css + /home/polls.com/core/static/css/base.css + Looking in the following locations: + /home/special.polls.com/core/static + /home/polls.com/core/static + /some/other/path/static + +.. versionadded:: 1.7 + + The additional message of on which directories it searched the relative + paths is new in Django 1.7. + .. _staticfiles-runserver: runserver @@ -366,6 +383,23 @@ files: .. _staticfiles-development-view: +Finders Module +============== + +``staticfiles`` finders has a ``searched_locations`` list with directory paths +in which they search for the relative paths. Example usage:: + + from django.contrib.staticfiles import finders + + result = finders.find('css/base.css') + searched_locations = finders.searched_locations + +.. versionadded:: 1.7 + +The ``get_searched_locations`` function is new in Django 1.7. Previously, we +have to check the locations of our :setting:`STATICFILES_FINDERS` manually +one by one. + Static file development view ---------------------------- diff --git a/docs/releases/1.7.txt b/docs/releases/1.7.txt index 905d0a3bf5..cd8732fa82 100644 --- a/docs/releases/1.7.txt +++ b/docs/releases/1.7.txt @@ -1051,6 +1051,10 @@ Miscellaneous which does allow primary keys with value 0. It only forbids *autoincrement* primary keys with value 0. +* :djadmin:`findstatic` now accepts verbosity flag level 2, meaning it will + show the directories on which it searched the relative paths. See + :djadmin:`findstatic` for example output. + .. _deprecated-features-1.7: Features deprecated in 1.7 diff --git a/tests/staticfiles_tests/tests.py b/tests/staticfiles_tests/tests.py index c67a4672b4..9261503726 100644 --- a/tests/staticfiles_tests/tests.py +++ b/tests/staticfiles_tests/tests.py @@ -223,6 +223,35 @@ class TestFindStatic(CollectionTestCase, TestDefaults): self.assertIn('project', force_text(lines[0])) self.assertIn('apps', force_text(lines[1])) + def test_all_files_more_verbose(self): + """ + Test that findstatic returns all candidate files if run without --first and -v2. + Also, test that findstatic returns the searched locations with -v2. + """ + out = six.StringIO() + call_command('findstatic', 'test/file.txt', verbosity=2, stdout=out) + out.seek(0) + lines = [l.strip() for l in out.readlines()] + self.assertIn('project', force_text(lines[1])) + self.assertIn('apps', force_text(lines[2])) + self.assertIn("Looking in the following locations:", force_text(lines[3])) + searched_locations = ', '.join(lines[4:]) + #AppDirectoriesFinder searched locations + self.assertIn(os.path.join('staticfiles_tests', 'apps', 'test', 'static'), + searched_locations) + self.assertIn(os.path.join('staticfiles_tests', 'apps', 'no_label', 'static'), + searched_locations) + self.assertIn(os.path.join('django', 'contrib', 'admin', 'static'), + searched_locations) + self.assertIn(os.path.join('django', 'tests', 'servers', 'another_app', 'static'), + searched_locations) + #FileSystemFinder searched locations + self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][1][1], searched_locations) + self.assertIn(TEST_SETTINGS['STATICFILES_DIRS'][0], searched_locations) + #DefaultStorageFinder searched locations + self.assertIn(os.path.join('staticfiles_tests', 'project', 'site_media', 'media'), + searched_locations) + class TestConfiguration(StaticFilesTestCase): def test_location_empty(self): @@ -779,6 +808,9 @@ class TestDefaultStorageFinder(StaticFilesTestCase, FinderTestCase): self.find_all = ('media-file.txt', [test_file_path]) +@override_settings(STATICFILES_FINDERS= + ('django.contrib.staticfiles.finders.FileSystemFinder',), + STATICFILES_DIRS=[os.path.join(TEST_ROOT, 'project', 'documents')]) class TestMiscFinder(TestCase): """ A few misc finder tests. @@ -805,6 +837,11 @@ class TestMiscFinder(TestCase): self.assertEqual(cache_info.hits, 9) self.assertEqual(cache_info.currsize, 1) + def test_searched_locations(self): + finders.find('spam') + self.assertEqual(finders.searched_locations, + [os.path.join(TEST_ROOT, 'project', 'documents')]) + @override_settings(STATICFILES_DIRS='a string') def test_non_tuple_raises_exception(self): """