mirror of
https://github.com/jazzband/django-downloadview.git
synced 2026-03-16 22:40:25 +00:00
Introduced support of Python >= 3.3. Closes #46.
This commit is contained in:
commit
3b725c2ab4
13 changed files with 49 additions and 83 deletions
|
|
@ -1,6 +1,7 @@
|
|||
language: python
|
||||
env:
|
||||
- TOXENV=py27
|
||||
- TOXENV=py33
|
||||
- TOXENV=flake8
|
||||
- TOXENV=sphinx
|
||||
- TOXENV=readme
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ future releases, check `milestones`_ and :doc:`/about/vision`.
|
|||
1.6 (unreleased)
|
||||
----------------
|
||||
|
||||
- Feature #46: introduced support for Python>=3.3.
|
||||
|
||||
- Feature #74: the Makefile in project's repository no longer creates a
|
||||
virtualenv. Developers setup the environment as they like, i.e. using
|
||||
virtualenv, virtualenvwrapper or whatever. Tests are run with tox.
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
from StringIO import StringIO
|
||||
from six import StringIO
|
||||
|
||||
from django.core.files.base import ContentFile
|
||||
|
||||
|
|
@ -10,7 +10,7 @@ from django_downloadview import StringIteratorIO
|
|||
class TextDownloadView(VirtualDownloadView):
|
||||
def get_file(self):
|
||||
"""Return :class:`django.core.files.base.ContentFile` object."""
|
||||
return ContentFile(u"Hello world!\n", name='hello-world.txt')
|
||||
return ContentFile(b"Hello world!\n", name='hello-world.txt')
|
||||
|
||||
|
||||
class StringIODownloadView(VirtualDownloadView):
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from six import iteritems
|
||||
from django_downloadview.apache.response import XSendfileResponse
|
||||
|
||||
|
||||
|
|
@ -21,7 +22,7 @@ class XSendfileValidator(object):
|
|||
|
||||
"""
|
||||
self.assert_x_sendfile_response(test_case, response)
|
||||
for key, value in assertions.iteritems():
|
||||
for key, value in iteritems(assertions):
|
||||
assert_func = getattr(self, 'assert_%s' % key)
|
||||
assert_func(test_case, response, value)
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
"""File wrappers for use as exchange data between views and responses."""
|
||||
from __future__ import absolute_import
|
||||
from io import BytesIO
|
||||
from urlparse import urlparse
|
||||
from six.moves.urllib.parse import urlparse
|
||||
|
||||
from django.core.files.base import File
|
||||
from django.utils.encoding import force_bytes
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
from six import iteritems
|
||||
from django_downloadview.nginx.response import XAccelRedirectResponse
|
||||
|
||||
|
||||
|
|
@ -35,7 +36,7 @@ class XAccelRedirectValidator(object):
|
|||
|
||||
"""
|
||||
self.assert_x_accel_redirect_response(test_case, response)
|
||||
for key, value in assertions.iteritems():
|
||||
for key, value in iteritems(assertions):
|
||||
assert_func = getattr(self, 'assert_%s' % key)
|
||||
assert_func(test_case, response, value)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,7 +4,8 @@ import os
|
|||
import mimetypes
|
||||
import re
|
||||
import unicodedata
|
||||
import urllib
|
||||
import six
|
||||
from six.moves import urllib
|
||||
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponse, StreamingHttpResponse
|
||||
|
|
@ -12,30 +13,27 @@ from django.utils.encoding import force_str
|
|||
|
||||
|
||||
def encode_basename_ascii(value):
|
||||
"""Return US-ASCII encoded ``value`` for use in Content-Disposition header.
|
||||
u"""Return US-ASCII encoded ``value`` for Content-Disposition header.
|
||||
|
||||
>>> encode_basename_ascii(unicode('éà', 'utf-8'))
|
||||
u'ea'
|
||||
>>> print(encode_basename_ascii(u'éà'))
|
||||
ea
|
||||
|
||||
Spaces are converted to underscores.
|
||||
|
||||
>>> encode_basename_ascii(' ')
|
||||
u'_'
|
||||
|
||||
Text with non US-ASCII characters is expected to be unicode.
|
||||
|
||||
>>> encode_basename_ascii('éà') # doctest: +ELLIPSIS
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
UnicodeDecodeError: \'ascii\' codec can\'t decode byte ...
|
||||
>>> print(encode_basename_ascii(' '))
|
||||
_
|
||||
|
||||
Of course, ASCII values are not modified.
|
||||
|
||||
>>> encode_basename_ascii('ea')
|
||||
u'ea'
|
||||
>>> print(encode_basename_ascii('ea'))
|
||||
ea
|
||||
>>> print(encode_basename_ascii(b'ea'))
|
||||
ea
|
||||
|
||||
"""
|
||||
ascii_basename = unicode(value)
|
||||
if isinstance(value, six.binary_type):
|
||||
value = value.decode('utf-8')
|
||||
ascii_basename = six.text_type(value)
|
||||
ascii_basename = unicodedata.normalize('NFKD', ascii_basename)
|
||||
ascii_basename = ascii_basename.encode('ascii', 'ignore')
|
||||
ascii_basename = ascii_basename.decode('ascii')
|
||||
|
|
@ -44,34 +42,34 @@ def encode_basename_ascii(value):
|
|||
|
||||
|
||||
def encode_basename_utf8(value):
|
||||
"""Return UTF-8 encoded ``value`` for use in Content-Disposition header.
|
||||
u"""Return UTF-8 encoded ``value`` for use in Content-Disposition header.
|
||||
|
||||
>>> encode_basename_utf8(u' .txt')
|
||||
'%20.txt'
|
||||
>>> print(encode_basename_utf8(u' .txt'))
|
||||
%20.txt
|
||||
|
||||
>>> encode_basename_utf8(unicode('éà', 'utf-8'))
|
||||
'%C3%A9%C3%A0'
|
||||
>>> print(encode_basename_utf8(u'éà'))
|
||||
%C3%A9%C3%A0
|
||||
|
||||
"""
|
||||
return urllib.quote(force_str(value))
|
||||
return urllib.parse.quote(force_str(value))
|
||||
|
||||
|
||||
def content_disposition(filename):
|
||||
"""Return value of ``Content-Disposition`` header with 'attachment'.
|
||||
u"""Return value of ``Content-Disposition`` header with 'attachment'.
|
||||
|
||||
>>> content_disposition('demo.txt')
|
||||
'attachment; filename=demo.txt'
|
||||
>>> print(content_disposition('demo.txt'))
|
||||
attachment; filename=demo.txt
|
||||
|
||||
If filename is empty, only "attachment" is returned.
|
||||
|
||||
>>> content_disposition('')
|
||||
'attachment'
|
||||
>>> print(content_disposition(''))
|
||||
attachment
|
||||
|
||||
If filename contains non US-ASCII characters, the returned value contains
|
||||
UTF-8 encoded filename and US-ASCII fallback.
|
||||
|
||||
>>> content_disposition(unicode('é.txt', 'utf-8'))
|
||||
"attachment; filename=e.txt; filename*=UTF-8''%C3%A9.txt"
|
||||
>>> print(content_disposition(u'é.txt'))
|
||||
attachment; filename=e.txt; filename*=UTF-8''%C3%A9.txt
|
||||
|
||||
"""
|
||||
if not filename:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
"""Testing utilities."""
|
||||
import shutil
|
||||
from six import iteritems
|
||||
import tempfile
|
||||
|
||||
from django.conf import settings
|
||||
|
|
@ -101,7 +102,7 @@ class DownloadResponseValidator(object):
|
|||
|
||||
"""
|
||||
self.assert_download_response(test_case, response)
|
||||
for key, value in assertions.iteritems():
|
||||
for key, value in iteritems(assertions):
|
||||
assert_func = getattr(self, 'assert_%s' % key)
|
||||
assert_func(test_case, response, value)
|
||||
|
||||
|
|
@ -138,7 +139,9 @@ class DownloadResponseValidator(object):
|
|||
test_case.assertTrue(response['Content-Type'].startswith(value))
|
||||
|
||||
def assert_content(self, test_case, response, value):
|
||||
test_case.assertEqual(''.join(response.streaming_content), value)
|
||||
test_case.assertEqual(
|
||||
''.join([s.decode('utf-8') for s in response.streaming_content]),
|
||||
value)
|
||||
|
||||
def assert_attachment(self, test_case, response, value):
|
||||
test_case.assertEqual('attachment;' in response['Content-Disposition'],
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
"""Test suite around :mod:`django_downloadview.api` and deprecation plan."""
|
||||
import unittest
|
||||
from six.moves import reload_module as reload
|
||||
import warnings
|
||||
|
||||
from django.core.exceptions import ImproperlyConfigured
|
||||
|
|
|
|||
|
|
@ -26,8 +26,8 @@ def url_basename(url, content_type):
|
|||
|
||||
If URL contains extension, it is kept as-is.
|
||||
|
||||
>>> url_basename(u'/path/to/somefile.rst', 'text/plain')
|
||||
u'somefile.rst'
|
||||
>>> print(url_basename(u'/path/to/somefile.rst', 'text/plain'))
|
||||
somefile.rst
|
||||
|
||||
"""
|
||||
return url.split('/')[-1]
|
||||
|
|
@ -43,5 +43,5 @@ def import_member(import_string):
|
|||
|
||||
"""
|
||||
module_name, factory_name = str(import_string).rsplit('.', 1)
|
||||
module = __import__(module_name, globals(), locals(), [factory_name], -1)
|
||||
module = __import__(module_name, globals(), locals(), [factory_name], 0)
|
||||
return getattr(module, factory_name)
|
||||
|
|
|
|||
2
setup.py
2
setup.py
|
|
@ -35,7 +35,7 @@ KEYWORDS = ['file',
|
|||
'mod_xsendfile',
|
||||
'offload']
|
||||
PACKAGES = [NAME.replace('-', '_')]
|
||||
REQUIREMENTS = ['setuptools', 'Django>=1.5', 'requests']
|
||||
REQUIREMENTS = ['setuptools', 'Django>=1.5', 'requests', 'six']
|
||||
if IS_PYTHON2:
|
||||
REQUIREMENTS.append('mock')
|
||||
ENTRY_POINTS = {}
|
||||
|
|
|
|||
|
|
@ -50,44 +50,3 @@ class VersionTestCase(unittest.TestCase):
|
|||
'You may need to run ``make develop`` to update the '
|
||||
'installed version in development environment.'
|
||||
% (self.get_version(), file_version))
|
||||
|
||||
|
||||
class ReadMeTestCase(unittest.TestCase):
|
||||
"""Test suite around README file."""
|
||||
def test_readme_build(self):
|
||||
"""README builds to HTML without errors."""
|
||||
# Run build.
|
||||
import docutils.core
|
||||
import docutils.io
|
||||
source = open(os.path.join(project_dir, 'README.rst')).read()
|
||||
writer_name = 'html'
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
stderr_backup = sys.stderr
|
||||
sys.stderr = StringIO()
|
||||
output, pub = docutils.core.publish_programmatically(
|
||||
source=source,
|
||||
source_class=docutils.io.StringInput,
|
||||
source_path=None,
|
||||
destination_class=docutils.io.StringOutput,
|
||||
destination=None,
|
||||
destination_path=None,
|
||||
reader=None,
|
||||
reader_name='standalone',
|
||||
parser=None,
|
||||
parser_name='restructuredtext',
|
||||
writer=None,
|
||||
writer_name=writer_name,
|
||||
settings=None,
|
||||
settings_spec=None,
|
||||
settings_overrides=None,
|
||||
config_section=None,
|
||||
enable_exit_status=False)
|
||||
sys.stderr = stderr_backup
|
||||
errors = pub._stderr.stream.getvalue()
|
||||
# Check result.
|
||||
self.assertFalse(errors, "Docutils reported errors while building "
|
||||
"readme content from reStructuredText to "
|
||||
"HTML. So PyPI would display the readme as "
|
||||
"text instead of HTML. Errors are:\n%s"
|
||||
% errors)
|
||||
|
|
|
|||
4
tox.ini
4
tox.ini
|
|
@ -1,5 +1,5 @@
|
|||
[tox]
|
||||
envlist = py27,flake8,sphinx,readme
|
||||
envlist = py27,py33,flake8,sphinx,readme
|
||||
|
||||
[testenv]
|
||||
deps =
|
||||
|
|
@ -39,6 +39,6 @@ deps =
|
|||
pygments
|
||||
commands =
|
||||
mkdir -p var/docs
|
||||
rst2html.py --exit-status=2 README.rst var/docs/README.html
|
||||
rst2html.py --exit-status=2 README.rst var/docs/README.html
|
||||
whitelist_externals =
|
||||
mkdir
|
||||
|
|
|
|||
Loading…
Reference in a new issue