2012-04-26 23:19:12 +04:00
|
|
|
"""Compatibility code for using CherryPy with various versions of Python.
|
|
|
|
|
|
|
|
CherryPy 3.2 is compatible with Python versions 2.3+. This module provides a
|
|
|
|
useful abstraction over the differences between Python versions, sometimes by
|
|
|
|
preferring a newer idiom, sometimes an older one, and sometimes a custom one.
|
|
|
|
|
|
|
|
In particular, Python 2 uses str and '' for byte strings, while Python 3
|
|
|
|
uses str and '' for unicode strings. We will call each of these the 'native
|
|
|
|
string' type for each version. Because of this major difference, this module
|
|
|
|
provides new 'bytestr', 'unicodestr', and 'nativestr' attributes, as well as
|
|
|
|
two functions: 'ntob', which translates native strings (of type 'str') into
|
|
|
|
byte strings regardless of Python version, and 'ntou', which translates native
|
|
|
|
strings to unicode strings. This also provides a 'BytesIO' name for dealing
|
|
|
|
specifically with bytes, and a 'StringIO' name for dealing with native strings.
|
|
|
|
It also provides a 'base64_decode' function with native strings as input and
|
|
|
|
output.
|
|
|
|
"""
|
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import sys
|
2015-05-10 20:19:02 +03:00
|
|
|
import threading
|
2012-04-26 23:19:12 +04:00
|
|
|
|
|
|
|
if sys.version_info >= (3, 0):
|
|
|
|
py3k = True
|
|
|
|
bytestr = bytes
|
|
|
|
unicodestr = str
|
|
|
|
nativestr = unicodestr
|
|
|
|
basestring = (bytes, str)
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def ntob(n, encoding='ISO-8859-1'):
|
2015-05-10 20:19:02 +03:00
|
|
|
"""Return the given native string as a byte string in the given
|
|
|
|
encoding.
|
|
|
|
"""
|
|
|
|
assert_native(n)
|
2012-04-26 23:19:12 +04:00
|
|
|
# In Python 3, the native string type is unicode
|
|
|
|
return n.encode(encoding)
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def ntou(n, encoding='ISO-8859-1'):
|
2015-05-10 20:19:02 +03:00
|
|
|
"""Return the given native string as a unicode string with the given
|
|
|
|
encoding.
|
|
|
|
"""
|
|
|
|
assert_native(n)
|
2012-04-26 23:19:12 +04:00
|
|
|
# In Python 3, the native string type is unicode
|
|
|
|
return n
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def tonative(n, encoding='ISO-8859-1'):
|
|
|
|
"""Return the given string as a native string in the given encoding."""
|
|
|
|
# In Python 3, the native string type is unicode
|
|
|
|
if isinstance(n, bytes):
|
|
|
|
return n.decode(encoding)
|
|
|
|
return n
|
|
|
|
# type("")
|
|
|
|
from io import StringIO
|
|
|
|
# bytes:
|
|
|
|
from io import BytesIO as BytesIO
|
|
|
|
else:
|
|
|
|
# Python 2
|
|
|
|
py3k = False
|
|
|
|
bytestr = str
|
|
|
|
unicodestr = unicode
|
|
|
|
nativestr = bytestr
|
|
|
|
basestring = basestring
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def ntob(n, encoding='ISO-8859-1'):
|
2015-05-10 20:19:02 +03:00
|
|
|
"""Return the given native string as a byte string in the given
|
|
|
|
encoding.
|
|
|
|
"""
|
|
|
|
assert_native(n)
|
2012-04-26 23:19:12 +04:00
|
|
|
# In Python 2, the native string type is bytes. Assume it's already
|
|
|
|
# in the given encoding, which for ISO-8859-1 is almost always what
|
|
|
|
# was intended.
|
|
|
|
return n
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def ntou(n, encoding='ISO-8859-1'):
|
2015-05-10 20:19:02 +03:00
|
|
|
"""Return the given native string as a unicode string with the given
|
|
|
|
encoding.
|
|
|
|
"""
|
|
|
|
assert_native(n)
|
2012-04-26 23:19:12 +04:00
|
|
|
# In Python 2, the native string type is bytes.
|
2015-05-10 20:19:02 +03:00
|
|
|
# First, check for the special encoding 'escape'. The test suite uses
|
|
|
|
# this to signal that it wants to pass a string with embedded \uXXXX
|
|
|
|
# escapes, but without having to prefix it with u'' for Python 2,
|
|
|
|
# but no prefix for Python 3.
|
2012-04-26 23:19:12 +04:00
|
|
|
if encoding == 'escape':
|
|
|
|
return unicode(
|
|
|
|
re.sub(r'\\u([0-9a-zA-Z]{4})',
|
|
|
|
lambda m: unichr(int(m.group(1), 16)),
|
|
|
|
n.decode('ISO-8859-1')))
|
2015-05-10 20:19:02 +03:00
|
|
|
# Assume it's already in the given encoding, which for ISO-8859-1
|
|
|
|
# is almost always what was intended.
|
2012-04-26 23:19:12 +04:00
|
|
|
return n.decode(encoding)
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def tonative(n, encoding='ISO-8859-1'):
|
|
|
|
"""Return the given string as a native string in the given encoding."""
|
|
|
|
# In Python 2, the native string type is bytes.
|
|
|
|
if isinstance(n, unicode):
|
|
|
|
return n.encode(encoding)
|
|
|
|
return n
|
|
|
|
try:
|
|
|
|
# type("")
|
|
|
|
from cStringIO import StringIO
|
|
|
|
except ImportError:
|
|
|
|
# type("")
|
|
|
|
from StringIO import StringIO
|
|
|
|
# bytes:
|
|
|
|
BytesIO = StringIO
|
|
|
|
|
2015-05-10 20:19:02 +03:00
|
|
|
|
|
|
|
def assert_native(n):
|
|
|
|
if not isinstance(n, nativestr):
|
|
|
|
raise TypeError("n must be a native str (got %s)" % type(n).__name__)
|
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
try:
|
|
|
|
set = set
|
|
|
|
except NameError:
|
|
|
|
from sets import Set as set
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 3.1+
|
|
|
|
from base64 import decodebytes as _base64_decodebytes
|
|
|
|
except ImportError:
|
|
|
|
# Python 3.0-
|
2015-05-10 20:19:02 +03:00
|
|
|
# since CherryPy claims compability with Python 2.3, we must use
|
2012-04-26 23:19:12 +04:00
|
|
|
# the legacy API of base64
|
|
|
|
from base64 import decodestring as _base64_decodebytes
|
|
|
|
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def base64_decode(n, encoding='ISO-8859-1'):
|
|
|
|
"""Return the native string base64-decoded (as a native string)."""
|
|
|
|
if isinstance(n, unicodestr):
|
|
|
|
b = n.encode(encoding)
|
|
|
|
else:
|
|
|
|
b = n
|
|
|
|
b = _base64_decodebytes(b)
|
|
|
|
if nativestr is unicodestr:
|
|
|
|
return b.decode(encoding)
|
|
|
|
else:
|
|
|
|
return b
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 2.5+
|
|
|
|
from hashlib import md5
|
|
|
|
except ImportError:
|
|
|
|
from md5 import new as md5
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 2.5+
|
|
|
|
from hashlib import sha1 as sha
|
|
|
|
except ImportError:
|
|
|
|
from sha import new as sha
|
|
|
|
|
|
|
|
try:
|
|
|
|
sorted = sorted
|
|
|
|
except NameError:
|
|
|
|
def sorted(i):
|
|
|
|
i = i[:]
|
|
|
|
i.sort()
|
|
|
|
return i
|
|
|
|
|
|
|
|
try:
|
|
|
|
reversed = reversed
|
|
|
|
except NameError:
|
|
|
|
def reversed(x):
|
|
|
|
i = len(x)
|
|
|
|
while i > 0:
|
|
|
|
i -= 1
|
|
|
|
yield x[i]
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 3
|
|
|
|
from urllib.parse import urljoin, urlencode
|
|
|
|
from urllib.parse import quote, quote_plus
|
|
|
|
from urllib.request import unquote, urlopen
|
|
|
|
from urllib.request import parse_http_list, parse_keqv_list
|
|
|
|
except ImportError:
|
|
|
|
# Python 2
|
|
|
|
from urlparse import urljoin
|
|
|
|
from urllib import urlencode, urlopen
|
|
|
|
from urllib import quote, quote_plus
|
|
|
|
from urllib import unquote
|
|
|
|
from urllib2 import parse_http_list, parse_keqv_list
|
|
|
|
|
|
|
|
try:
|
|
|
|
from threading import local as threadlocal
|
|
|
|
except ImportError:
|
|
|
|
from cherrypy._cpthreadinglocal import local as threadlocal
|
|
|
|
|
|
|
|
try:
|
|
|
|
dict.iteritems
|
|
|
|
# Python 2
|
|
|
|
iteritems = lambda d: d.iteritems()
|
|
|
|
copyitems = lambda d: d.items()
|
|
|
|
except AttributeError:
|
|
|
|
# Python 3
|
|
|
|
iteritems = lambda d: d.items()
|
|
|
|
copyitems = lambda d: list(d.items())
|
|
|
|
|
|
|
|
try:
|
|
|
|
dict.iterkeys
|
|
|
|
# Python 2
|
|
|
|
iterkeys = lambda d: d.iterkeys()
|
|
|
|
copykeys = lambda d: d.keys()
|
|
|
|
except AttributeError:
|
|
|
|
# Python 3
|
|
|
|
iterkeys = lambda d: d.keys()
|
|
|
|
copykeys = lambda d: list(d.keys())
|
|
|
|
|
|
|
|
try:
|
|
|
|
dict.itervalues
|
|
|
|
# Python 2
|
|
|
|
itervalues = lambda d: d.itervalues()
|
|
|
|
copyvalues = lambda d: d.values()
|
|
|
|
except AttributeError:
|
|
|
|
# Python 3
|
|
|
|
itervalues = lambda d: d.values()
|
|
|
|
copyvalues = lambda d: list(d.values())
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 3
|
|
|
|
import builtins
|
|
|
|
except ImportError:
|
|
|
|
# Python 2
|
|
|
|
import __builtin__ as builtins
|
|
|
|
|
|
|
|
try:
|
2015-05-10 20:19:02 +03:00
|
|
|
# Python 2. We try Python 2 first clients on Python 2
|
2012-04-26 23:19:12 +04:00
|
|
|
# don't try to import the 'http' module from cherrypy.lib
|
|
|
|
from Cookie import SimpleCookie, CookieError
|
2015-05-10 20:19:02 +03:00
|
|
|
from httplib import BadStatusLine, HTTPConnection, IncompleteRead
|
|
|
|
from httplib import NotConnected
|
2012-04-26 23:19:12 +04:00
|
|
|
from BaseHTTPServer import BaseHTTPRequestHandler
|
|
|
|
except ImportError:
|
|
|
|
# Python 3
|
|
|
|
from http.cookies import SimpleCookie, CookieError
|
2015-05-10 20:19:02 +03:00
|
|
|
from http.client import BadStatusLine, HTTPConnection, IncompleteRead
|
|
|
|
from http.client import NotConnected
|
2012-04-26 23:19:12 +04:00
|
|
|
from http.server import BaseHTTPRequestHandler
|
|
|
|
|
2015-05-10 20:19:02 +03:00
|
|
|
# Some platforms don't expose HTTPSConnection, so handle it separately
|
|
|
|
if py3k:
|
2012-04-26 23:19:12 +04:00
|
|
|
try:
|
|
|
|
from http.client import HTTPSConnection
|
|
|
|
except ImportError:
|
|
|
|
# Some platforms which don't have SSL don't expose HTTPSConnection
|
|
|
|
HTTPSConnection = None
|
2015-05-10 20:19:02 +03:00
|
|
|
else:
|
|
|
|
try:
|
|
|
|
from httplib import HTTPSConnection
|
|
|
|
except ImportError:
|
|
|
|
HTTPSConnection = None
|
2012-04-26 23:19:12 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 2
|
|
|
|
xrange = xrange
|
|
|
|
except NameError:
|
|
|
|
# Python 3
|
|
|
|
xrange = range
|
|
|
|
|
|
|
|
import threading
|
|
|
|
if hasattr(threading.Thread, "daemon"):
|
|
|
|
# Python 2.6+
|
|
|
|
def get_daemon(t):
|
|
|
|
return t.daemon
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def set_daemon(t, val):
|
|
|
|
t.daemon = val
|
|
|
|
else:
|
|
|
|
def get_daemon(t):
|
|
|
|
return t.isDaemon()
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def set_daemon(t, val):
|
|
|
|
t.setDaemon(val)
|
|
|
|
|
|
|
|
try:
|
|
|
|
from email.utils import formatdate
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def HTTPDate(timeval=None):
|
|
|
|
return formatdate(timeval, usegmt=True)
|
|
|
|
except ImportError:
|
|
|
|
from rfc822 import formatdate as HTTPDate
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 3
|
|
|
|
from urllib.parse import unquote as parse_unquote
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def unquote_qs(atom, encoding, errors='strict'):
|
2015-05-10 20:19:02 +03:00
|
|
|
return parse_unquote(
|
|
|
|
atom.replace('+', ' '),
|
|
|
|
encoding=encoding,
|
|
|
|
errors=errors)
|
2012-04-26 23:19:12 +04:00
|
|
|
except ImportError:
|
|
|
|
# Python 2
|
|
|
|
from urllib import unquote as parse_unquote
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def unquote_qs(atom, encoding, errors='strict'):
|
|
|
|
return parse_unquote(atom.replace('+', ' ')).decode(encoding, errors)
|
|
|
|
|
|
|
|
try:
|
2015-05-10 20:19:02 +03:00
|
|
|
# Prefer simplejson, which is usually more advanced than the builtin
|
|
|
|
# module.
|
2012-04-26 23:19:12 +04:00
|
|
|
import simplejson as json
|
|
|
|
json_decode = json.JSONDecoder().decode
|
2015-05-10 20:19:02 +03:00
|
|
|
_json_encode = json.JSONEncoder().iterencode
|
2012-04-26 23:19:12 +04:00
|
|
|
except ImportError:
|
2015-05-10 20:19:02 +03:00
|
|
|
if sys.version_info >= (2, 6):
|
|
|
|
# Python >=2.6 : json is part of the standard library
|
2012-04-26 23:19:12 +04:00
|
|
|
import json
|
|
|
|
json_decode = json.JSONDecoder().decode
|
|
|
|
_json_encode = json.JSONEncoder().iterencode
|
|
|
|
else:
|
|
|
|
json = None
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def json_decode(s):
|
|
|
|
raise ValueError('No JSON library is available')
|
2015-05-10 20:19:02 +03:00
|
|
|
|
|
|
|
def _json_encode(s):
|
2012-04-26 23:19:12 +04:00
|
|
|
raise ValueError('No JSON library is available')
|
2015-05-10 20:19:02 +03:00
|
|
|
finally:
|
|
|
|
if json and py3k:
|
|
|
|
# The two Python 3 implementations (simplejson/json)
|
|
|
|
# outputs str. We need bytes.
|
|
|
|
def json_encode(value):
|
|
|
|
for chunk in _json_encode(value):
|
|
|
|
yield chunk.encode('utf8')
|
|
|
|
else:
|
|
|
|
json_encode = _json_encode
|
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
|
|
|
|
try:
|
|
|
|
import cPickle as pickle
|
|
|
|
except ImportError:
|
|
|
|
# In Python 2, pickle is a Python version.
|
|
|
|
# In Python 3, pickle is the sped-up C version.
|
|
|
|
import pickle
|
|
|
|
|
|
|
|
try:
|
|
|
|
os.urandom(20)
|
|
|
|
import binascii
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def random20():
|
|
|
|
return binascii.hexlify(os.urandom(20)).decode('ascii')
|
|
|
|
except (AttributeError, NotImplementedError):
|
|
|
|
import random
|
|
|
|
# os.urandom not available until Python 2.4. Fall back to random.random.
|
2015-05-10 20:19:02 +03:00
|
|
|
|
2012-04-26 23:19:12 +04:00
|
|
|
def random20():
|
|
|
|
return sha('%s' % random.random()).hexdigest()
|
|
|
|
|
|
|
|
try:
|
|
|
|
from _thread import get_ident as get_thread_ident
|
|
|
|
except ImportError:
|
|
|
|
from thread import get_ident as get_thread_ident
|
|
|
|
|
|
|
|
try:
|
|
|
|
# Python 3
|
|
|
|
next = next
|
|
|
|
except NameError:
|
|
|
|
# Python 2
|
|
|
|
def next(i):
|
|
|
|
return i.next()
|
2015-05-10 20:19:02 +03:00
|
|
|
|
|
|
|
if sys.version_info >= (3, 3):
|
|
|
|
Timer = threading.Timer
|
|
|
|
Event = threading.Event
|
|
|
|
else:
|
|
|
|
# Python 3.2 and earlier
|
|
|
|
Timer = threading._Timer
|
|
|
|
Event = threading._Event
|
|
|
|
|
|
|
|
# Prior to Python 2.6, the Thread class did not have a .daemon property.
|
|
|
|
# This mix-in adds that property.
|
|
|
|
|
|
|
|
|
|
|
|
class SetDaemonProperty:
|
|
|
|
|
|
|
|
def __get_daemon(self):
|
|
|
|
return self.isDaemon()
|
|
|
|
|
|
|
|
def __set_daemon(self, daemon):
|
|
|
|
self.setDaemon(daemon)
|
|
|
|
|
|
|
|
if sys.version_info < (2, 6):
|
|
|
|
daemon = property(__get_daemon, __set_daemon)
|