mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-03-16 22:40:25 +00:00
Run pre-commit on all files
This commit is contained in:
parent
dd35f867e0
commit
711b2e50b5
53 changed files with 90 additions and 56 deletions
|
|
@ -11,7 +11,7 @@ This is a `Jazzband <https://jazzband.co>`_ project. By contributing you agree t
|
|||
|
||||
|
||||
This document provides guidelines for people who want to contribute to
|
||||
`django-downloadview`.
|
||||
``django-downloadview``.
|
||||
|
||||
|
||||
**************
|
||||
|
|
@ -50,7 +50,7 @@ Use topic branches
|
|||
Fork, clone
|
||||
***********
|
||||
|
||||
Clone `django-downloadview` repository (adapt to use your own fork):
|
||||
Clone ``django-downloadview`` repository (adapt to use your own fork):
|
||||
|
||||
.. code:: sh
|
||||
|
||||
|
|
@ -62,7 +62,7 @@ Clone `django-downloadview` repository (adapt to use your own fork):
|
|||
Usual actions
|
||||
*************
|
||||
|
||||
The `Makefile` is the reference card for usual actions in development
|
||||
The ``Makefile`` is the reference card for usual actions in development
|
||||
environment:
|
||||
|
||||
* Install development toolkit with `pip`_: ``make develop``.
|
||||
|
|
@ -70,7 +70,7 @@ environment:
|
|||
* Run tests with `tox`_: ``make test``.
|
||||
|
||||
* Build documentation: ``make documentation``. It builds `Sphinx`_
|
||||
documentation in `var/docs/html/index.html`.
|
||||
documentation in ``var/docs/html/index.html``.
|
||||
|
||||
* Release project with `zest.releaser`_: ``make release``.
|
||||
|
||||
|
|
@ -84,7 +84,7 @@ See also ``make help``.
|
|||
Demo project included
|
||||
*********************
|
||||
|
||||
The `demo` included in project's repository is part of the tests and
|
||||
The ``demo`` included in project's repository is part of the tests and
|
||||
documentation. Maintain it along with code and documentation.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -26,16 +26,16 @@ django-downloadview
|
|||
:target: https://codecov.io/gh/jazzband/django-downloadview
|
||||
:alt: Coverage
|
||||
|
||||
`django-downloadview` makes it easy to serve files with `Django`_:
|
||||
``django-downloadview`` makes it easy to serve files with `Django`_:
|
||||
|
||||
* you manage files with Django (permissions, filters, generation, ...);
|
||||
|
||||
* files are stored somewhere or generated somehow (local filesystem, remote
|
||||
storage, memory...);
|
||||
|
||||
* `django-downloadview` helps you stream the files with very little code;
|
||||
* ``django-downloadview`` helps you stream the files with very little code;
|
||||
|
||||
* `django-downloadview` helps you improve performances with reverse proxies,
|
||||
* ``django-downloadview`` helps you improve performances with reverse proxies,
|
||||
via mechanisms such as Nginx's X-Accel or Apache's X-Sendfile.
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@ Demo project
|
|||
############
|
||||
|
||||
`Demo folder in project's repository`_ contains a Django project to illustrate
|
||||
`django-downloadview` usage.
|
||||
``django-downloadview`` usage.
|
||||
|
||||
|
||||
*****************************************
|
||||
|
|
|
|||
|
|
@ -58,4 +58,4 @@ class ModifiedHeadersTestCase(django.test.TestCase):
|
|||
basename="hello-world.txt",
|
||||
file_path="/apache-modified-headers/hello-world.txt",
|
||||
)
|
||||
self.assertEqual(response['X-Test'], 'header')
|
||||
self.assertEqual(response["X-Test"], "header")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""URL mapping."""
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from demoproject.apache import views
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ optimized_by_decorator = x_sendfile(
|
|||
def _modified_headers(request):
|
||||
view = StorageDownloadView.as_view(storage=storage, path="hello-world.txt")
|
||||
response = view(request)
|
||||
response["X-Test"] = 'header'
|
||||
response["X-Test"] = "header"
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -58,4 +58,4 @@ class ModifiedHeadersTestCase(django.test.TestCase):
|
|||
basename="hello-world.txt",
|
||||
file_path="/lighttpd-modified-headers/hello-world.txt",
|
||||
)
|
||||
self.assertEqual(response['X-Test'], 'header')
|
||||
self.assertEqual(response["X-Test"], "header")
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""URL mapping."""
|
||||
|
||||
from django.urls import path
|
||||
|
||||
from demoproject.lighttpd import views
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ optimized_by_decorator = x_sendfile(
|
|||
def _modified_headers(request):
|
||||
view = StorageDownloadView.as_view(storage=storage, path="hello-world.txt")
|
||||
response = view(request)
|
||||
response["X-Test"] = 'header'
|
||||
response["X-Test"] = "header"
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -70,4 +70,4 @@ class ModifiedHeadersTestCase(django.test.TestCase):
|
|||
with_buffering=None,
|
||||
limit_rate=None,
|
||||
)
|
||||
self.assertEqual(response['X-Test'], 'header')
|
||||
self.assertEqual(response["X-Test"], "header")
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ optimized_by_decorator = x_accel_redirect(
|
|||
def _modified_headers(request):
|
||||
view = StorageDownloadView.as_view(storage=storage, path="hello-world.txt")
|
||||
response = view(request)
|
||||
response["X-Test"] = 'header'
|
||||
response["X-Test"] = "header"
|
||||
return response
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Django settings for django-downloadview demo project."""
|
||||
|
||||
import os
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -44,8 +44,7 @@ class StaticPathTestCase(django.test.TestCase):
|
|||
url = reverse("storage:static_path", kwargs={"path": "1.txt"})
|
||||
year = datetime.date.today().year + 4
|
||||
response = self.client.get(
|
||||
url,
|
||||
HTTP_IF_MODIFIED_SINCE=f"Sat, 29 Oct {year} 19:43:31 GMT",
|
||||
url, headers={"if-modified-since": f"Sat, 29 Oct {year} 19:43:31 GMT"}
|
||||
)
|
||||
self.assertTrue(isinstance(response, HttpResponseNotModified))
|
||||
|
||||
|
|
@ -55,7 +54,7 @@ class StaticPathTestCase(django.test.TestCase):
|
|||
setup_file("1.txt")
|
||||
url = reverse("storage:static_path", kwargs={"path": "1.txt"})
|
||||
response = self.client.get(
|
||||
url, HTTP_IF_MODIFIED_SINCE="Sat, 29 Oct 1980 19:43:31 GMT"
|
||||
url, headers={"if-modified-since": "Sat, 29 Oct 1980 19:43:31 GMT"}
|
||||
)
|
||||
assert_download_response(
|
||||
self,
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Test suite for demoproject.download."""
|
||||
|
||||
from django.test import TestCase
|
||||
from django.urls import reverse
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ middleware here, or combine a Django application with an application of another
|
|||
framework.
|
||||
|
||||
"""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.wsgi import get_wsgi_application
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Serve files with Django and reverse proxies."""
|
||||
|
||||
from django_downloadview.api import * # NoQA
|
||||
|
||||
import importlib.metadata
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ See also `documentation of mod_xsendfile for Apache
|
|||
Apache optimizations </optimizations/apache>`.
|
||||
|
||||
"""
|
||||
|
||||
# API shortcuts.
|
||||
from django_downloadview.apache.decorators import x_sendfile # NoQA
|
||||
from django_downloadview.apache.middlewares import XSendfileMiddleware # NoQA
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Decorators to apply Apache X-Sendfile on a specific view."""
|
||||
|
||||
from django_downloadview.apache.middlewares import XSendfileMiddleware
|
||||
from django_downloadview.decorators import DownloadDecorator
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Apache's specific responses."""
|
||||
|
||||
import os.path
|
||||
|
||||
from django_downloadview.response import ProxiedDownloadResponse, content_disposition
|
||||
|
|
@ -7,11 +8,13 @@ from django_downloadview.response import ProxiedDownloadResponse, content_dispos
|
|||
class XSendfileResponse(ProxiedDownloadResponse):
|
||||
"Delegates serving file to Apache via X-Sendfile header."
|
||||
|
||||
def __init__(self, file_path, content_type, basename=None, attachment=True, headers=None):
|
||||
def __init__(
|
||||
self, file_path, content_type, basename=None, attachment=True, headers=None
|
||||
):
|
||||
"""Return a HttpResponse with headers for Apache X-Sendfile."""
|
||||
# content-type must be provided only as keyword argument to response
|
||||
if headers and content_type:
|
||||
headers.pop('Content-Type', None)
|
||||
headers.pop("Content-Type", None)
|
||||
super().__init__(content_type=content_type, headers=headers)
|
||||
if attachment:
|
||||
self.basename = basename or os.path.basename(file_path)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
# flake8: noqa
|
||||
"""Declaration of API shortcuts."""
|
||||
|
||||
from django_downloadview.files import HTTPFile, StorageFile, VirtualFile
|
||||
from django_downloadview.io import BytesIteratorIO, TextIteratorIO
|
||||
from django_downloadview.middlewares import (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""File wrappers for use as exchange data between views and responses."""
|
||||
|
||||
from io import BytesIO
|
||||
from urllib.parse import urlparse
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Low-level IO operations, for use with file wrappers."""
|
||||
|
||||
import io
|
||||
|
||||
from django.utils.encoding import force_bytes, force_str
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ See also `documentation of X-Sendfile for Lighttpd
|
|||
</optimizations/lighttpd>`.
|
||||
|
||||
"""
|
||||
|
||||
# API shortcuts.
|
||||
from django_downloadview.lighttpd.decorators import x_sendfile # NoQA
|
||||
from django_downloadview.lighttpd.middlewares import XSendfileMiddleware # NoQA
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Decorators to apply Lighttpd X-Sendfile on a specific view."""
|
||||
|
||||
from django_downloadview.decorators import DownloadDecorator
|
||||
from django_downloadview.lighttpd.middlewares import XSendfileMiddleware
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Lighttpd's specific responses."""
|
||||
|
||||
import os.path
|
||||
|
||||
from django_downloadview.response import ProxiedDownloadResponse, content_disposition
|
||||
|
|
@ -7,11 +8,13 @@ from django_downloadview.response import ProxiedDownloadResponse, content_dispos
|
|||
class XSendfileResponse(ProxiedDownloadResponse):
|
||||
"Delegates serving file to Lighttpd via X-Sendfile header."
|
||||
|
||||
def __init__(self, file_path, content_type, basename=None, attachment=True, headers=None):
|
||||
def __init__(
|
||||
self, file_path, content_type, basename=None, attachment=True, headers=None
|
||||
):
|
||||
"""Return a HttpResponse with headers for Lighttpd X-Sendfile."""
|
||||
# content-type must be porvided only as keyword argument to response
|
||||
if headers and content_type:
|
||||
headers.pop('Content-Type', None)
|
||||
headers.pop("Content-Type", None)
|
||||
super().__init__(content_type=content_type, headers=headers)
|
||||
if attachment:
|
||||
self.basename = basename or os.path.basename(file_path)
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ Download middlewares capture :py:class:`django_downloadview.DownloadResponse`
|
|||
responses and may replace them with optimized download responses.
|
||||
|
||||
"""
|
||||
|
||||
import collections.abc
|
||||
import copy
|
||||
import os
|
||||
|
|
@ -36,6 +37,7 @@ class BaseDownloadMiddleware:
|
|||
Subclasses **must** implement :py:meth:`process_download_response` method.
|
||||
|
||||
"""
|
||||
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
|
|
@ -75,9 +77,8 @@ class RealDownloadMiddleware(BaseDownloadMiddleware):
|
|||
whose file attribute have either an URL or a file name.
|
||||
|
||||
"""
|
||||
return (
|
||||
super().is_download_response(response)
|
||||
and bool(getattr(response.file, 'url', None) or getattr(response.file, 'name', None))
|
||||
return super().is_download_response(response) and bool(
|
||||
getattr(response.file, "url", None) or getattr(response.file, "name", None)
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -91,7 +92,7 @@ class DownloadDispatcher:
|
|||
def auto_configure_middlewares(self):
|
||||
"""Populate :attr:`middlewares` from
|
||||
``settings.DOWNLOADVIEW_MIDDLEWARES``."""
|
||||
for (key, import_string, kwargs) in getattr(
|
||||
for key, import_string, kwargs in getattr(
|
||||
settings, "DOWNLOADVIEW_MIDDLEWARES", []
|
||||
):
|
||||
factory = import_member(import_string)
|
||||
|
|
@ -100,7 +101,7 @@ class DownloadDispatcher:
|
|||
|
||||
def dispatch(self, request, response):
|
||||
"""Dispatches job to children middlewares."""
|
||||
for (key, middleware) in self.middlewares:
|
||||
for key, middleware in self.middlewares:
|
||||
response = middleware.process_response(request, response)
|
||||
return response
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ See also `Nginx X-accel documentation <http://wiki.nginx.org/X-accel>`_ and
|
|||
</optimizations/nginx>`.
|
||||
|
||||
"""
|
||||
|
||||
# API shortcuts.
|
||||
from django_downloadview.nginx.decorators import x_accel_redirect # NoQA
|
||||
from django_downloadview.nginx.middlewares import XAccelRedirectMiddleware # NoQA
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Decorators to apply Nginx X-Accel on a specific view."""
|
||||
|
||||
from django_downloadview.decorators import DownloadDecorator
|
||||
from django_downloadview.nginx.middlewares import XAccelRedirectMiddleware
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Nginx's specific responses."""
|
||||
|
||||
from datetime import timedelta
|
||||
|
||||
from django.utils.timezone import now
|
||||
|
|
@ -24,7 +25,7 @@ class XAccelRedirectResponse(ProxiedDownloadResponse):
|
|||
"""Return a HttpResponse with headers for Nginx X-Accel-Redirect."""
|
||||
# content-type must be porvided only as keyword argument to response
|
||||
if headers and content_type:
|
||||
headers.pop('Content-Type', None)
|
||||
headers.pop("Content-Type", None)
|
||||
super().__init__(content_type=content_type, headers=headers)
|
||||
if attachment:
|
||||
self.basename = basename or url_basename(redirect_url, content_type)
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@
|
|||
for details.
|
||||
|
||||
"""
|
||||
|
||||
import warnings
|
||||
|
||||
from django.conf import settings
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
""":py:class:`django.http.HttpResponse` subclasses."""
|
||||
|
||||
import mimetypes
|
||||
import os
|
||||
import re
|
||||
|
|
@ -77,11 +78,10 @@ def content_disposition(filename):
|
|||
# which can permit a reflected file download attack. The UTF-8
|
||||
# version is immune because it's not quoted.
|
||||
ascii_filename = (
|
||||
encode_basename_ascii(filename).replace("\\", "\\\\").replace('"', r'\"')
|
||||
encode_basename_ascii(filename).replace("\\", "\\\\").replace('"', r"\"")
|
||||
)
|
||||
utf8_filename = encode_basename_utf8(filename)
|
||||
if ascii_filename == utf8_filename: # ASCII only.
|
||||
|
||||
return f'attachment; filename="{ascii_filename}"'
|
||||
else:
|
||||
return (
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Port of django-sendfile in django-downloadview."""
|
||||
|
||||
from django_downloadview.views.path import PathDownloadView
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Utility functions that may be implemented in external packages."""
|
||||
|
||||
import re
|
||||
|
||||
charset_pattern = re.compile(r"charset=(?P<charset>.+)$", re.I | re.U)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Views to stream files."""
|
||||
|
||||
# API shortcuts.
|
||||
from django_downloadview.views.base import BaseDownloadView, DownloadMixin # NoQA
|
||||
from django_downloadview.views.http import HTTPDownloadView # NoQA
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""Base material for download views: :class:`DownloadMixin` and
|
||||
:class:`BaseDownloadView`"""
|
||||
|
||||
import calendar
|
||||
|
||||
from django.http import Http404, HttpResponseNotModified
|
||||
|
|
@ -156,7 +157,7 @@ class DownloadMixin(object):
|
|||
except exceptions.FileNotFound:
|
||||
return self.file_not_found_response()
|
||||
# Respect the If-Modified-Since header.
|
||||
since = self.request.META.get("HTTP_IF_MODIFIED_SINCE", None)
|
||||
since = self.request.headers.get("if-modified-since", None)
|
||||
if since is not None:
|
||||
if not self.was_modified_since(self.file_instance, since):
|
||||
return self.not_modified_response(**response_kwargs)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Stream files given an URL, i.e. files you want to proxy."""
|
||||
|
||||
from django_downloadview.files import HTTPFile
|
||||
from django_downloadview.views.base import BaseDownloadView
|
||||
|
||||
|
|
@ -44,5 +45,5 @@ class HTTPDownloadView(BaseDownloadView):
|
|||
request_factory=self.get_request_factory(),
|
||||
name=self.get_basename(),
|
||||
url=self.get_url(),
|
||||
**self.get_request_kwargs()
|
||||
**self.get_request_kwargs(),
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Stream files that live in models."""
|
||||
|
||||
from django.views.generic.detail import SingleObjectMixin
|
||||
|
||||
from django_downloadview.exceptions import FileNotFound
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
""":class:`PathDownloadView`."""
|
||||
|
||||
import os
|
||||
|
||||
from django.core.files import File
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Stream files from storage."""
|
||||
|
||||
from django.core.files.storage import DefaultStorage
|
||||
|
||||
from django_downloadview.files import StorageFile
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Stream files that you generate or that live in memory."""
|
||||
|
||||
from django_downloadview.views.base import BaseDownloadView
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""django-downloadview documentation build configuration file."""
|
||||
import os
|
||||
|
||||
import re
|
||||
|
||||
import importlib.metadata
|
||||
|
|
@ -51,7 +51,7 @@ author_slug = re.sub(r"([\w_.-]+)", "-", author)
|
|||
# The full version, including alpha/beta/rc tags.
|
||||
release = importlib.metadata.version("django-downloadview")
|
||||
# The short X.Y version.
|
||||
version = '.'.join(release.split('.')[:2])
|
||||
version = ".".join(release.split(".")[:2])
|
||||
|
||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||
# for a list of supported languages.
|
||||
|
|
|
|||
|
|
@ -41,4 +41,3 @@ Example, related to :doc:`StorageDownloadView demo </views/storage>`:
|
|||
.. literalinclude:: /../demo/demoproject/storage/tests.py
|
||||
:language: python
|
||||
:lines: 1-2, 8-12, 59-
|
||||
|
||||
|
|
|
|||
8
setup.py
8
setup.py
|
|
@ -10,7 +10,7 @@ setup(
|
|||
setup_requires=["setuptools_scm"],
|
||||
description="Serve files with Django and reverse-proxies.",
|
||||
long_description=open(os.path.join(here, "README.rst")).read(),
|
||||
long_description_content_type='text/x-rst',
|
||||
long_description_content_type="text/x-rst",
|
||||
classifiers=[
|
||||
"Development Status :: 5 - Production/Stable",
|
||||
"License :: OSI Approved :: BSD License",
|
||||
|
|
@ -20,9 +20,9 @@ setup(
|
|||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Programming Language :: Python :: 3.12",
|
||||
'Framework :: Django',
|
||||
'Framework :: Django :: 4.2',
|
||||
'Framework :: Django :: 5.0',
|
||||
"Framework :: Django",
|
||||
"Framework :: Django :: 4.2",
|
||||
"Framework :: Django :: 5.0",
|
||||
],
|
||||
keywords=" ".join(
|
||||
[
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Test suite around :mod:`django_downloadview.api` and deprecation plan."""
|
||||
|
||||
from importlib import import_module, reload
|
||||
import unittest
|
||||
import warnings
|
||||
|
|
@ -130,7 +131,7 @@ class DeprecatedAPITestCase(django.test.SimpleTestCase):
|
|||
reload(django_downloadview.nginx.settings)
|
||||
caught = False
|
||||
for warning_item in warning_list:
|
||||
if warning_item.category == DeprecationWarning:
|
||||
if warning_item.category is DeprecationWarning:
|
||||
if "deprecated" in str(warning_item.message):
|
||||
if setting_name in str(warning_item.message):
|
||||
caught = True
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Tests around :mod:`django_downloadview.io`."""
|
||||
|
||||
import unittest
|
||||
|
||||
from django_downloadview import BytesIteratorIO, TextIteratorIO
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Tests around project's distribution and packaging."""
|
||||
|
||||
import importlib.metadata
|
||||
import os
|
||||
import unittest
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Unit tests around responses."""
|
||||
|
||||
import unittest
|
||||
|
||||
from django_downloadview.response import DownloadResponse
|
||||
|
|
@ -23,12 +24,9 @@ class DownloadResponseTestCase(unittest.TestCase):
|
|||
def test_content_disposition_escaping(self):
|
||||
"""Content-Disposition headers escape special characters."""
|
||||
response = DownloadResponse(
|
||||
"fake file",
|
||||
attachment=True,
|
||||
basename=r'"malicious\file.exe'
|
||||
"fake file", attachment=True, basename=r'"malicious\file.exe'
|
||||
)
|
||||
headers = response.default_headers
|
||||
self.assertIn(
|
||||
r'filename="\"malicious\\file.exe"',
|
||||
headers["Content-Disposition"]
|
||||
r'filename="\"malicious\\file.exe"', headers["Content-Disposition"]
|
||||
)
|
||||
|
|
@ -1,4 +1,5 @@
|
|||
"""Tests around :py:mod:`django_downloadview.sendfile`."""
|
||||
|
||||
from django.http import Http404
|
||||
import django.test
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
"""Tests around :mod:`django_downloadview.views`."""
|
||||
|
||||
import calendar
|
||||
from datetime import datetime
|
||||
import os
|
||||
|
|
|
|||
Loading…
Reference in a new issue