Package restkit :: Module util
[hide private]
[frames] | no frames]

Source Code for Module restkit.util

  1  # -*- coding: utf-8 - 
  2  # 
  3  # This file is part of restkit released under the MIT license.  
  4  # See the NOTICE for more information. 
  5   
  6  import os 
  7  import re 
  8  import time 
  9  import urllib 
 10  import urlparse 
 11  import warnings 
 12   
 13  from restkit.errors import InvalidUrl 
 14   
 15  absolute_http_url_re = re.compile(r"^https?://", re.I) 
 16   
 17  try:#python 2.6, use subprocess 
 18      import subprocess 
 19      subprocess.Popen  # trigger ImportError early 
 20      closefds = os.name == 'posix' 
 21       
22 - def popen3(cmd, mode='t', bufsize=0):
23 p = subprocess.Popen(cmd, shell=True, bufsize=bufsize, 24 stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 25 close_fds=closefds) 26 p.wait() 27 return (p.stdin, p.stdout, p.stderr)
28 except ImportError: 29 subprocess = None 30 popen3 = os.popen3 31
32 -def locate_program(program):
33 if os.path.isabs(program): 34 return program 35 if os.path.dirname(program): 36 program = os.path.normpath(os.path.realpath(program)) 37 return program 38 paths = os.getenv('PATH') 39 if not paths: 40 return False 41 for path in paths.split(os.pathsep): 42 filename = os.path.join(path, program) 43 if os.access(filename, os.X_OK): 44 return filename 45 return False
46 47 weekdayname = ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'] 48 monthname = [None, 49 'Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 50 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'] 51
52 -def http_date(timestamp=None):
53 """Return the current date and time formatted for a message header.""" 54 if timestamp is None: 55 timestamp = time.time() 56 year, month, day, hh, mm, ss, wd, y, z = time.gmtime(timestamp) 57 s = "%s, %02d %3s %4d %02d:%02d:%02d GMT" % ( 58 weekdayname[wd], 59 day, monthname[month], year, 60 hh, mm, ss) 61 return s
62
63 -def parse_netloc(uri):
64 host = uri.netloc 65 port = None 66 i = host.rfind(':') 67 j = host.rfind(']') # ipv6 addresses have [...] 68 if i > j: 69 try: 70 port = int(host[i+1:]) 71 except ValueError: 72 raise InvalidUrl("nonnumeric port: '%s'" % host[i+1:]) 73 host = host[:i] 74 else: 75 # default port 76 if uri.scheme == "https": 77 port = 443 78 else: 79 port = 80 80 81 if host and host[0] == '[' and host[-1] == ']': 82 host = host[1:-1] 83 return (host, port)
84
85 -def to_bytestring(s):
86 if not isinstance(s, basestring): 87 raise TypeError("value should be a str or unicode") 88 89 if isinstance(s, unicode): 90 return s.encode('utf-8') 91 return s
92
93 -def url_quote(s, charset='utf-8', safe='/:'):
94 """URL encode a single string with a given encoding.""" 95 if isinstance(s, unicode): 96 s = s.encode(charset) 97 elif not isinstance(s, str): 98 s = str(s) 99 return urllib.quote(s, safe=safe)
100 101
102 -def url_encode(obj, charset="utf8", encode_keys=False):
103 items = [] 104 if isinstance(obj, dict): 105 for k, v in list(obj.items()): 106 items.append((k, v)) 107 else: 108 items = list(items) 109 110 tmp = [] 111 for k, v in items: 112 if encode_keys: 113 k = encode(k, charset) 114 115 if not isinstance(v, (tuple, list)): 116 v = [v] 117 118 for v1 in v: 119 if v1 is None: 120 v1 = '' 121 elif callable(v1): 122 v1 = encode(v1(), charset) 123 else: 124 v1 = encode(v1, charset) 125 tmp.append('%s=%s' % (urllib.quote(k), urllib.quote_plus(v1))) 126 return '&'.join(tmp)
127
128 -def encode(v, charset="utf8"):
129 if isinstance(v, unicode): 130 v = v.encode(charset) 131 else: 132 v = str(v) 133 return v
134 135
136 -def make_uri(base, *args, **kwargs):
137 """Assemble a uri based on a base, any number of path segments, 138 and query string parameters. 139 140 """ 141 142 # get encoding parameters 143 charset = kwargs.pop("charset", "utf-8") 144 safe = kwargs.pop("safe", "/:") 145 encode_keys = kwargs.pop("encode_keys", True) 146 147 base_trailing_slash = False 148 if base and base.endswith("/"): 149 base_trailing_slash = True 150 base = base[:-1] 151 retval = [base] 152 153 # build the path 154 _path = [] 155 trailing_slash = False 156 for s in args: 157 if s is not None and isinstance(s, basestring): 158 if len(s) > 1 and s.endswith('/'): 159 trailing_slash = True 160 else: 161 trailing_slash = False 162 _path.append(url_quote(s.strip('/'), charset, safe)) 163 164 path_str ="" 165 if _path: 166 path_str = "/".join([''] + _path) 167 if trailing_slash: 168 path_str = path_str + "/" 169 elif base_trailing_slash: 170 path_str = path_str + "/" 171 172 if path_str: 173 retval.append(path_str) 174 175 params_str = url_encode(kwargs, charset, encode_keys) 176 if params_str: 177 retval.extend(['?', params_str]) 178 179 return ''.join(retval)
180 181
182 -def rewrite_location(host_uri, location, prefix_path=None):
183 prefix_path = prefix_path or '' 184 url = urlparse.urlparse(location) 185 host_url = urlparse.urlparse(host_uri) 186 187 if not absolute_http_url_re.match(location): 188 # remote server doesn't follow rfc2616 189 proxy_uri = '%s%s' % (host_uri, prefix_path) 190 return urlparse.urljoin(proxy_uri, location) 191 elif url.scheme == host_url.scheme and url.netloc == host_url.netloc: 192 return urlparse.urlunparse((host_url.scheme, host_url.netloc, 193 prefix_path + url.path, url.params, url.query, url.fragment)) 194 195 return location
196
197 -def replace_header(name, value, headers):
198 idx = -1 199 for i, (k, v) in enumerate(headers): 200 if k.upper() == name.upper(): 201 idx = i 202 break 203 if idx >= 0: 204 headers[i] = (name.title(), value) 205 else: 206 headers.append((name.title(), value)) 207 return headers
208
209 -def replace_headers(new_headers, headers):
210 hdrs = {} 211 for (k, v) in new_headers: 212 hdrs[k.upper()] = v 213 214 found = [] 215 for i, (k, v) in enumerate(headers): 216 ku = k.upper() 217 if ku in hdrs: 218 headers[i] = (k.title(), hdrs[ku]) 219 found.append(ku) 220 if len(found) == len(new_headers): 221 return 222 223 for k, v in new_headers.items(): 224 if k not in found: 225 headers.append((k.title(), v)) 226 return headers
227 228 229
230 -class deprecated_property(object):
231 """ 232 Wraps a decorator, with a deprecation warning or error 233 """
234 - def __init__(self, decorator, attr, message, warning=True):
235 self.decorator = decorator 236 self.attr = attr 237 self.message = message 238 self.warning = warning
239
240 - def __get__(self, obj, type=None):
241 if obj is None: 242 return self 243 self.warn() 244 return self.decorator.__get__(obj, type)
245
246 - def __set__(self, obj, value):
247 self.warn() 248 self.decorator.__set__(obj, value)
249
250 - def __delete__(self, obj):
251 self.warn() 252 self.decorator.__delete__(obj)
253
254 - def __repr__(self):
255 return '<Deprecated attribute %s: %r>' % ( 256 self.attr, 257 self.decorator)
258
259 - def warn(self):
260 if not self.warning: 261 raise DeprecationWarning( 262 'The attribute %s is deprecated: %s' % (self.attr, self.message)) 263 else: 264 warnings.warn( 265 'The attribute %s is deprecated: %s' % (self.attr, self.message), 266 DeprecationWarning, 267 stacklevel=3)
268