Merge pull request #147 from joehybird/master

Add Django 2.2, Django 3.0 support & Drop Django 1.5, 1.6, 1.7, 1.8, 1.9, 1.10, 2.0 and 2.1 as well as Python 3.4 and 3.5
This commit is contained in:
Rémy HUBSCHER 2020-01-07 14:39:30 +01:00 committed by GitHub
commit 31bb096a77
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
37 changed files with 182 additions and 130 deletions

View file

@ -1,28 +1,12 @@
language: python
dist: bionic
python:
- "3.5"
env:
- TOXENV=py27-django15
- TOXENV=py27-django16
- TOXENV=py27-django17
- TOXENV=py27-django18
- TOXENV=py27-django19
- TOXENV=py27-django110
- TOXENV=py27-django111
- TOXENV=py34-django15
- TOXENV=py34-django16
- TOXENV=py34-django17
- TOXENV=py34-django18
- TOXENV=py34-django19
- TOXENV=py34-django110
- TOXENV=py34-django111
- TOXENV=py35-django18
- TOXENV=py35-django19
- TOXENV=py34-django110
- TOXENV=py34-django111
- TOXENV=flake8
# - TOXENV=sphinx
# - TOXENV=readme
- 2.7
- 3.6
- 3.7
- 3.8
install:
- pip install tox
- pip install -q tox-travis
script:
- make test
- tox

View file

@ -8,7 +8,8 @@ future releases, check `milestones`_ and :doc:`/about/vision`.
1.10 (unreleased)
-----------------
- Nothing changed yet.
- Introduced support from Django 1.10 to 2.2.
- Drop support of Django 1.5, 1.6 & 1.7
1.9 (2016-03-15)

View file

@ -87,8 +87,8 @@ documentation. Maintain it along with code and documentation.
.. _`bugtracker`:
https://github.com/benoitbryon/django-downloadview/issues
.. _`rebase`: http://git-scm.com/book/en/Git-Branching-Rebasing
.. _`merge-based rebase`: http://tech.novapost.fr/psycho-rebasing-en.html
.. _`merge-based rebase`: https://tech.people-doc.com/psycho-rebasing.html
.. _`pip`: https://pypi.python.org/pypi/pip/
.. _`tox`: http://tox.testrun.org
.. _`tox`: https://tox.readthedocs.io/
.. _`Sphinx`: https://pypi.python.org/pypi/Sphinx/
.. _`zest.releaser`: https://pypi.python.org/pypi/zest.releaser/

View file

@ -1,11 +1,11 @@
import os
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
import django.test
from django_downloadview.apache import assert_x_sendfile
from demoproject.compat import reverse
from demoproject.apache.views import storage, storage_dir

View file

@ -1,8 +1,8 @@
"""URL mapping."""
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.apache import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(

View file

@ -0,0 +1,22 @@
from distutils.version import StrictVersion
from django.utils.version import get_version
try:
from django.conf.urls import patterns # noqa
except ImportError:
def patterns(prefix, *args):
return list(args)
try:
from django.urls import reverse # noqa
except ImportError:
from django.core.urlresolvers import reverse # noqa
if StrictVersion(get_version()) >= StrictVersion('2.0'):
from django.conf.urls import include as urlinclude # noqa
def include(arg, namespace=None, app_name=None):
return urlinclude((arg, app_name), namespace=namespace)
else:
from django.conf.urls import include # noqa

View file

@ -1,8 +1,9 @@
from django.core.urlresolvers import reverse
import django.test
from django_downloadview import assert_download_response
from demoproject.compat import reverse
class SimpleURLTestCase(django.test.TestCase):
def test_download_response(self):

View file

@ -1,7 +1,8 @@
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.http import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(
'',

View file

@ -1,11 +1,11 @@
import os
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
import django.test
from django_downloadview.lighttpd import assert_x_sendfile
from demoproject.compat import reverse
from demoproject.lighttpd.views import storage, storage_dir

View file

@ -1,8 +1,8 @@
"""URL mapping."""
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.lighttpd import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(

View file

@ -1,11 +1,11 @@
import os
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
import django.test
from django_downloadview.nginx import assert_x_accel_redirect
from demoproject.compat import reverse
from demoproject.nginx.views import storage, storage_dir

View file

@ -2,8 +2,8 @@
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.nginx import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(

View file

@ -1,9 +1,9 @@
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
import django.test
from django_downloadview import temporary_media_root, assert_download_response
from demoproject.compat import reverse
from demoproject.object.models import Document

View file

@ -1,7 +1,8 @@
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.object import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(
'',

View file

@ -1,8 +1,9 @@
from django.core.urlresolvers import reverse
import django.test
from django_downloadview import assert_download_response
from demoproject.compat import reverse
class StaticPathTestCase(django.test.TestCase):
def test_download_response(self):

View file

@ -1,7 +1,8 @@
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.path import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(
'',

View file

@ -64,14 +64,24 @@ INSTALLED_APPS = (
# BEGIN middlewares
MIDDLEWARE_CLASSES = [
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django_downloadview.SmartDownloadMiddleware'
]
if StrictVersion(get_version()) >= StrictVersion('1.10'):
MIDDLEWARE = [
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django_downloadview.SmartDownloadMiddleware'
]
else:
MIDDLEWARE_CLASSES = [
'django.middleware.common.CommonMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django_downloadview.SmartDownloadMiddleware'
]
# END middlewares

View file

@ -2,13 +2,13 @@ import datetime
import unittest
from django.core.files.base import ContentFile
from django.core.urlresolvers import reverse
from django.http.response import HttpResponseNotModified
import django.test
from django_downloadview import assert_download_response, temporary_media_root
from django_downloadview import setup_view
from demoproject.compat import reverse
from demoproject.storage import views

View file

@ -1,7 +1,7 @@
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.storage import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(

View file

@ -1,6 +1,7 @@
# coding=utf8
"""Test suite for demoproject.download."""
from django.core.urlresolvers import reverse
from demoproject.compat import reverse
from django.test import TestCase

View file

@ -1,5 +0,0 @@
try:
from django.conf.urls import patterns
except:
def patterns(prefix, *args):
return list(args)

View file

@ -1,7 +1,7 @@
from django.conf.urls import include, url
from django.conf.urls import url
from django.views.generic import TemplateView
from demoproject.urlpatterns import patterns
from demoproject.compat import patterns, include
home = TemplateView.as_view(template_name='home.html')

View file

@ -1,8 +1,9 @@
from django.core.urlresolvers import reverse
import django.test
from django_downloadview import assert_download_response
from demoproject.compat import reverse
class TextTestCase(django.test.TestCase):
def test_download_response(self):

View file

@ -1,7 +1,7 @@
from django.conf.urls import url
from demoproject.compat import patterns
from demoproject.virtual import views
from demoproject.urlpatterns import patterns
urlpatterns = patterns(

View file

@ -12,11 +12,13 @@ class XSendfileMiddleware(ProxiedDownloadMiddleware):
:py:class:`django_downloadview.decorators.DownloadDecorator`.
"""
def __init__(self, source_dir=None, source_url=None, destination_dir=None):
def __init__(self,
get_response=None,
source_dir=None, source_url=None, destination_dir=None):
"""Constructor."""
super(XSendfileMiddleware, self).__init__(source_dir,
source_url,
destination_dir)
super(XSendfileMiddleware, self).__init__(
get_response, source_dir, source_url, destination_dir
)
def process_download_response(self, request, response):
"""Replace DownloadResponse instances by XSendfileResponse ones."""

View file

@ -1,6 +1,7 @@
# -*- coding: utf-8 -*-
"""File wrappers for use as exchange data between views and responses."""
from __future__ import absolute_import
from io import BytesIO
from six.moves.urllib.parse import urlparse
@ -122,7 +123,10 @@ class StorageFile(File):
Proxy to self.storage.accessed_time(self.name).
"""
return self.storage.accessed(self.name)
try:
return self.storage.get_accessed_time(self.name)
except AttributeError:
return self.storage.accessed_time(self.name)
@property
def created_time(self):
@ -131,7 +135,10 @@ class StorageFile(File):
Proxy to self.storage.created_time(self.name).
"""
return self.storage.created_time(self.name)
try:
return self.storage.get_created_time(self.name)
except AttributeError:
return self.storage.created_time(self.name)
@property
def modified_time(self):
@ -140,7 +147,10 @@ class StorageFile(File):
Proxy to self.storage.modified_time(self.name).
"""
return self.storage.modified_time(self.name)
try:
return self.storage.get_modified_time(self.name)
except AttributeError:
return self.storage.modified_time(self.name)
class VirtualFile(File):

View file

@ -40,38 +40,38 @@ class TextIteratorIO(io.TextIOBase):
def read(self, n=None):
"""Return content up to ``n`` length."""
l = []
chunks = []
if n is None or n < 0:
while True:
m = self._read1()
if not m:
break
l.append(m)
chunks.append(m)
else:
while n > 0:
m = self._read1(n)
if not m:
break
n -= len(m)
l.append(m)
return u''.join(l)
chunks.append(m)
return u''.join(chunks)
def readline(self):
l = []
chunks = []
while True:
i = self._left.find(u'\n')
if i == -1:
l.append(self._left)
chunks.append(self._left)
try:
self._left = next(self._iter)
except StopIteration:
self._left = u''
break
else:
l.append(self._left[:i + 1])
chunks.append(self._left[:i + 1])
self._left = self._left[i + 1:]
break
return u''.join(l)
return u''.join(chunks)
class BytesIteratorIO(io.BytesIO):
@ -108,35 +108,35 @@ class BytesIteratorIO(io.BytesIO):
def read(self, n=None):
"""Return content up to ``n`` length."""
l = []
chunks = []
if n is None or n < 0:
while True:
m = self._read1()
if not m:
break
l.append(m)
chunks.append(m)
else:
while n > 0:
m = self._read1(n)
if not m:
break
n -= len(m)
l.append(m)
return b''.join(l)
chunks.append(m)
return b''.join(chunks)
def readline(self):
l = []
chunks = []
while True:
i = self._left.find(b'\n')
if i == -1:
l.append(self._left)
chunks.append(self._left)
try:
self._left = next(self._iter)
except StopIteration:
self._left = b''
break
else:
l.append(self._left[:i + 1])
chunks.append(self._left[:i + 1])
self._left = self._left[i + 1:]
break
return b''.join(l)
return b''.join(chunks)

View file

@ -12,11 +12,13 @@ class XSendfileMiddleware(ProxiedDownloadMiddleware):
:py:class:`django_downloadview.decorators.DownloadDecorator`.
"""
def __init__(self, source_dir=None, source_url=None, destination_dir=None):
def __init__(self,
get_response=None,
source_dir=None, source_url=None, destination_dir=None):
"""Constructor."""
super(XSendfileMiddleware, self).__init__(source_dir,
source_url,
destination_dir)
super(XSendfileMiddleware, self).__init__(
get_response, source_dir, source_url, destination_dir
)
def process_download_response(self, request, response):
"""Replace DownloadResponse instances by XSendfileResponse ones."""

View file

@ -12,6 +12,13 @@ import os
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
try:
from django.utils.deprecation import MiddlewareMixin
except ImportError:
class MiddlewareMixin(object):
def __init__(self, get_response=None):
super(MiddlewareMixin, self).__init__()
from django_downloadview.response import DownloadResponse
from django_downloadview.utils import import_member
@ -31,7 +38,7 @@ def is_download_response(response):
return isinstance(response, DownloadResponse)
class BaseDownloadMiddleware(object):
class BaseDownloadMiddleware(MiddlewareMixin):
"""Base (abstract) Django middleware that handles download responses.
Subclasses **must** implement :py:meth:`process_download_response` method.
@ -80,7 +87,8 @@ class RealDownloadMiddleware(BaseDownloadMiddleware):
class DownloadDispatcherMiddleware(BaseDownloadMiddleware):
"Download middleware that dispatches job to several middleware instances."
def __init__(self, middlewares=AUTO_CONFIGURE):
def __init__(self, get_response=None, middlewares=AUTO_CONFIGURE):
super(DownloadDispatcherMiddleware, self).__init__(get_response)
#: List of children middlewares.
self.middlewares = middlewares
if self.middlewares is AUTO_CONFIGURE:
@ -106,9 +114,11 @@ class DownloadDispatcherMiddleware(BaseDownloadMiddleware):
class SmartDownloadMiddleware(BaseDownloadMiddleware):
"""Easy to configure download middleware."""
def __init__(self,
get_response=None,
backend_factory=AUTO_CONFIGURE,
backend_options=AUTO_CONFIGURE):
"""Constructor."""
super(SmartDownloadMiddleware, self).__init__(get_response)
#: :class:`DownloadDispatcher` instance that can hold multiple
#: backend instances.
self.dispatcher = DownloadDispatcherMiddleware(middlewares=[])
@ -165,8 +175,14 @@ class NoRedirectionMatch(Exception):
class ProxiedDownloadMiddleware(RealDownloadMiddleware):
"""Base class for middlewares that use optimizations of reverse proxies."""
def __init__(self, source_dir=None, source_url=None, destination_url=None):
def __init__(self,
get_response=None,
source_dir=None,
source_url=None,
destination_url=None):
"""Constructor."""
super(ProxiedDownloadMiddleware, self).__init__(get_response)
self.source_dir = source_dir
self.source_url = source_url
self.destination_url = destination_url

View file

@ -17,7 +17,9 @@ class XAccelRedirectMiddleware(ProxiedDownloadMiddleware):
:py:class:`django_downloadview.decorators.DownloadDecorator`.
"""
def __init__(self, source_dir=None, source_url=None, destination_url=None,
def __init__(self,
get_response=None,
source_dir=None, source_url=None, destination_url=None,
expires=None, with_buffering=None, limit_rate=None,
media_root=None, media_url=None):
"""Constructor."""
@ -42,9 +44,10 @@ class XAccelRedirectMiddleware(ProxiedDownloadMiddleware):
source_dir = source_dir
else:
source_dir = source_dir
super(XAccelRedirectMiddleware, self).__init__(source_dir,
source_url,
destination_url)
super(XAccelRedirectMiddleware, self).__init__(
get_response, source_dir, source_url, destination_url)
self.expires = expires
self.with_buffering = with_buffering
self.limit_rate = limit_rate
@ -105,13 +108,14 @@ class SingleXAccelRedirectMiddleware(XAccelRedirectMiddleware):
Replaced by ``NGINX_DOWNLOAD_MIDDLEWARE_DESTINATION_URL``.
"""
def __init__(self):
def __init__(self, get_response=None):
"""Use Django settings as configuration."""
if settings.NGINX_DOWNLOAD_MIDDLEWARE_DESTINATION_URL is None:
raise ImproperlyConfigured(
'settings.NGINX_DOWNLOAD_MIDDLEWARE_DESTINATION_URL is '
'required by %s middleware' % self.__class__.__name__)
super(SingleXAccelRedirectMiddleware, self).__init__(
get_response=get_response,
source_dir=settings.NGINX_DOWNLOAD_MIDDLEWARE_SOURCE_DIR,
source_url=settings.NGINX_DOWNLOAD_MIDDLEWARE_SOURCE_URL,
destination_url=settings.NGINX_DOWNLOAD_MIDDLEWARE_DESTINATION_URL,

View file

@ -16,11 +16,20 @@ from django.core.exceptions import ImproperlyConfigured
# In version 1.3, former XAccelRedirectMiddleware has been renamed to
# SingleXAccelRedirectMiddleware. So tell the users.
middleware = 'django_downloadview.nginx.XAccelRedirectMiddleware'
if middleware in settings.MIDDLEWARE_CLASSES:
deprecated_middleware = 'django_downloadview.nginx.XAccelRedirectMiddleware'
def get_middlewares():
try:
return settings.MIDDLEWARE
except AttributeError:
return settings.MIDDLEWARE_CLASSES
if deprecated_middleware in get_middlewares():
raise ImproperlyConfigured(
'{middleware} middleware has been renamed as of django-downloadview '
'version 1.3. You may use '
'{deprecated_middleware} middleware has been renamed as of '
'django-downloadview version 1.3. You may use '
'"django_downloadview.nginx.SingleXAccelRedirectMiddleware" instead, '
'or upgrade to "django_downloadview.SmartDownloadDispatcher". ')

View file

@ -116,7 +116,8 @@ class DownloadMixin(object):
modification_time = calendar.timegm(
file_instance.modified_time.utctimetuple())
size = file_instance.size
except (AttributeError, NotImplementedError):
except (AttributeError, NotImplementedError) as e:
print("!=======!", e)
return True
else:
return was_modified_since(since, modification_time, size)

View file

@ -75,6 +75,6 @@ Here are main differences between the two projects:
.. target-notes::
.. _`django.contrib.staticfiles provides a view to serve files`:
https://docs.djangoproject.com/en/1.9/ref/contrib/staticfiles/#static-file-development-view
https://docs.djangoproject.com/en/3.0/ref/contrib/staticfiles/#static-file-development-view
.. _`Django ticket #2131`: https://code.djangoproject.com/ticket/2131
.. _`django-sendfile`: http://pypi.python.org/pypi/django-sendfile

View file

@ -127,4 +127,4 @@ TextIteratorIO
.. target-notes::
.. _`Django itself provides some file wrappers`:
https://docs.djangoproject.com/en/1.9/ref/files/file/
https://docs.djangoproject.com/en/3.0/ref/files/file/

View file

@ -40,9 +40,10 @@ CLASSIFIERS = [
'Framework :: Django',
'License :: OSI Approved :: BSD License',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
]
KEYWORDS = ['file',
'stream',
@ -58,7 +59,7 @@ KEYWORDS = ['file',
PACKAGES = [NAME.replace('-', '_')]
REQUIREMENTS = [
# BEGIN requirements
'Django>=1.5',
'Django>=1.11',
'requests',
'setuptools',
'six',

View file

@ -96,7 +96,9 @@ class DeprecatedAPITestCase(django.test.SimpleTestCase):
"XAccelRedirectMiddleware in settings triggers ImproperlyConfigured."
with override_settings(
MIDDLEWARE_CLASSES=[
'django_downloadview.nginx.XAccelRedirectMiddleware']):
'django_downloadview.nginx.XAccelRedirectMiddleware'],
MIDDLEWARE=[
'django_downloadview.nginx.XAccelRedirectMiddleware'],):
with self.assertRaises(ImproperlyConfigured):
import django_downloadview.nginx.settings
reload(django_downloadview.nginx.settings)

34
tox.ini
View file

@ -1,46 +1,34 @@
[tox]
envlist = py{27}-django{15,16,17,18,19,110,111}, py{33,34}-django{15,16,17}, py{34,35}-django{18,19,110,111}, flake8, sphinx, readme
envlist = py27-django111,
py{35,36,36,37,38}-django{22,30},
flake8, sphinx, readme
[travis]
python=
3.8: py38, flake8, sphinx, readme
[testenv]
basepython =
py27: python2.7
py33: python3.3
py34: python3.4
py35: python3.5
deps =
coverage
django15: Django>=1.5,<1.6
django15: django-nose<1.4.3
django16: Django>=1.6,<1.7
django16: django-nose<1.4.3
django17: Django>=1.7,<1.8
django17: django-nose<1.4.3
django18: Django>=1.8,<1.9
django18: django-nose>=1.4.2
django19: Django>=1.9,<1.10
django19: django-nose>=1.4.3
django110: Django>=1.10,<1.11
django110: django-nose>=1.4.4
django111: Django>=1.11,<2.0
django111: django-nose>=1.4.5
django22: Django>=2.2,<3.0
django30: Django>=3.0,<3.1
nose
py27: mock
commands =
pip install -e .
pip install -e demo
demo test --cover-package=django_downloadview --cover-package=demoproject {posargs: tests demoproject}
python -Wd {envbindir}/demo test --cover-package=django_downloadview --cover-package=demoproject {posargs: tests demoproject}
coverage erase
pip freeze
[testenv:flake8]
basepython = python3.5
deps =
flake8
commands =
flake8 demo django_downloadview tests
[testenv:sphinx]
basepython = python3.5
deps =
Sphinx
commands =
@ -50,7 +38,6 @@ whitelist_externals =
make
[testenv:readme]
basepython = python3.5
deps =
docutils
pygments
@ -62,7 +49,6 @@ whitelist_externals =
mkdir
[testenv:release]
basepython = python3.5
deps =
wheel
zest.releaser