Opened 11 years ago

Closed 11 years ago

#19046 closed Bug (needsinfo)

staticfiles not finding the files on Windows

Reported by: nelutzu_13@… Owned by: nobody
Component: contrib.staticfiles Version: 1.4
Severity: Normal Keywords:
Cc: Triage Stage: Accepted
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I'm using Django 1.4 on Windows XP and when a file is requested from STATIC_ROOT I get the following error:

Traceback (most recent call last):
  File "C:\Python27\lib\wsgiref\handlers.py", line 85, in run
    self.result = application(self.environ, self.start_response)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 68, in __call__
    return super(StaticFilesHandler, self).__call__(environ, start_response)
  File "C:\Python27\lib\site-packages\django\core\handlers\wsgi.py", line 241, in __call__
    response = self.get_response(request)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 58, in get_response
    return self.serve(request)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\handlers.py", line 51, in serve
    return serve(request, self.file_path(request.path), insecure=True)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\views.py", line 35, in serve
    absolute_path = finders.find(normalized_path)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 238, in find
    result = finder.find(path, all=all)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 78, in find
    matched_path = self.find_location(root, path, prefix)
  File "C:\Python27\lib\site-packages\django\contrib\staticfiles\finders.py", line 95, in find_location
    path = safe_join(root, path)
  File "C:\Python27\lib\site-packages\django\utils\_os.py", line 52, in safe_join
    'path component (%s)' % (final_path, base_path))
ValueError: The joined path (C:\images\common\fnurse.jpg) is located outside of the base path component (C:\DJANGO\webtech-proto-work\static)
[01/Oct/2012 09:34:06] "GET /static/images/common/fnurse.jpg HTTP/1.1" 500 59

I have traced the cause of the error to line 37 in staticfiles/views.py :

normalized_path = posixpath.normpath(unquote(path)).lstrip('/')

I have replaced the hardcoded '/' to os.sep and after that static files have been served on Windows. Please check if this change is correct and if it correct than please integrate it.

Change History (3)

comment:1 by Jannis Leidel, 11 years ago

Severity: NormalRelease blocker
Triage Stage: UnreviewedAccepted
Type: UncategorizedBug

comment:2 by Aymeric Augustin, 11 years ago

Easy pickings: unset
Severity: Release blockerNormal

It wasn't easy but I eventually managed to reproduce this problem.

Here's what I did:

  • Install VirtualBox, Windows, Python, Git, etc.
  • Start a new project
  • Set STATIC_ROOT = './static' (no trailing slash -- otherwise the bug doesn't happen), STATICFILES_DIRS = ('./static_src/',)
  • Comment out a few lines in django/conf/__init__.py to cancel the effects of fbfaa35fb0a2a1206221e00c56cf6136cf93b6f1 -- otherwise Django refuses to start
  • Create a file static_src/yay.txt
  • Launch the development server and go to http://localhost:8000/static/yay.txt

Since fbfaa35fb0a2a1206221e00c56cf6136cf93b6f1, Django requires STATIC_URL to end with a slash, so this problem cannot occur in Django 1.5. For this reason, I'm going to downgrade the severity.


However, I still feel there's something wrong with this line:

normalized_path = posixpath.normpath(unquote(path)).lstrip('/')

As far as I can tell:

  • The undocumented posixpath module from Python's standard library is used to perform path normalization with forward slashes, which seems suitable for URLs;
  • The .lstrip('/') is intended to strip the leading slash from the URL, in case STATIC_URL setting doesn't end in a slash.

I see two problems:

  • In the situation described above, the serve() function receives path = '\yay.txt' — the backslash has absolutely no reason for being here; everything's happening in "URL-space", not in "file-space" until this point;
  • The .lstrip('/') isn't necessary any longer since Django >= 1.5 requires STATIC_URL to end in a slash.

comment:3 by Jannis Leidel, 11 years ago

Resolution: needsinfo
Status: newclosed

posixpath is the module that is used on POSIX systems when import os.path. It uses the hardcoded forward slash and a couple of other things different to the ntpath module that does the same for Windows systems. In other words os.path just a facade for the platform specific tools. The different modules are documented on http://docs.python.org/2/library/os.path.html

The lstrip('/') happens there since the path passed to the serve view may be formatted as http://localhost:8000/static///someother/file.ext making path having a value of //someother/file.ext. The posix.normpath happens there to remove redundancies in the path (e.g. path/to/../some//file.ext is converted to path/some/file.ext). The finder api expects that kind of cleaned up name to make sure it does the right thing later on.

As I'm not able to reproduce the issue with the current code, I'm closing as needsinfo. A testcase that demostrates the issue would be appreciated, for example.

Note: See TracTickets for help on using tickets.
Back to Top