Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
Django 3.0 support (#1603)
* Fix broken imports, some warnings

* Fix tests broken by changed single-quote escaping

* Loosen number of queries assertion to allow less than, as django 3.0 optimized away one query

* Update travis to run against django 3.0

* flake8

* Compat for contextmanager

* contextmanager didn't move, IDE was confused

* Build docs on 2.2 until gdal bug fixed

* Get debug output from travis about currently installed packages

* Fix less-than assertion

* Temporarily skip building docs under django 3.0

* Fix exclusion-by-env line

* Undo attempt to workaround GDAL; exclusion line isn't going to work

* Revert useless exclusion attempt

* Attempt updating gdal from PPA

* Try building on bionic

* Drop 3.5 support for Django 3.0

* Try other specifier for mod_spatialite

* Exclude python 3.5 from django 3.0 matrix

* Fix for force_str vs force_text under python 2

* Missing django for py35-docs

* Fix examples using render_to_response (deprecated) in docs

* Update docs, version, release notes
  • Loading branch information
georgedorn committed Jan 6, 2020
1 parent b78417e commit 2700cdc
Show file tree
Hide file tree
Showing 28 changed files with 135 additions and 82 deletions.
21 changes: 12 additions & 9 deletions .travis.yml
@@ -1,5 +1,5 @@
sudo: false
dist: xenial
dist: bionic

language: python

Expand All @@ -14,8 +14,8 @@ env:
- MODE=flake8-strict
- MODE=docs
- DJANGO_VERSION=dj111
- DJANGO_VERSION=dj21
- DJANGO_VERSION=dj22
- DJANGO_VERSION=dj30
- DJANGO_VERSION=djdev

matrix:
Expand All @@ -25,16 +25,18 @@ matrix:
exclude:
- python: "2.7"
env: DJANGO_VERSION=djdev
- python: "2.7"
env: DJANGO_VERSION=dj21
- python: "2.7"
env: DJANGO_VERSION=dj22
- python: "2.7"
env: DJANGO_VERSION=dj30
- python: "3.5"
env: DJANGO_VERSION=dj30
- python: "3.5"
env: DJANGO_VERSION=djdev

before_install:
- sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 762E3157

addons:
apt:
packages:
Expand All @@ -52,14 +54,15 @@ before_cache:

# command to install dependencies
install:
- pip install -U pip
- pip install -U wheel virtualenv
- pip install tox coveralls

- pip install -U pip
- pip install -U wheel virtualenv
- pip install tox coveralls
after_success:
- coveralls

# command to run tests
script:
- apt list --installed
- coverage erase
- tox -e py${TRAVIS_PYTHON_VERSION/./}-${DJANGO_VERSION}${MODE}
6 changes: 3 additions & 3 deletions README.rst
Expand Up @@ -24,7 +24,7 @@ django-tastypie

Creating delicious APIs for Django apps since 2010.

Currently in beta (v0.14.2) but being used actively in production on several
Currently in beta but being used actively in production on several
sites.


Expand All @@ -34,8 +34,8 @@ Requirements
Core
----

* Python 2.7+ or Python 3.4+
* Django - 1.11 (LTS), 2.1, or 2.2 (LTS). The last two LTS and the last incremental release are supported.
* Python 2.7+ or Python 3.4+ (Whatever is supported by your version of Django)
* Django 1.11, 2.2 (LTS releases) or Django 3.0 (latest release)
* dateutil (http://labix.org/python-dateutil) >= 2.1

Format Support
Expand Down
8 changes: 4 additions & 4 deletions docs/cookbook.rst
Expand Up @@ -121,7 +121,7 @@ not include a valid ``resource_uri``:
.. testcode::

# views.py
from django.shortcuts import render_to_response
from django.shortcuts import render
from myapp.api.resources import UserResource


Expand All @@ -135,7 +135,7 @@ not include a valid ``resource_uri``:
user_bundle = res.build_bundle(request=request, obj=user)
user_json = res.serialize(None, res.full_dehydrate(user_bundle), "application/json")

return render_to_response("myapp/user_detail.html", {
return render(request, "myapp/user_detail.html", {
# Other things here.
"user_json": user_json,
})
Expand Down Expand Up @@ -179,7 +179,7 @@ parameter directly to the Resource:
.. testcode::

# views.py
from django.shortcuts import render_to_response
from django.shortcuts import render
from myapp.api.resources import UserResource


Expand Down Expand Up @@ -209,7 +209,7 @@ Example of getting a list of users:

list_json = res.serialize(None, bundles, "application/json")

return render_to_response('myapp/user_list.html', {
return render(request, 'myapp/user_list.html', {
# Other things here.
"list_json": list_json,
})
Expand Down
4 changes: 2 additions & 2 deletions docs/index.rst
Expand Up @@ -83,8 +83,8 @@ Requirements
Core
----

* Python 2.7+ or Python 3.4+
* Django 1.11 (LTS releases) or Django 2.0+ (requires Python 3.4+)
* Python 2.7+ or Python 3.4+ (Whatever is supported by your version of Django)
* Django 1.11, 2.2 (LTS releases) or Django 3.0 (latest release)
* dateutil (http://labix.org/python-dateutil) >= 2.1

Format Support
Expand Down
2 changes: 0 additions & 2 deletions docs/release_notes/dev.rst
Expand Up @@ -7,8 +7,6 @@ copied to the release notes for the next release.
Major changes
-------------

* Dropped support for Django 1.8 (EOL).
* Compatability fixes for upstream changes, most notably the removal of QUERY_TERMS.

Bugfixes
--------
Expand Down
1 change: 1 addition & 0 deletions docs/release_notes/index.rst
Expand Up @@ -5,6 +5,7 @@ Release Notes
:maxdepth: 1

dev
v0.14.3
v0.14.2
v0.14.1
v0.14.0
Expand Down
9 changes: 9 additions & 0 deletions docs/release_notes/v0.14.3.rst
@@ -0,0 +1,9 @@
v0.14.3
=======

:date: 2019-12-16

Added support for Django 3.0; minor bugs and regressions fixed.
Drops explicit support for Django 2.1 (non-LTS).

This will be the last version to explicitly support Python 2.0.
2 changes: 1 addition & 1 deletion tastypie/__init__.py
Expand Up @@ -3,7 +3,7 @@

__author__ = 'Daniel Lindsley & the Tastypie core team'

VERSION = (0, 14, 2)
VERSION = (0, 14, 3)

__short_version__ = '.'.join(map(str, VERSION[0:2]))
__version__ = ''.join(['.'.join(map(str, VERSION[0:3])), ''.join(VERSION[3:])])
3 changes: 2 additions & 1 deletion tastypie/authentication.py
Expand Up @@ -10,9 +10,10 @@
from django.contrib.auth import authenticate
from django.core.exceptions import ImproperlyConfigured
from django.middleware.csrf import _sanitize_token, constant_time_compare
from django.utils.six.moves.urllib.parse import urlparse
from django.utils.translation import ugettext as _

from six.moves.urllib.parse import urlparse

from tastypie.compat import (
get_user_model, get_username_field, unsalt_token, is_authenticated
)
Expand Down
3 changes: 2 additions & 1 deletion tastypie/bundle.py
@@ -1,6 +1,7 @@
from __future__ import unicode_literals
from django.http import HttpRequest
from django.utils import six

import six


# In a separate file to avoid circular imports...
Expand Down
9 changes: 9 additions & 0 deletions tastypie/compat.py
Expand Up @@ -45,3 +45,12 @@ def unsalt_token(token):

def unsalt_token(token):
return token


# force_text deprecated in 2.2, removed in 3.0
# note that in 1.1.x, force_str and force_text both exist, but force_str behaves
# much differently on python 3 than python 2.
if django.VERSION < (2, 2):
from django.utils.encoding import force_text as force_str # noqa
else:
from django.utils.encoding import force_str # noqa
4 changes: 3 additions & 1 deletion tastypie/fields.py
Expand Up @@ -14,7 +14,9 @@
except ImportError:
from django.db.models.fields.related_descriptors import\
ReverseOneToOneDescriptor
from django.utils import datetime_safe, six
from django.utils import datetime_safe

import six

from tastypie.bundle import Bundle
from tastypie.exceptions import ApiFieldError, NotFound
Expand Down
2 changes: 1 addition & 1 deletion tastypie/models.py
Expand Up @@ -6,7 +6,7 @@

from django.conf import settings
from django.db import models
from django.utils.encoding import python_2_unicode_compatible
from six import python_2_unicode_compatible

from tastypie.utils import now

Expand Down
3 changes: 2 additions & 1 deletion tastypie/paginator.py
@@ -1,7 +1,8 @@
from __future__ import unicode_literals

from django.conf import settings
from django.utils import six

import six

from tastypie.exceptions import BadRequest

Expand Down
5 changes: 3 additions & 2 deletions tastypie/resources.py
Expand Up @@ -30,11 +30,12 @@
ReverseOneToOneDescriptor

from django.http import HttpResponse, HttpResponseNotFound, Http404
from django.utils import six
from django.utils.cache import patch_cache_control, patch_vary_headers
from django.utils.html import escape
from django.views.decorators.csrf import csrf_exempt

import six

from tastypie.authentication import Authentication
from tastypie.authorization import ReadOnlyAuthorization
from tastypie.bundle import Bundle
Expand Down Expand Up @@ -63,7 +64,7 @@
def sanitize(text):
# We put the single quotes back, due to their frequent usage in exception
# messages.
return escape(text).replace('&#39;', "'").replace('&quot;', '"')
return escape(text).replace('&#39;', "'").replace('&quot;', '"').replace('&#x27;', "'")


class ResourceOptions(object):
Expand Down
24 changes: 15 additions & 9 deletions tastypie/serializers.py
Expand Up @@ -6,21 +6,27 @@

from django.conf import settings
from django.core.exceptions import ImproperlyConfigured
from django.utils import six
from django.utils.encoding import force_text, smart_bytes
from django.utils.encoding import smart_bytes
from django.core.serializers import json as djangojson

import six

from tastypie.bundle import Bundle
from tastypie.compat import force_str
from tastypie.exceptions import BadRequest, UnsupportedSerializationFormat,\
UnsupportedDeserializationFormat
from tastypie.utils import format_datetime, format_date, format_time,\
make_naive


import warnings
try:
import defusedxml.lxml as lxml
from defusedxml.common import DefusedXmlException
from defusedxml.lxml import parse as parse_xml
from lxml.etree import Element, tostring, LxmlError
with warnings.catch_warnings():
warnings.simplefilter("ignore", DeprecationWarning)
import defusedxml.lxml as lxml
from defusedxml.common import DefusedXmlException
from defusedxml.lxml import parse as parse_xml
from lxml.etree import Element, tostring, LxmlError
except ImportError:
lxml = None

Expand Down Expand Up @@ -279,7 +285,7 @@ def deserialize(self, content, format='application/json'):
raise UnsupportedDeserializationFormat(format)

if isinstance(content, six.binary_type):
content = force_text(content)
content = force_str(content)

return method(content)

Expand Down Expand Up @@ -311,7 +317,7 @@ def to_simple(self, data, options):
to_simple = self.to_simple
return {key: to_simple(val, options) for key, val in six.iteritems(data)}
if stype == _STR:
return force_text(data)
return force_str(data)
if stype == _LIST:
to_simple = self.to_simple
return [to_simple(item, options) for item in data]
Expand Down Expand Up @@ -368,7 +374,7 @@ def to_etree(self, data, options=None, name=None, depth=0):
if isinstance(simple_data, six.text_type):
element.text = simple_data
else:
element.text = force_text(simple_data)
element.text = force_str(simple_data)

return element

Expand Down
10 changes: 5 additions & 5 deletions tastypie/test.py
Expand Up @@ -3,8 +3,8 @@

from django.conf import settings
from django.test.client import Client
from django.utils.encoding import force_text

from tastypie.compat import force_str
from tastypie.serializers import Serializer


Expand Down Expand Up @@ -484,7 +484,7 @@ def assertValidJSONResponse(self, resp):
"""
self.assertHttpOK(resp)
self.assertTrue(resp['Content-Type'].startswith('application/json'))
self.assertValidJSON(force_text(resp.content))
self.assertValidJSON(force_str(resp.content))

def assertValidXMLResponse(self, resp):
"""
Expand All @@ -497,7 +497,7 @@ def assertValidXMLResponse(self, resp):
"""
self.assertHttpOK(resp)
self.assertTrue(resp['Content-Type'].startswith('application/xml'))
self.assertValidXML(force_text(resp.content))
self.assertValidXML(force_str(resp.content))

def assertValidYAMLResponse(self, resp):
"""
Expand All @@ -510,7 +510,7 @@ def assertValidYAMLResponse(self, resp):
"""
self.assertHttpOK(resp)
self.assertTrue(resp['Content-Type'].startswith('text/yaml'))
self.assertValidYAML(force_text(resp.content))
self.assertValidYAML(force_str(resp.content))

def assertValidPlistResponse(self, resp):
"""
Expand All @@ -523,7 +523,7 @@ def assertValidPlistResponse(self, resp):
"""
self.assertHttpOK(resp)
self.assertTrue(resp['Content-Type'].startswith('application/x-plist'))
self.assertValidPlist(force_text(resp.content))
self.assertValidPlist(force_str(resp.content))

def deserialize(self, resp):
"""
Expand Down
3 changes: 2 additions & 1 deletion tastypie/utils/dict.py
@@ -1,6 +1,7 @@
from django.utils import six
from django.utils.encoding import smart_bytes

import six


def dict_strip_unicode_keys(uni_dict):
"""
Expand Down
3 changes: 2 additions & 1 deletion tastypie/utils/urls.py
@@ -1,7 +1,8 @@
from __future__ import unicode_literals

from django.conf import settings
from django.utils import six

import six


_trailing_slash = '/?' if getattr(settings, 'TASTYPIE_ALLOW_MISSING_SLASH', False) else '/'
Expand Down
2 changes: 1 addition & 1 deletion tastypie/utils/validate_jsonp.py
Expand Up @@ -9,7 +9,7 @@

from unicodedata import category

from django.utils import six
import six

# -----------------------------------------------------------------------------
# javascript identifier unicode categories and "exceptional" chars
Expand Down

0 comments on commit 2700cdc

Please sign in to comment.