Fixed python_path problem.
[smonitor.git] / lib / cherrypy / _cpcompat.py
blob216ddddce89b66d1ff13e460ac03b3aef4b703cf
1 """Compatibility code for using CherryPy with various versions of Python.
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.
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 sys
21 if sys.version_info >= (3, 0):
22 bytestr = bytes
23 unicodestr = str
24 nativestr = unicodestr
25 basestring = (bytes, str)
26 def ntob(n, encoding='ISO-8859-1'):
27 """Return the given native string as a byte string in the given encoding."""
28 # In Python 3, the native string type is unicode
29 return n.encode(encoding)
30 def ntou(n, encoding='ISO-8859-1'):
31 """Return the given native string as a unicode string with the given encoding."""
32 # In Python 3, the native string type is unicode
33 return n
34 # type("")
35 from io import StringIO
36 # bytes:
37 from io import BytesIO as BytesIO
38 else:
39 # Python 2
40 bytestr = str
41 unicodestr = unicode
42 nativestr = bytestr
43 basestring = basestring
44 def ntob(n, encoding='ISO-8859-1'):
45 """Return the given native string as a byte string in the given encoding."""
46 # In Python 2, the native string type is bytes. Assume it's already
47 # in the given encoding, which for ISO-8859-1 is almost always what
48 # was intended.
49 return n
50 def ntou(n, encoding='ISO-8859-1'):
51 """Return the given native string as a unicode string with the given encoding."""
52 # In Python 2, the native string type is bytes. Assume it's already
53 # in the given encoding, which for ISO-8859-1 is almost always what
54 # was intended.
55 return n.decode(encoding)
56 try:
57 # type("")
58 from cStringIO import StringIO
59 except ImportError:
60 # type("")
61 from StringIO import StringIO
62 # bytes:
63 BytesIO = StringIO
65 try:
66 set = set
67 except NameError:
68 from sets import Set as set
70 try:
71 # Python 3.1+
72 from base64 import decodebytes as _base64_decodebytes
73 except ImportError:
74 # Python 3.0-
75 # since CherryPy claims compability with Python 2.3, we must use
76 # the legacy API of base64
77 from base64 import decodestring as _base64_decodebytes
79 def base64_decode(n, encoding='ISO-8859-1'):
80 """Return the native string base64-decoded (as a native string)."""
81 if isinstance(n, unicodestr):
82 b = n.encode(encoding)
83 else:
84 b = n
85 b = _base64_decodebytes(b)
86 if nativestr is unicodestr:
87 return b.decode(encoding)
88 else:
89 return b
91 try:
92 # Python 2.5+
93 from hashlib import md5
94 except ImportError:
95 from md5 import new as md5
97 try:
98 # Python 2.5+
99 from hashlib import sha1 as sha
100 except ImportError:
101 from sha import new as sha
103 try:
104 sorted = sorted
105 except NameError:
106 def sorted(i):
107 i = i[:]
108 i.sort()
109 return i
111 try:
112 reversed = reversed
113 except NameError:
114 def reversed(x):
115 i = len(x)
116 while i > 0:
117 i -= 1
118 yield x[i]
120 try:
121 # Python 3
122 from urllib.parse import urljoin, urlencode
123 from urllib.parse import quote, quote_plus
124 from urllib.request import unquote, urlopen
125 from urllib.request import parse_http_list, parse_keqv_list
126 except ImportError:
127 # Python 2
128 from urlparse import urljoin
129 from urllib import urlencode, urlopen
130 from urllib import quote, quote_plus
131 from urllib import unquote
132 from urllib2 import parse_http_list, parse_keqv_list
134 try:
135 from threading import local as threadlocal
136 except ImportError:
137 from cherrypy._cpthreadinglocal import local as threadlocal
139 try:
140 dict.iteritems
141 # Python 2
142 iteritems = lambda d: d.iteritems()
143 copyitems = lambda d: d.items()
144 except AttributeError:
145 # Python 3
146 iteritems = lambda d: d.items()
147 copyitems = lambda d: list(d.items())
149 try:
150 dict.iterkeys
151 # Python 2
152 iterkeys = lambda d: d.iterkeys()
153 copykeys = lambda d: d.keys()
154 except AttributeError:
155 # Python 3
156 iterkeys = lambda d: d.keys()
157 copykeys = lambda d: list(d.keys())
159 try:
160 dict.itervalues
161 # Python 2
162 itervalues = lambda d: d.itervalues()
163 copyvalues = lambda d: d.values()
164 except AttributeError:
165 # Python 3
166 itervalues = lambda d: d.values()
167 copyvalues = lambda d: list(d.values())
169 try:
170 # Python 3
171 import builtins
172 except ImportError:
173 # Python 2
174 import __builtin__ as builtins
176 try:
177 # Python 2. We have to do it in this order so Python 2 builds
178 # don't try to import the 'http' module from cherrypy.lib
179 from Cookie import SimpleCookie, CookieError
180 from httplib import BadStatusLine, HTTPConnection, HTTPSConnection, IncompleteRead, NotConnected
181 from BaseHTTPServer import BaseHTTPRequestHandler
182 except ImportError:
183 # Python 3
184 from http.cookies import SimpleCookie, CookieError
185 from http.client import BadStatusLine, HTTPConnection, HTTPSConnection, IncompleteRead, NotConnected
186 from http.server import BaseHTTPRequestHandler
188 try:
189 # Python 2
190 xrange = xrange
191 except NameError:
192 # Python 3
193 xrange = range
195 import threading
196 if hasattr(threading.Thread, "daemon"):
197 # Python 2.6+
198 def get_daemon(t):
199 return t.daemon
200 def set_daemon(t, val):
201 t.daemon = val
202 else:
203 def get_daemon(t):
204 return t.isDaemon()
205 def set_daemon(t, val):
206 t.setDaemon(val)
208 try:
209 from email.utils import formatdate
210 def HTTPDate(timeval=None):
211 return formatdate(timeval, usegmt=True)
212 except ImportError:
213 from rfc822 import formatdate as HTTPDate
215 try:
216 # Python 3
217 from urllib.parse import unquote as parse_unquote
218 def unquote_qs(atom, encoding, errors='strict'):
219 return parse_unquote(atom.replace('+', ' '), encoding=encoding, errors=errors)
220 except ImportError:
221 # Python 2
222 from urllib import unquote as parse_unquote
223 def unquote_qs(atom, encoding, errors='strict'):
224 return parse_unquote(atom.replace('+', ' ')).decode(encoding, errors)
226 try:
227 # Prefer simplejson, which is usually more advanced than the builtin module.
228 import simplejson as json
229 json_decode = json.JSONDecoder().decode
230 json_encode = json.JSONEncoder().iterencode
231 except ImportError:
232 if sys.version_info >= (3, 0):
233 # Python 3.0: json is part of the standard library,
234 # but outputs unicode. We need bytes.
235 import json
236 json_decode = json.JSONDecoder().decode
237 _json_encode = json.JSONEncoder().iterencode
238 def json_encode(value):
239 for chunk in _json_encode(value):
240 yield chunk.encode('utf8')
241 elif sys.version_info >= (2, 6):
242 # Python 2.6: json is part of the standard library
243 import json
244 json_decode = json.JSONDecoder().decode
245 json_encode = json.JSONEncoder().iterencode
246 else:
247 json = None
248 def json_decode(s):
249 raise ValueError('No JSON library is available')
250 def json_encode(s):
251 raise ValueError('No JSON library is available')
253 try:
254 import cPickle as pickle
255 except ImportError:
256 # In Python 2, pickle is a Python version.
257 # In Python 3, pickle is the sped-up C version.
258 import pickle
260 try:
261 os.urandom(20)
262 import binascii
263 def random20():
264 return binascii.hexlify(os.urandom(20)).decode('ascii')
265 except (AttributeError, NotImplementedError):
266 import random
267 # os.urandom not available until Python 2.4. Fall back to random.random.
268 def random20():
269 return sha('%s' % random.random()).hexdigest()
271 try:
272 from _thread import get_ident as get_thread_ident
273 except ImportError:
274 from thread import get_ident as get_thread_ident
276 try:
277 # Python 3
278 next = next
279 except NameError:
280 # Python 2
281 def next(i):
282 return i.next()