OLD | NEW |
(Empty) | |
| 1 """Compatibility code for using CherryPy with various versions of Python. |
| 2 |
| 3 CherryPy 3.2 is compatible with Python versions 2.3+. This module provides a |
| 4 useful abstraction over the differences between Python versions, sometimes by |
| 5 preferring a newer idiom, sometimes an older one, and sometimes a custom one. |
| 6 |
| 7 In particular, Python 2 uses str and '' for byte strings, while Python 3 |
| 8 uses str and '' for unicode strings. We will call each of these the 'native |
| 9 string' type for each version. Because of this major difference, this module |
| 10 provides new 'bytestr', 'unicodestr', and 'nativestr' attributes, as well as |
| 11 two functions: 'ntob', which translates native strings (of type 'str') into |
| 12 byte strings regardless of Python version, and 'ntou', which translates native |
| 13 strings to unicode strings. This also provides a 'BytesIO' name for dealing |
| 14 specifically with bytes, and a 'StringIO' name for dealing with native strings. |
| 15 It also provides a 'base64_decode' function with native strings as input and |
| 16 output. |
| 17 """ |
| 18 import os |
| 19 import re |
| 20 import sys |
| 21 |
| 22 if sys.version_info >= (3, 0): |
| 23 py3k = True |
| 24 bytestr = bytes |
| 25 unicodestr = str |
| 26 nativestr = unicodestr |
| 27 basestring = (bytes, str) |
| 28 def ntob(n, encoding='ISO-8859-1'): |
| 29 """Return the given native string as a byte string in the given encoding
.""" |
| 30 # In Python 3, the native string type is unicode |
| 31 return n.encode(encoding) |
| 32 def ntou(n, encoding='ISO-8859-1'): |
| 33 """Return the given native string as a unicode string with the given enc
oding.""" |
| 34 # In Python 3, the native string type is unicode |
| 35 return n |
| 36 def tonative(n, encoding='ISO-8859-1'): |
| 37 """Return the given string as a native string in the given encoding.""" |
| 38 # In Python 3, the native string type is unicode |
| 39 if isinstance(n, bytes): |
| 40 return n.decode(encoding) |
| 41 return n |
| 42 # type("") |
| 43 from io import StringIO |
| 44 # bytes: |
| 45 from io import BytesIO as BytesIO |
| 46 else: |
| 47 # Python 2 |
| 48 py3k = False |
| 49 bytestr = str |
| 50 unicodestr = unicode |
| 51 nativestr = bytestr |
| 52 basestring = basestring |
| 53 def ntob(n, encoding='ISO-8859-1'): |
| 54 """Return the given native string as a byte string in the given encoding
.""" |
| 55 # In Python 2, the native string type is bytes. Assume it's already |
| 56 # in the given encoding, which for ISO-8859-1 is almost always what |
| 57 # was intended. |
| 58 return n |
| 59 def ntou(n, encoding='ISO-8859-1'): |
| 60 """Return the given native string as a unicode string with the given enc
oding.""" |
| 61 # In Python 2, the native string type is bytes. |
| 62 # First, check for the special encoding 'escape'. The test suite uses th
is |
| 63 # to signal that it wants to pass a string with embedded \uXXXX escapes, |
| 64 # but without having to prefix it with u'' for Python 2, but no prefix |
| 65 # for Python 3. |
| 66 if encoding == 'escape': |
| 67 return unicode( |
| 68 re.sub(r'\\u([0-9a-zA-Z]{4})', |
| 69 lambda m: unichr(int(m.group(1), 16)), |
| 70 n.decode('ISO-8859-1'))) |
| 71 # Assume it's already in the given encoding, which for ISO-8859-1 is alm
ost |
| 72 # always what was intended. |
| 73 return n.decode(encoding) |
| 74 def tonative(n, encoding='ISO-8859-1'): |
| 75 """Return the given string as a native string in the given encoding.""" |
| 76 # In Python 2, the native string type is bytes. |
| 77 if isinstance(n, unicode): |
| 78 return n.encode(encoding) |
| 79 return n |
| 80 try: |
| 81 # type("") |
| 82 from cStringIO import StringIO |
| 83 except ImportError: |
| 84 # type("") |
| 85 from StringIO import StringIO |
| 86 # bytes: |
| 87 BytesIO = StringIO |
| 88 |
| 89 try: |
| 90 set = set |
| 91 except NameError: |
| 92 from sets import Set as set |
| 93 |
| 94 try: |
| 95 # Python 3.1+ |
| 96 from base64 import decodebytes as _base64_decodebytes |
| 97 except ImportError: |
| 98 # Python 3.0- |
| 99 # since CherryPy claims compability with Python 2.3, we must use |
| 100 # the legacy API of base64 |
| 101 from base64 import decodestring as _base64_decodebytes |
| 102 |
| 103 def base64_decode(n, encoding='ISO-8859-1'): |
| 104 """Return the native string base64-decoded (as a native string).""" |
| 105 if isinstance(n, unicodestr): |
| 106 b = n.encode(encoding) |
| 107 else: |
| 108 b = n |
| 109 b = _base64_decodebytes(b) |
| 110 if nativestr is unicodestr: |
| 111 return b.decode(encoding) |
| 112 else: |
| 113 return b |
| 114 |
| 115 try: |
| 116 # Python 2.5+ |
| 117 from hashlib import md5 |
| 118 except ImportError: |
| 119 from md5 import new as md5 |
| 120 |
| 121 try: |
| 122 # Python 2.5+ |
| 123 from hashlib import sha1 as sha |
| 124 except ImportError: |
| 125 from sha import new as sha |
| 126 |
| 127 try: |
| 128 sorted = sorted |
| 129 except NameError: |
| 130 def sorted(i): |
| 131 i = i[:] |
| 132 i.sort() |
| 133 return i |
| 134 |
| 135 try: |
| 136 reversed = reversed |
| 137 except NameError: |
| 138 def reversed(x): |
| 139 i = len(x) |
| 140 while i > 0: |
| 141 i -= 1 |
| 142 yield x[i] |
| 143 |
| 144 try: |
| 145 # Python 3 |
| 146 from urllib.parse import urljoin, urlencode |
| 147 from urllib.parse import quote, quote_plus |
| 148 from urllib.request import unquote, urlopen |
| 149 from urllib.request import parse_http_list, parse_keqv_list |
| 150 except ImportError: |
| 151 # Python 2 |
| 152 from urlparse import urljoin |
| 153 from urllib import urlencode, urlopen |
| 154 from urllib import quote, quote_plus |
| 155 from urllib import unquote |
| 156 from urllib2 import parse_http_list, parse_keqv_list |
| 157 |
| 158 try: |
| 159 from threading import local as threadlocal |
| 160 except ImportError: |
| 161 from cherrypy._cpthreadinglocal import local as threadlocal |
| 162 |
| 163 try: |
| 164 dict.iteritems |
| 165 # Python 2 |
| 166 iteritems = lambda d: d.iteritems() |
| 167 copyitems = lambda d: d.items() |
| 168 except AttributeError: |
| 169 # Python 3 |
| 170 iteritems = lambda d: d.items() |
| 171 copyitems = lambda d: list(d.items()) |
| 172 |
| 173 try: |
| 174 dict.iterkeys |
| 175 # Python 2 |
| 176 iterkeys = lambda d: d.iterkeys() |
| 177 copykeys = lambda d: d.keys() |
| 178 except AttributeError: |
| 179 # Python 3 |
| 180 iterkeys = lambda d: d.keys() |
| 181 copykeys = lambda d: list(d.keys()) |
| 182 |
| 183 try: |
| 184 dict.itervalues |
| 185 # Python 2 |
| 186 itervalues = lambda d: d.itervalues() |
| 187 copyvalues = lambda d: d.values() |
| 188 except AttributeError: |
| 189 # Python 3 |
| 190 itervalues = lambda d: d.values() |
| 191 copyvalues = lambda d: list(d.values()) |
| 192 |
| 193 try: |
| 194 # Python 3 |
| 195 import builtins |
| 196 except ImportError: |
| 197 # Python 2 |
| 198 import __builtin__ as builtins |
| 199 |
| 200 try: |
| 201 # Python 2. We have to do it in this order so Python 2 builds |
| 202 # don't try to import the 'http' module from cherrypy.lib |
| 203 from Cookie import SimpleCookie, CookieError |
| 204 from httplib import BadStatusLine, HTTPConnection, HTTPSConnection, Incomple
teRead, NotConnected |
| 205 from BaseHTTPServer import BaseHTTPRequestHandler |
| 206 except ImportError: |
| 207 # Python 3 |
| 208 from http.cookies import SimpleCookie, CookieError |
| 209 from http.client import BadStatusLine, HTTPConnection, HTTPSConnection, Inco
mpleteRead, NotConnected |
| 210 from http.server import BaseHTTPRequestHandler |
| 211 |
| 212 try: |
| 213 # Python 2. We have to do it in this order so Python 2 builds |
| 214 # don't try to import the 'http' module from cherrypy.lib |
| 215 from httplib import HTTPSConnection |
| 216 except ImportError: |
| 217 try: |
| 218 # Python 3 |
| 219 from http.client import HTTPSConnection |
| 220 except ImportError: |
| 221 # Some platforms which don't have SSL don't expose HTTPSConnection |
| 222 HTTPSConnection = None |
| 223 |
| 224 try: |
| 225 # Python 2 |
| 226 xrange = xrange |
| 227 except NameError: |
| 228 # Python 3 |
| 229 xrange = range |
| 230 |
| 231 import threading |
| 232 if hasattr(threading.Thread, "daemon"): |
| 233 # Python 2.6+ |
| 234 def get_daemon(t): |
| 235 return t.daemon |
| 236 def set_daemon(t, val): |
| 237 t.daemon = val |
| 238 else: |
| 239 def get_daemon(t): |
| 240 return t.isDaemon() |
| 241 def set_daemon(t, val): |
| 242 t.setDaemon(val) |
| 243 |
| 244 try: |
| 245 from email.utils import formatdate |
| 246 def HTTPDate(timeval=None): |
| 247 return formatdate(timeval, usegmt=True) |
| 248 except ImportError: |
| 249 from rfc822 import formatdate as HTTPDate |
| 250 |
| 251 try: |
| 252 # Python 3 |
| 253 from urllib.parse import unquote as parse_unquote |
| 254 def unquote_qs(atom, encoding, errors='strict'): |
| 255 return parse_unquote(atom.replace('+', ' '), encoding=encoding, errors=e
rrors) |
| 256 except ImportError: |
| 257 # Python 2 |
| 258 from urllib import unquote as parse_unquote |
| 259 def unquote_qs(atom, encoding, errors='strict'): |
| 260 return parse_unquote(atom.replace('+', ' ')).decode(encoding, errors) |
| 261 |
| 262 try: |
| 263 # Prefer simplejson, which is usually more advanced than the builtin module. |
| 264 import simplejson as json |
| 265 json_decode = json.JSONDecoder().decode |
| 266 json_encode = json.JSONEncoder().iterencode |
| 267 except ImportError: |
| 268 if py3k: |
| 269 # Python 3.0: json is part of the standard library, |
| 270 # but outputs unicode. We need bytes. |
| 271 import json |
| 272 json_decode = json.JSONDecoder().decode |
| 273 _json_encode = json.JSONEncoder().iterencode |
| 274 def json_encode(value): |
| 275 for chunk in _json_encode(value): |
| 276 yield chunk.encode('utf8') |
| 277 elif sys.version_info >= (2, 6): |
| 278 # Python 2.6: json is part of the standard library |
| 279 import json |
| 280 json_decode = json.JSONDecoder().decode |
| 281 json_encode = json.JSONEncoder().iterencode |
| 282 else: |
| 283 json = None |
| 284 def json_decode(s): |
| 285 raise ValueError('No JSON library is available') |
| 286 def json_encode(s): |
| 287 raise ValueError('No JSON library is available') |
| 288 |
| 289 try: |
| 290 import cPickle as pickle |
| 291 except ImportError: |
| 292 # In Python 2, pickle is a Python version. |
| 293 # In Python 3, pickle is the sped-up C version. |
| 294 import pickle |
| 295 |
| 296 try: |
| 297 os.urandom(20) |
| 298 import binascii |
| 299 def random20(): |
| 300 return binascii.hexlify(os.urandom(20)).decode('ascii') |
| 301 except (AttributeError, NotImplementedError): |
| 302 import random |
| 303 # os.urandom not available until Python 2.4. Fall back to random.random. |
| 304 def random20(): |
| 305 return sha('%s' % random.random()).hexdigest() |
| 306 |
| 307 try: |
| 308 from _thread import get_ident as get_thread_ident |
| 309 except ImportError: |
| 310 from thread import get_ident as get_thread_ident |
| 311 |
| 312 try: |
| 313 # Python 3 |
| 314 next = next |
| 315 except NameError: |
| 316 # Python 2 |
| 317 def next(i): |
| 318 return i.next() |
OLD | NEW |