1 | # (c) 2005 Ian Bicking and contributors; written for Paste (http://pythonpaste.org) |
---|
2 | # Licensed under the MIT license: http://www.opensource.org/licenses/mit-license.php |
---|
3 | |
---|
4 | """ |
---|
5 | A module of many disparate routines. |
---|
6 | """ |
---|
7 | |
---|
8 | # functions which moved to paste.request and paste.response |
---|
9 | # Deprecated around 15 Dec 2005 |
---|
10 | from paste.request import get_cookies, parse_querystring, parse_formvars |
---|
11 | from paste.request import construct_url, path_info_split, path_info_pop |
---|
12 | from paste.response import HeaderDict, has_header, header_value, remove_header |
---|
13 | from paste.response import error_body_response, error_response, error_response_app |
---|
14 | |
---|
15 | from traceback import print_exception |
---|
16 | import urllib |
---|
17 | from cStringIO import StringIO |
---|
18 | import sys |
---|
19 | from urlparse import urlsplit |
---|
20 | import warnings |
---|
21 | |
---|
22 | __all__ = ['add_close', 'add_start_close', 'capture_output', 'catch_errors', |
---|
23 | 'catch_errors_app', 'chained_app_iters', 'construct_url', |
---|
24 | 'dump_environ', 'encode_unicode_app_iter', 'error_body_response', |
---|
25 | 'error_response', 'get_cookies', 'has_header', 'header_value', |
---|
26 | 'interactive', 'intercept_output', 'path_info_pop', |
---|
27 | 'path_info_split', 'raw_interactive', 'send_file'] |
---|
28 | |
---|
29 | class add_close(object): |
---|
30 | """ |
---|
31 | An an iterable that iterates over app_iter, then calls |
---|
32 | close_func. |
---|
33 | """ |
---|
34 | |
---|
35 | def __init__(self, app_iterable, close_func): |
---|
36 | self.app_iterable = app_iterable |
---|
37 | self.app_iter = iter(app_iterable) |
---|
38 | self.close_func = close_func |
---|
39 | self._closed = False |
---|
40 | |
---|
41 | def __iter__(self): |
---|
42 | return self |
---|
43 | |
---|
44 | def next(self): |
---|
45 | return self.app_iter.next() |
---|
46 | |
---|
47 | def close(self): |
---|
48 | self._closed = True |
---|
49 | if hasattr(self.app_iterable, 'close'): |
---|
50 | self.app_iterable.close() |
---|
51 | self.close_func() |
---|
52 | |
---|
53 | def __del__(self): |
---|
54 | if not self._closed: |
---|
55 | # We can't raise an error or anything at this stage |
---|
56 | print >> sys.stderr, ( |
---|
57 | "Error: app_iter.close() was not called when finishing " |
---|
58 | "WSGI request. finalization function %s not called" |
---|
59 | % self.close_func) |
---|
60 | |
---|
61 | class add_start_close(object): |
---|
62 | """ |
---|
63 | An an iterable that iterates over app_iter, calls start_func |
---|
64 | before the first item is returned, then calls close_func at the |
---|
65 | end. |
---|
66 | """ |
---|
67 | |
---|
68 | def __init__(self, app_iterable, start_func, close_func=None): |
---|
69 | self.app_iterable = app_iterable |
---|
70 | self.app_iter = iter(app_iterable) |
---|
71 | self.first = True |
---|
72 | self.start_func = start_func |
---|
73 | self.close_func = close_func |
---|
74 | self._closed = False |
---|
75 | |
---|
76 | def __iter__(self): |
---|
77 | return self |
---|
78 | |
---|
79 | def next(self): |
---|
80 | if self.first: |
---|
81 | self.start_func() |
---|
82 | self.first = False |
---|
83 | return self.app_iter.next() |
---|
84 | |
---|
85 | def close(self): |
---|
86 | self._closed = True |
---|
87 | if hasattr(self.app_iterable, 'close'): |
---|
88 | self.app_iterable.close() |
---|
89 | if self.close_func is not None: |
---|
90 | self.close_func() |
---|
91 | |
---|
92 | def __del__(self): |
---|
93 | if not self._closed: |
---|
94 | # We can't raise an error or anything at this stage |
---|
95 | print >> sys.stderr, ( |
---|
96 | "Error: app_iter.close() was not called when finishing " |
---|
97 | "WSGI request. finalization function %s not called" |
---|
98 | % self.close_func) |
---|
99 | |
---|
100 | class chained_app_iters(object): |
---|
101 | |
---|
102 | """ |
---|
103 | Chains several app_iters together, also delegating .close() to each |
---|
104 | of them. |
---|
105 | """ |
---|
106 | |
---|
107 | def __init__(self, *chained): |
---|
108 | self.app_iters = chained |
---|
109 | self.chained = [iter(item) for item in chained] |
---|
110 | self._closed = False |
---|
111 | |
---|
112 | def __iter__(self): |
---|
113 | return self |
---|
114 | |
---|
115 | def next(self): |
---|
116 | if len(self.chained) == 1: |
---|
117 | return self.chained[0].next() |
---|
118 | else: |
---|
119 | try: |
---|
120 | return self.chained[0].next() |
---|
121 | except StopIteration: |
---|
122 | self.chained.pop(0) |
---|
123 | return self.next() |
---|
124 | |
---|
125 | def close(self): |
---|
126 | self._closed = True |
---|
127 | got_exc = None |
---|
128 | for app_iter in self.app_iters: |
---|
129 | try: |
---|
130 | if hasattr(app_iter, 'close'): |
---|
131 | app_iter.close() |
---|
132 | except: |
---|
133 | got_exc = sys.exc_info() |
---|
134 | if got_exc: |
---|
135 | raise got_exc[0], got_exc[1], got_exc[2] |
---|
136 | |
---|
137 | def __del__(self): |
---|
138 | if not self._closed: |
---|
139 | # We can't raise an error or anything at this stage |
---|
140 | print >> sys.stderr, ( |
---|
141 | "Error: app_iter.close() was not called when finishing " |
---|
142 | "WSGI request. finalization function %s not called" |
---|
143 | % self.close_func) |
---|
144 | |
---|
145 | class encode_unicode_app_iter(object): |
---|
146 | """ |
---|
147 | Encodes an app_iterable's unicode responses as strings |
---|
148 | """ |
---|
149 | |
---|
150 | def __init__(self, app_iterable, encoding=sys.getdefaultencoding(), |
---|
151 | errors='strict'): |
---|
152 | self.app_iterable = app_iterable |
---|
153 | self.app_iter = iter(app_iterable) |
---|
154 | self.encoding = encoding |
---|
155 | self.errors = errors |
---|
156 | |
---|
157 | def __iter__(self): |
---|
158 | return self |
---|
159 | |
---|
160 | def next(self): |
---|
161 | content = self.app_iter.next() |
---|
162 | if isinstance(content, unicode): |
---|
163 | content = content.encode(self.encoding, self.errors) |
---|
164 | return content |
---|
165 | |
---|
166 | def close(self): |
---|
167 | if hasattr(self.app_iterable, 'close'): |
---|
168 | self.app_iterable.close() |
---|
169 | |
---|
170 | def catch_errors(application, environ, start_response, error_callback, |
---|
171 | ok_callback=None): |
---|
172 | """ |
---|
173 | Runs the application, and returns the application iterator (which should be |
---|
174 | passed upstream). If an error occurs then error_callback will be called with |
---|
175 | exc_info as its sole argument. If no errors occur and ok_callback is given, |
---|
176 | then it will be called with no arguments. |
---|
177 | """ |
---|
178 | try: |
---|
179 | app_iter = application(environ, start_response) |
---|
180 | except: |
---|
181 | error_callback(sys.exc_info()) |
---|
182 | raise |
---|
183 | if type(app_iter) in (list, tuple): |
---|
184 | # These won't produce exceptions |
---|
185 | if ok_callback: |
---|
186 | ok_callback() |
---|
187 | return app_iter |
---|
188 | else: |
---|
189 | return _wrap_app_iter(app_iter, error_callback, ok_callback) |
---|
190 | |
---|
191 | class _wrap_app_iter(object): |
---|
192 | |
---|
193 | def __init__(self, app_iterable, error_callback, ok_callback): |
---|
194 | self.app_iterable = app_iterable |
---|
195 | self.app_iter = iter(app_iterable) |
---|
196 | self.error_callback = error_callback |
---|
197 | self.ok_callback = ok_callback |
---|
198 | if hasattr(self.app_iterable, 'close'): |
---|
199 | self.close = self.app_iterable.close |
---|
200 | |
---|
201 | def __iter__(self): |
---|
202 | return self |
---|
203 | |
---|
204 | def next(self): |
---|
205 | try: |
---|
206 | return self.app_iter.next() |
---|
207 | except StopIteration: |
---|
208 | if self.ok_callback: |
---|
209 | self.ok_callback() |
---|
210 | raise |
---|
211 | except: |
---|
212 | self.error_callback(sys.exc_info()) |
---|
213 | raise |
---|
214 | |
---|
215 | def catch_errors_app(application, environ, start_response, error_callback_app, |
---|
216 | ok_callback=None, catch=Exception): |
---|
217 | """ |
---|
218 | Like ``catch_errors``, except error_callback_app should be a |
---|
219 | callable that will receive *three* arguments -- ``environ``, |
---|
220 | ``start_response``, and ``exc_info``. It should call |
---|
221 | ``start_response`` (*with* the exc_info argument!) and return an |
---|
222 | iterator. |
---|
223 | """ |
---|
224 | try: |
---|
225 | app_iter = application(environ, start_response) |
---|
226 | except catch: |
---|
227 | return error_callback_app(environ, start_response, sys.exc_info()) |
---|
228 | if type(app_iter) in (list, tuple): |
---|
229 | # These won't produce exceptions |
---|
230 | if ok_callback is not None: |
---|
231 | ok_callback() |
---|
232 | return app_iter |
---|
233 | else: |
---|
234 | return _wrap_app_iter_app( |
---|
235 | environ, start_response, app_iter, |
---|
236 | error_callback_app, ok_callback, catch=catch) |
---|
237 | |
---|
238 | class _wrap_app_iter_app(object): |
---|
239 | |
---|
240 | def __init__(self, environ, start_response, app_iterable, |
---|
241 | error_callback_app, ok_callback, catch=Exception): |
---|
242 | self.environ = environ |
---|
243 | self.start_response = start_response |
---|
244 | self.app_iterable = app_iterable |
---|
245 | self.app_iter = iter(app_iterable) |
---|
246 | self.error_callback_app = error_callback_app |
---|
247 | self.ok_callback = ok_callback |
---|
248 | self.catch = catch |
---|
249 | if hasattr(self.app_iterable, 'close'): |
---|
250 | self.close = self.app_iterable.close |
---|
251 | |
---|
252 | def __iter__(self): |
---|
253 | return self |
---|
254 | |
---|
255 | def next(self): |
---|
256 | try: |
---|
257 | return self.app_iter.next() |
---|
258 | except StopIteration: |
---|
259 | if self.ok_callback: |
---|
260 | self.ok_callback() |
---|
261 | raise |
---|
262 | except self.catch: |
---|
263 | if hasattr(self.app_iterable, 'close'): |
---|
264 | try: |
---|
265 | self.app_iterable.close() |
---|
266 | except: |
---|
267 | # @@: Print to wsgi.errors? |
---|
268 | pass |
---|
269 | new_app_iterable = self.error_callback_app( |
---|
270 | self.environ, self.start_response, sys.exc_info()) |
---|
271 | app_iter = iter(new_app_iterable) |
---|
272 | if hasattr(new_app_iterable, 'close'): |
---|
273 | self.close = new_app_iterable.close |
---|
274 | self.next = app_iter.next |
---|
275 | return self.next() |
---|
276 | |
---|
277 | def raw_interactive(application, path='', raise_on_wsgi_error=False, |
---|
278 | **environ): |
---|
279 | """ |
---|
280 | Runs the application in a fake environment. |
---|
281 | """ |
---|
282 | assert "path_info" not in environ, "argument list changed" |
---|
283 | if raise_on_wsgi_error: |
---|
284 | errors = ErrorRaiser() |
---|
285 | else: |
---|
286 | errors = StringIO() |
---|
287 | basic_environ = { |
---|
288 | # mandatory CGI variables |
---|
289 | 'REQUEST_METHOD': 'GET', # always mandatory |
---|
290 | 'SCRIPT_NAME': '', # may be empty if app is at the root |
---|
291 | 'PATH_INFO': '', # may be empty if at root of app |
---|
292 | 'SERVER_NAME': 'localhost', # always mandatory |
---|
293 | 'SERVER_PORT': '80', # always mandatory |
---|
294 | 'SERVER_PROTOCOL': 'HTTP/1.0', |
---|
295 | # mandatory wsgi variables |
---|
296 | 'wsgi.version': (1, 0), |
---|
297 | 'wsgi.url_scheme': 'http', |
---|
298 | 'wsgi.input': StringIO(''), |
---|
299 | 'wsgi.errors': errors, |
---|
300 | 'wsgi.multithread': False, |
---|
301 | 'wsgi.multiprocess': False, |
---|
302 | 'wsgi.run_once': False, |
---|
303 | } |
---|
304 | if path: |
---|
305 | (_, _, path_info, query, fragment) = urlsplit(str(path)) |
---|
306 | path_info = urllib.unquote(path_info) |
---|
307 | basic_environ['PATH_INFO'] = path_info |
---|
308 | if query: |
---|
309 | basic_environ['QUERY_STRING'] = query |
---|
310 | for name, value in environ.items(): |
---|
311 | name = name.replace('__', '.') |
---|
312 | basic_environ[name] = value |
---|
313 | if ('SERVER_NAME' in basic_environ |
---|
314 | and 'HTTP_HOST' not in basic_environ): |
---|
315 | basic_environ['HTTP_HOST'] = basic_environ['SERVER_NAME'] |
---|
316 | istream = basic_environ['wsgi.input'] |
---|
317 | if isinstance(istream, str): |
---|
318 | basic_environ['wsgi.input'] = StringIO(istream) |
---|
319 | basic_environ['CONTENT_LENGTH'] = len(istream) |
---|
320 | data = {} |
---|
321 | output = [] |
---|
322 | headers_set = [] |
---|
323 | headers_sent = [] |
---|
324 | def start_response(status, headers, exc_info=None): |
---|
325 | if exc_info: |
---|
326 | try: |
---|
327 | if headers_sent: |
---|
328 | # Re-raise original exception only if headers sent |
---|
329 | raise exc_info[0], exc_info[1], exc_info[2] |
---|
330 | finally: |
---|
331 | # avoid dangling circular reference |
---|
332 | exc_info = None |
---|
333 | elif headers_set: |
---|
334 | # You cannot set the headers more than once, unless the |
---|
335 | # exc_info is provided. |
---|
336 | raise AssertionError("Headers already set and no exc_info!") |
---|
337 | headers_set.append(True) |
---|
338 | data['status'] = status |
---|
339 | data['headers'] = headers |
---|
340 | return output.append |
---|
341 | app_iter = application(basic_environ, start_response) |
---|
342 | try: |
---|
343 | try: |
---|
344 | for s in app_iter: |
---|
345 | if not isinstance(s, str): |
---|
346 | raise ValueError( |
---|
347 | "The app_iter response can only contain str (not " |
---|
348 | "unicode); got: %r" % s) |
---|
349 | headers_sent.append(True) |
---|
350 | if not headers_set: |
---|
351 | raise AssertionError("Content sent w/o headers!") |
---|
352 | output.append(s) |
---|
353 | except TypeError, e: |
---|
354 | # Typically "iteration over non-sequence", so we want |
---|
355 | # to give better debugging information... |
---|
356 | e.args = ((e.args[0] + ' iterable: %r' % app_iter),) + e.args[1:] |
---|
357 | raise |
---|
358 | finally: |
---|
359 | if hasattr(app_iter, 'close'): |
---|
360 | app_iter.close() |
---|
361 | return (data['status'], data['headers'], ''.join(output), |
---|
362 | errors.getvalue()) |
---|
363 | |
---|
364 | class ErrorRaiser(object): |
---|
365 | |
---|
366 | def flush(self): |
---|
367 | pass |
---|
368 | |
---|
369 | def write(self, value): |
---|
370 | if not value: |
---|
371 | return |
---|
372 | raise AssertionError( |
---|
373 | "No errors should be written (got: %r)" % value) |
---|
374 | |
---|
375 | def writelines(self, seq): |
---|
376 | raise AssertionError( |
---|
377 | "No errors should be written (got lines: %s)" % list(seq)) |
---|
378 | |
---|
379 | def getvalue(self): |
---|
380 | return '' |
---|
381 | |
---|
382 | def interactive(*args, **kw): |
---|
383 | """ |
---|
384 | Runs the application interatively, wrapping `raw_interactive` but |
---|
385 | returning the output in a formatted way. |
---|
386 | """ |
---|
387 | status, headers, content, errors = raw_interactive(*args, **kw) |
---|
388 | full = StringIO() |
---|
389 | if errors: |
---|
390 | full.write('Errors:\n') |
---|
391 | full.write(errors.strip()) |
---|
392 | full.write('\n----------end errors\n') |
---|
393 | full.write(status + '\n') |
---|
394 | for name, value in headers: |
---|
395 | full.write('%s: %s\n' % (name, value)) |
---|
396 | full.write('\n') |
---|
397 | full.write(content) |
---|
398 | return full.getvalue() |
---|
399 | interactive.proxy = 'raw_interactive' |
---|
400 | |
---|
401 | def dump_environ(environ, start_response): |
---|
402 | """ |
---|
403 | Application which simply dumps the current environment |
---|
404 | variables out as a plain text response. |
---|
405 | """ |
---|
406 | output = [] |
---|
407 | keys = environ.keys() |
---|
408 | keys.sort() |
---|
409 | for k in keys: |
---|
410 | v = str(environ[k]).replace("\n","\n ") |
---|
411 | output.append("%s: %s\n" % (k, v)) |
---|
412 | output.append("\n") |
---|
413 | content_length = environ.get("CONTENT_LENGTH", '') |
---|
414 | if content_length: |
---|
415 | output.append(environ['wsgi.input'].read(int(content_length))) |
---|
416 | output.append("\n") |
---|
417 | output = "".join(output) |
---|
418 | headers = [('Content-Type', 'text/plain'), |
---|
419 | ('Content-Length', str(len(output)))] |
---|
420 | start_response("200 OK", headers) |
---|
421 | return [output] |
---|
422 | |
---|
423 | def send_file(filename): |
---|
424 | warnings.warn( |
---|
425 | "wsgilib.send_file has been moved to paste.fileapp.FileApp", |
---|
426 | DeprecationWarning, 2) |
---|
427 | from paste import fileapp |
---|
428 | return fileapp.FileApp(filename) |
---|
429 | |
---|
430 | def capture_output(environ, start_response, application): |
---|
431 | """ |
---|
432 | Runs application with environ and start_response, and captures |
---|
433 | status, headers, and body. |
---|
434 | |
---|
435 | Sends status and header, but *not* body. Returns (status, |
---|
436 | headers, body). Typically this is used like: |
---|
437 | |
---|
438 | .. code-block:: Python |
---|
439 | |
---|
440 | def dehtmlifying_middleware(application): |
---|
441 | def replacement_app(environ, start_response): |
---|
442 | status, headers, body = capture_output( |
---|
443 | environ, start_response, application) |
---|
444 | content_type = header_value(headers, 'content-type') |
---|
445 | if (not content_type |
---|
446 | or not content_type.startswith('text/html')): |
---|
447 | return [body] |
---|
448 | body = re.sub(r'<.*?>', '', body) |
---|
449 | return [body] |
---|
450 | return replacement_app |
---|
451 | |
---|
452 | """ |
---|
453 | warnings.warn( |
---|
454 | 'wsgilib.capture_output has been deprecated in favor ' |
---|
455 | 'of wsgilib.intercept_output', |
---|
456 | DeprecationWarning, 2) |
---|
457 | data = [] |
---|
458 | output = StringIO() |
---|
459 | def replacement_start_response(status, headers, exc_info=None): |
---|
460 | if data: |
---|
461 | data[:] = [] |
---|
462 | data.append(status) |
---|
463 | data.append(headers) |
---|
464 | start_response(status, headers, exc_info) |
---|
465 | return output.write |
---|
466 | app_iter = application(environ, replacement_start_response) |
---|
467 | try: |
---|
468 | for item in app_iter: |
---|
469 | output.write(item) |
---|
470 | finally: |
---|
471 | if hasattr(app_iter, 'close'): |
---|
472 | app_iter.close() |
---|
473 | if not data: |
---|
474 | data.append(None) |
---|
475 | if len(data) < 2: |
---|
476 | data.append(None) |
---|
477 | data.append(output.getvalue()) |
---|
478 | return data |
---|
479 | |
---|
480 | def intercept_output(environ, application, conditional=None, |
---|
481 | start_response=None): |
---|
482 | """ |
---|
483 | Runs application with environ and captures status, headers, and |
---|
484 | body. None are sent on; you must send them on yourself (unlike |
---|
485 | ``capture_output``) |
---|
486 | |
---|
487 | Typically this is used like: |
---|
488 | |
---|
489 | .. code-block:: Python |
---|
490 | |
---|
491 | def dehtmlifying_middleware(application): |
---|
492 | def replacement_app(environ, start_response): |
---|
493 | status, headers, body = intercept_output( |
---|
494 | environ, application) |
---|
495 | content_type = header_value(headers, 'content-type') |
---|
496 | if (not content_type |
---|
497 | or not content_type.startswith('text/html')): |
---|
498 | return [body] |
---|
499 | body = re.sub(r'<.*?>', '', body) |
---|
500 | return [body] |
---|
501 | return replacement_app |
---|
502 | |
---|
503 | A third optional argument ``conditional`` should be a function |
---|
504 | that takes ``conditional(status, headers)`` and returns False if |
---|
505 | the request should not be intercepted. In that case |
---|
506 | ``start_response`` will be called and ``(None, None, app_iter)`` |
---|
507 | will be returned. You must detect that in your code and return |
---|
508 | the app_iter, like: |
---|
509 | |
---|
510 | .. code-block:: Python |
---|
511 | |
---|
512 | def dehtmlifying_middleware(application): |
---|
513 | def replacement_app(environ, start_response): |
---|
514 | status, headers, body = intercept_output( |
---|
515 | environ, application, |
---|
516 | lambda s, h: header_value(headers, 'content-type').startswith('text/html'), |
---|
517 | start_response) |
---|
518 | if status is None: |
---|
519 | return body |
---|
520 | body = re.sub(r'<.*?>', '', body) |
---|
521 | return [body] |
---|
522 | return replacement_app |
---|
523 | """ |
---|
524 | if conditional is not None and start_response is None: |
---|
525 | raise TypeError( |
---|
526 | "If you provide conditional you must also provide " |
---|
527 | "start_response") |
---|
528 | data = [] |
---|
529 | output = StringIO() |
---|
530 | def replacement_start_response(status, headers, exc_info=None): |
---|
531 | if conditional is not None and not conditional(status, headers): |
---|
532 | data.append(None) |
---|
533 | return start_response(status, headers, exc_info) |
---|
534 | if data: |
---|
535 | data[:] = [] |
---|
536 | data.append(status) |
---|
537 | data.append(headers) |
---|
538 | return output.write |
---|
539 | app_iter = application(environ, replacement_start_response) |
---|
540 | if data[0] is None: |
---|
541 | return (None, None, app_iter) |
---|
542 | try: |
---|
543 | for item in app_iter: |
---|
544 | output.write(item) |
---|
545 | finally: |
---|
546 | if hasattr(app_iter, 'close'): |
---|
547 | app_iter.close() |
---|
548 | if not data: |
---|
549 | data.append(None) |
---|
550 | if len(data) < 2: |
---|
551 | data.append(None) |
---|
552 | data.append(output.getvalue()) |
---|
553 | return data |
---|
554 | |
---|
555 | ## Deprecation warning wrapper: |
---|
556 | |
---|
557 | class ResponseHeaderDict(HeaderDict): |
---|
558 | |
---|
559 | def __init__(self, *args, **kw): |
---|
560 | warnings.warn( |
---|
561 | "The class wsgilib.ResponseHeaderDict has been moved " |
---|
562 | "to paste.response.HeaderDict", |
---|
563 | DeprecationWarning, 2) |
---|
564 | HeaderDict.__init__(self, *args, **kw) |
---|
565 | |
---|
566 | def _warn_deprecated(new_func): |
---|
567 | new_name = new_func.func_name |
---|
568 | new_path = new_func.func_globals['__name__'] + '.' + new_name |
---|
569 | def replacement(*args, **kw): |
---|
570 | warnings.warn( |
---|
571 | "The function wsgilib.%s has been moved to %s" |
---|
572 | % (new_name, new_path), |
---|
573 | DeprecationWarning, 2) |
---|
574 | return new_func(*args, **kw) |
---|
575 | try: |
---|
576 | replacement.func_name = new_func.func_name |
---|
577 | except: |
---|
578 | pass |
---|
579 | return replacement |
---|
580 | |
---|
581 | # Put warnings wrapper in place for all public functions that |
---|
582 | # were imported from elsewhere: |
---|
583 | |
---|
584 | for _name in __all__: |
---|
585 | _func = globals()[_name] |
---|
586 | if (hasattr(_func, 'func_globals') |
---|
587 | and _func.func_globals['__name__'] != __name__): |
---|
588 | globals()[_name] = _warn_deprecated(_func) |
---|
589 | |
---|
590 | if __name__ == '__main__': |
---|
591 | import doctest |
---|
592 | doctest.testmod() |
---|
593 | |
---|