Py3k cleanup

This commit is contained in:
Tom Christie 2013-02-01 15:07:51 +00:00
parent f4f237e3ee
commit 00752dcd2a
8 changed files with 408 additions and 16 deletions

View File

@ -4,18 +4,17 @@ python:
- "2.6" - "2.6"
- "2.7" - "2.7"
- "3.2" - "3.2"
- "3.3"
env: env:
- DJANGO=https://github.com/django/django/zipball/master
- DJANGO=https://www.djangoproject.com/download/1.5c1/tarball/ - DJANGO=https://www.djangoproject.com/download/1.5c1/tarball/
- DJANGO="django==1.4.3 --use-mirrors" - DJANGO="django==1.4.3 --use-mirrors"
- DJANGO="django==1.3.5 --use-mirrors" - DJANGO="django==1.3.5 --use-mirrors"
install: install:
- pip install $DJANGO - pip install $DJANGO
- "if [[ $TRAVIS_PYTHON_VERSION != '3.2' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} != '3' ]]; then pip install django-filter==0.5.4 --use-mirrors; fi"
- "if [[ $TRAVIS_PYTHON_VERSION == '3.2' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi" - "if [[ ${TRAVIS_PYTHON_VERSION::1} == '3' ]]; then pip install https://github.com/alex/django-filter/tarball/master; fi"
- "if [[ $DJANGO == 'django==1.3.5 --use-mirrors' ]]; then pip install six --use-mirrors; fi"
- export PYTHONPATH=. - export PYTHONPATH=.
script: script:
@ -27,3 +26,7 @@ matrix:
env: DJANGO="django==1.4.3 --use-mirrors" env: DJANGO="django==1.4.3 --use-mirrors"
- python: "3.2" - python: "3.2"
env: DJANGO="django==1.3.5 --use-mirrors" env: DJANGO="django==1.3.5 --use-mirrors"
- python: "3.3"
env: DJANGO="django==1.4.3 --use-mirrors"
- python: "3.3"
env: DJANGO="django==1.3.5 --use-mirrors"

View File

@ -28,7 +28,7 @@ There is also a sandbox API you can use for testing purposes, [available here][s
# Requirements # Requirements
* Python (2.6, 2.7) * Python (2.6, 2.7, 3.2, 3.3)
* Django (1.3, 1.4, 1.5) * Django (1.3, 1.4, 1.5)
**Optional:** **Optional:**

View File

@ -33,7 +33,7 @@ There is also a sandbox API you can use for testing purposes, [available here][s
REST framework requires the following: REST framework requires the following:
* Python (2.6, 2.7) * Python (2.6, 2.7, 3.2, 3.3)
* Django (1.3, 1.4, 1.5) * Django (1.3, 1.4, 1.5)
The following packages are optional: The following packages are optional:

View File

@ -7,11 +7,11 @@ from __future__ import unicode_literals
import django import django
# Try to import six from Django, fallback to six itself (1.3.x) # Try to import six from Django, fallback to included `six`.
try: try:
from django.utils import six from django.utils import six
except: except:
import six from rest_framework import six
# location of patterns, url, include changes in 1.4 onwards # location of patterns, url, include changes in 1.4 onwards
try: try:

View File

@ -9,10 +9,11 @@ The wrapped request then offers a richer API, in particular :
- full support of PUT method, including support for file uploads - full support of PUT method, including support for file uploads
- form overloading of HTTP method, content type and content - form overloading of HTTP method, content type and content
""" """
from rest_framework.compat import BytesIO
from django.http.multipartparser import parse_header from django.http.multipartparser import parse_header
from rest_framework import HTTP_HEADER_ENCODING
from rest_framework import exceptions from rest_framework import exceptions
from rest_framework.compat import BytesIO
from rest_framework.settings import api_settings from rest_framework.settings import api_settings
@ -20,7 +21,7 @@ def is_form_media_type(media_type):
""" """
Return True if the media type is a valid form media type. Return True if the media type is a valid form media type.
""" """
base_media_type, params = parse_header(media_type.encode('iso-8859-1')) base_media_type, params = parse_header(media_type.encode(HTTP_HEADER_ENCODING))
return (base_media_type == 'application/x-www-form-urlencoded' or return (base_media_type == 'application/x-www-form-urlencoded' or
base_media_type == 'multipart/form-data') base_media_type == 'multipart/form-data')
@ -277,7 +278,7 @@ class Request(object):
self._CONTENT_PARAM in self._data and self._CONTENT_PARAM in self._data and
self._CONTENTTYPE_PARAM in self._data): self._CONTENTTYPE_PARAM in self._data):
self._content_type = self._data[self._CONTENTTYPE_PARAM] self._content_type = self._data[self._CONTENTTYPE_PARAM]
self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode('iso-8859-1')) self._stream = BytesIO(self._data[self._CONTENT_PARAM].encode(HTTP_HEADER_ENCODING))
self._data, self._files = (Empty, Empty) self._data, self._files = (Empty, Empty)
def _parse(self): def _parse(self):

389
rest_framework/six.py Normal file
View File

@ -0,0 +1,389 @@
"""Utilities for writing code that runs on Python 2 and 3"""
import operator
import sys
import types
__author__ = "Benjamin Peterson <benjamin@python.org>"
__version__ = "1.2.0"
# True if we are running on Python 3.
PY3 = sys.version_info[0] == 3
if PY3:
string_types = str,
integer_types = int,
class_types = type,
text_type = str
binary_type = bytes
MAXSIZE = sys.maxsize
else:
string_types = basestring,
integer_types = (int, long)
class_types = (type, types.ClassType)
text_type = unicode
binary_type = str
if sys.platform == "java":
# Jython always uses 32 bits.
MAXSIZE = int((1 << 31) - 1)
else:
# It's possible to have sizeof(long) != sizeof(Py_ssize_t).
class X(object):
def __len__(self):
return 1 << 31
try:
len(X())
except OverflowError:
# 32-bit
MAXSIZE = int((1 << 31) - 1)
else:
# 64-bit
MAXSIZE = int((1 << 63) - 1)
del X
def _add_doc(func, doc):
"""Add documentation to a function."""
func.__doc__ = doc
def _import_module(name):
"""Import module, returning the module after the last dot."""
__import__(name)
return sys.modules[name]
class _LazyDescr(object):
def __init__(self, name):
self.name = name
def __get__(self, obj, tp):
result = self._resolve()
setattr(obj, self.name, result)
# This is a bit ugly, but it avoids running this again.
delattr(tp, self.name)
return result
class MovedModule(_LazyDescr):
def __init__(self, name, old, new=None):
super(MovedModule, self).__init__(name)
if PY3:
if new is None:
new = name
self.mod = new
else:
self.mod = old
def _resolve(self):
return _import_module(self.mod)
class MovedAttribute(_LazyDescr):
def __init__(self, name, old_mod, new_mod, old_attr=None, new_attr=None):
super(MovedAttribute, self).__init__(name)
if PY3:
if new_mod is None:
new_mod = name
self.mod = new_mod
if new_attr is None:
if old_attr is None:
new_attr = name
else:
new_attr = old_attr
self.attr = new_attr
else:
self.mod = old_mod
if old_attr is None:
old_attr = name
self.attr = old_attr
def _resolve(self):
module = _import_module(self.mod)
return getattr(module, self.attr)
class _MovedItems(types.ModuleType):
"""Lazy loading of moved objects"""
_moved_attributes = [
MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
MovedAttribute("filter", "itertools", "builtins", "ifilter", "filter"),
MovedAttribute("input", "__builtin__", "builtins", "raw_input", "input"),
MovedAttribute("map", "itertools", "builtins", "imap", "map"),
MovedAttribute("reload_module", "__builtin__", "imp", "reload"),
MovedAttribute("reduce", "__builtin__", "functools"),
MovedAttribute("StringIO", "StringIO", "io"),
MovedAttribute("xrange", "__builtin__", "builtins", "xrange", "range"),
MovedAttribute("zip", "itertools", "builtins", "izip", "zip"),
MovedModule("builtins", "__builtin__"),
MovedModule("configparser", "ConfigParser"),
MovedModule("copyreg", "copy_reg"),
MovedModule("http_cookiejar", "cookielib", "http.cookiejar"),
MovedModule("http_cookies", "Cookie", "http.cookies"),
MovedModule("html_entities", "htmlentitydefs", "html.entities"),
MovedModule("html_parser", "HTMLParser", "html.parser"),
MovedModule("http_client", "httplib", "http.client"),
MovedModule("BaseHTTPServer", "BaseHTTPServer", "http.server"),
MovedModule("CGIHTTPServer", "CGIHTTPServer", "http.server"),
MovedModule("SimpleHTTPServer", "SimpleHTTPServer", "http.server"),
MovedModule("cPickle", "cPickle", "pickle"),
MovedModule("queue", "Queue"),
MovedModule("reprlib", "repr"),
MovedModule("socketserver", "SocketServer"),
MovedModule("tkinter", "Tkinter"),
MovedModule("tkinter_dialog", "Dialog", "tkinter.dialog"),
MovedModule("tkinter_filedialog", "FileDialog", "tkinter.filedialog"),
MovedModule("tkinter_scrolledtext", "ScrolledText", "tkinter.scrolledtext"),
MovedModule("tkinter_simpledialog", "SimpleDialog", "tkinter.simpledialog"),
MovedModule("tkinter_tix", "Tix", "tkinter.tix"),
MovedModule("tkinter_constants", "Tkconstants", "tkinter.constants"),
MovedModule("tkinter_dnd", "Tkdnd", "tkinter.dnd"),
MovedModule("tkinter_colorchooser", "tkColorChooser",
"tkinter.colorchooser"),
MovedModule("tkinter_commondialog", "tkCommonDialog",
"tkinter.commondialog"),
MovedModule("tkinter_tkfiledialog", "tkFileDialog", "tkinter.filedialog"),
MovedModule("tkinter_font", "tkFont", "tkinter.font"),
MovedModule("tkinter_messagebox", "tkMessageBox", "tkinter.messagebox"),
MovedModule("tkinter_tksimpledialog", "tkSimpleDialog",
"tkinter.simpledialog"),
MovedModule("urllib_robotparser", "robotparser", "urllib.robotparser"),
MovedModule("winreg", "_winreg"),
]
for attr in _moved_attributes:
setattr(_MovedItems, attr.name, attr)
del attr
moves = sys.modules["django.utils.six.moves"] = _MovedItems("moves")
def add_move(move):
"""Add an item to six.moves."""
setattr(_MovedItems, move.name, move)
def remove_move(name):
"""Remove item from six.moves."""
try:
delattr(_MovedItems, name)
except AttributeError:
try:
del moves.__dict__[name]
except KeyError:
raise AttributeError("no such move, %r" % (name,))
if PY3:
_meth_func = "__func__"
_meth_self = "__self__"
_func_code = "__code__"
_func_defaults = "__defaults__"
_iterkeys = "keys"
_itervalues = "values"
_iteritems = "items"
else:
_meth_func = "im_func"
_meth_self = "im_self"
_func_code = "func_code"
_func_defaults = "func_defaults"
_iterkeys = "iterkeys"
_itervalues = "itervalues"
_iteritems = "iteritems"
try:
advance_iterator = next
except NameError:
def advance_iterator(it):
return it.next()
next = advance_iterator
if PY3:
def get_unbound_function(unbound):
return unbound
Iterator = object
def callable(obj):
return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
else:
def get_unbound_function(unbound):
return unbound.im_func
class Iterator(object):
def next(self):
return type(self).__next__(self)
callable = callable
_add_doc(get_unbound_function,
"""Get the function out of a possibly unbound function""")
get_method_function = operator.attrgetter(_meth_func)
get_method_self = operator.attrgetter(_meth_self)
get_function_code = operator.attrgetter(_func_code)
get_function_defaults = operator.attrgetter(_func_defaults)
def iterkeys(d):
"""Return an iterator over the keys of a dictionary."""
return iter(getattr(d, _iterkeys)())
def itervalues(d):
"""Return an iterator over the values of a dictionary."""
return iter(getattr(d, _itervalues)())
def iteritems(d):
"""Return an iterator over the (key, value) pairs of a dictionary."""
return iter(getattr(d, _iteritems)())
if PY3:
def b(s):
return s.encode("latin-1")
def u(s):
return s
if sys.version_info[1] <= 1:
def int2byte(i):
return bytes((i,))
else:
# This is about 2x faster than the implementation above on 3.2+
int2byte = operator.methodcaller("to_bytes", 1, "big")
import io
StringIO = io.StringIO
BytesIO = io.BytesIO
else:
def b(s):
return s
def u(s):
return unicode(s, "unicode_escape")
int2byte = chr
import StringIO
StringIO = BytesIO = StringIO.StringIO
_add_doc(b, """Byte literal""")
_add_doc(u, """Text literal""")
if PY3:
import builtins
exec_ = getattr(builtins, "exec")
def reraise(tp, value, tb=None):
if value.__traceback__ is not tb:
raise value.with_traceback(tb)
raise value
print_ = getattr(builtins, "print")
del builtins
else:
def exec_(code, globs=None, locs=None):
"""Execute code in a namespace."""
if globs is None:
frame = sys._getframe(1)
globs = frame.f_globals
if locs is None:
locs = frame.f_locals
del frame
elif locs is None:
locs = globs
exec("""exec code in globs, locs""")
exec_("""def reraise(tp, value, tb=None):
raise tp, value, tb
""")
def print_(*args, **kwargs):
"""The new-style print function."""
fp = kwargs.pop("file", sys.stdout)
if fp is None:
return
def write(data):
if not isinstance(data, basestring):
data = str(data)
fp.write(data)
want_unicode = False
sep = kwargs.pop("sep", None)
if sep is not None:
if isinstance(sep, unicode):
want_unicode = True
elif not isinstance(sep, str):
raise TypeError("sep must be None or a string")
end = kwargs.pop("end", None)
if end is not None:
if isinstance(end, unicode):
want_unicode = True
elif not isinstance(end, str):
raise TypeError("end must be None or a string")
if kwargs:
raise TypeError("invalid keyword arguments to print()")
if not want_unicode:
for arg in args:
if isinstance(arg, unicode):
want_unicode = True
break
if want_unicode:
newline = unicode("\n")
space = unicode(" ")
else:
newline = "\n"
space = " "
if sep is None:
sep = space
if end is None:
end = newline
for i, arg in enumerate(args):
if i:
write(sep)
write(arg)
write(end)
_add_doc(reraise, """Reraise an exception.""")
def with_metaclass(meta, base=object):
"""Create a base class with a metaclass."""
return meta("NewBase", (base,), {})
### Additional customizations for Django ###
if PY3:
_iterlists = "lists"
_assertRaisesRegex = "assertRaisesRegex"
else:
_iterlists = "iterlists"
_assertRaisesRegex = "assertRaisesRegexp"
def iterlists(d):
"""Return an iterator over the values of a MultiValueDict."""
return getattr(d, _iterlists)()
def assertRaisesRegex(self, *args, **kwargs):
return getattr(self, _assertRaisesRegex)(*args, **kwargs)
add_move(MovedModule("_dummy_thread", "dummy_thread"))
add_move(MovedModule("_thread", "thread"))

View File

@ -5,6 +5,7 @@ See http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html#sec3.7
""" """
from django.http.multipartparser import parse_header from django.http.multipartparser import parse_header
from rest_framework import HTTP_HEADER_ENCODING
def media_type_matches(lhs, rhs): def media_type_matches(lhs, rhs):
@ -47,7 +48,7 @@ class _MediaType(object):
if media_type_str is None: if media_type_str is None:
media_type_str = '' media_type_str = ''
self.orig = media_type_str self.orig = media_type_str
self.full_type, self.params = parse_header(media_type_str.encode('iso-8859-1')) self.full_type, self.params = parse_header(media_type_str.encode(HTTP_HEADER_ENCODING))
self.main_type, sep, self.sub_type = self.full_type.partition('/') self.main_type, sep, self.sub_type = self.full_type.partition('/')
def match(self, other): def match(self, other):

View File

@ -8,12 +8,12 @@ commands = {envpython} rest_framework/runtests/runtests.py
[testenv:py3.3-django1.5] [testenv:py3.3-django1.5]
basepython = python3.3 basepython = python3.3
deps = https://www.djangoproject.com/download/1.5c1/tarball/ deps = https://www.djangoproject.com/download/1.5c1/tarball/
django-filter==0.5.4 https://github.com/alex/django-filter/archive/master.tar.gz
[testenv:py3.2-django1.5] [testenv:py3.2-django1.5]
basepython = python3.2 basepython = python3.2
deps = https://www.djangoproject.com/download/1.5c1/tarball/ deps = https://www.djangoproject.com/download/1.5c1/tarball/
django-filter==0.5.4 https://github.com/alex/django-filter/archive/master.tar.gz
[testenv:py2.7-django1.5] [testenv:py2.7-django1.5]
basepython = python2.7 basepython = python2.7
@ -39,10 +39,8 @@ deps = django==1.4.3
basepython = python2.7 basepython = python2.7
deps = django==1.3.5 deps = django==1.3.5
django-filter==0.5.4 django-filter==0.5.4
six
[testenv:py2.6-django1.3] [testenv:py2.6-django1.3]
basepython = python2.6 basepython = python2.6
deps = django==1.3.5 deps = django==1.3.5
django-filter==0.5.4 django-filter==0.5.4
six