1
# -*- coding: utf-8 -*-
6
This module provides internally used helpers and constants.
8
:copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
9
:license: BSD, see LICENSE for more details.
12
from weakref import WeakKeyDictionary
13
from cStringIO import StringIO
14
from Cookie import BaseCookie, Morsel, CookieError
15
from time import gmtime
16
from datetime import datetime
20
_empty_stream = StringIO('')
21
_signature_cache = WeakKeyDictionary()
26
101: 'Switching Protocols',
31
203: 'Non Authoritative Information',
34
206: 'Partial Content',
36
226: 'IM Used', # see RFC 3229
37
300: 'Multiple Choices',
38
301: 'Moved Permanently',
43
307: 'Temporary Redirect',
46
402: 'Payment Required', # unused
49
405: 'Method Not Allowed',
50
406: 'Not Acceptable',
51
407: 'Proxy Authentication Required',
52
408: 'Request Timeout',
55
411: 'Length Required',
56
412: 'Precondition Failed',
57
413: 'Request Entity Too Large',
58
414: 'Request URI Too Long',
59
415: 'Unsupported Media Type',
60
416: 'Requested Range Not Satisfiable',
61
417: 'Expectation Failed',
62
418: 'I\'m a teapot', # see RFC 2324
63
422: 'Unprocessable Entity',
65
424: 'Failed Dependency',
66
426: 'Upgrade Required',
67
449: 'Retry With', # propritary MS extension
68
500: 'Internal Server Error',
69
501: 'Not Implemented',
71
503: 'Service Unavailable',
72
504: 'Gateway Timeout',
73
505: 'HTTP Version Not Supported',
74
507: 'Insufficient Storage',
79
class _Missing(object):
92
return '%s(%s)' % (self.__class__.__name__, cls.__repr__(self))
96
def _log(type, message, *args, **kwargs):
97
"""Log into the internal werkzeug logger."""
101
_logger = logging.getLogger('werkzeug')
102
if _logger.level == logging.NOTSET:
103
_logger.setLevel(logging.INFO)
104
handler = logging.StreamHandler()
105
_logger.addHandler(handler)
106
getattr(_logger, type)(message.rstrip(), *args, **kwargs)
109
def _parse_signature(func):
110
"""Return a signature object for the function."""
111
if hasattr(func, 'im_func'):
114
# if we have a cached validator for this function, return it
115
parse = _signature_cache.get(func)
116
if parse is not None:
119
# inspect the function signature and collect all the information
120
positional, vararg_var, kwarg_var, defaults = inspect.getargspec(func)
121
defaults = defaults or ()
122
arg_count = len(positional)
124
for idx, name in enumerate(positional):
125
if isinstance(name, list):
126
raise TypeError('cannot parse functions that unpack tuples '
127
'in the function signature')
129
default = defaults[idx - arg_count]
131
param = (name, False, None)
133
param = (name, True, default)
134
arguments.append(param)
135
arguments = tuple(arguments)
137
def parse(args, kwargs):
142
# consume as many arguments as positional as possible
143
for idx, (name, has_default, default) in enumerate(arguments):
145
new_args.append(args[idx])
148
new_args.append(kwargs.pop(name))
151
new_args.append(default)
156
extra[name] = kwargs.pop(name)
158
# handle extra arguments
159
extra_positional = args[arg_count:]
160
if vararg_var is not None:
161
new_args.extend(extra_positional)
162
extra_positional = ()
163
if kwargs and not kwarg_var is not None:
167
return new_args, kwargs, missing, extra, extra_positional, \
168
arguments, vararg_var, kwarg_var
169
_signature_cache[func] = parse
173
def _patch_wrapper(old, new):
174
"""Helper function that forwards all the function details to the
175
decorated function."""
177
new.__name__ = old.__name__
178
new.__module__ = old.__module__
179
new.__doc__ = old.__doc__
180
new.__dict__ = old.__dict__
186
def _decode_unicode(value, charset, errors):
187
"""Like the regular decode function but this one raises an
188
`HTTPUnicodeError` if errors is `strict`."""
190
if errors.startswith('fallback:'):
191
fallback = errors[9:]
194
return value.decode(charset, errors)
195
except UnicodeError, e:
196
if fallback is not None:
197
return value.decode(fallback, 'ignore')
198
from werkzeug.exceptions import HTTPUnicodeError
199
raise HTTPUnicodeError(str(e))
202
def _iter_modules(path):
203
"""Iterate over all modules in a package."""
206
if hasattr(pkgutil, 'iter_modules'):
207
for importer, modname, ispkg in pkgutil.iter_modules(path):
210
from inspect import getmodulename
211
from pydoc import ispackage
214
for filename in os.listdir(path):
215
p = os.path.join(path, filename)
216
modname = getmodulename(filename)
217
if modname and modname != '__init__':
218
if modname not in found:
220
yield modname, ispackage(modname)
223
def _dump_date(d, delim):
224
"""Used for `http_date` and `cookie_date`."""
227
elif isinstance(d, datetime):
229
elif isinstance(d, (int, long, float)):
231
return '%s, %02d%s%s%s%s %02d:%02d:%02d GMT' % (
232
('Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun')[d.tm_wday],
234
('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep',
235
'Oct', 'Nov', 'Dec')[d.tm_mon - 1],
236
delim, str(d.tm_year), d.tm_hour, d.tm_min, d.tm_sec
241
def _date_to_unix(arg):
242
"""Converts a timetuple, integer or datetime object into the seconds from
246
if isinstance(arg, datetime):
247
arg = arg.utctimetuple()
248
elif isinstance(arg, (int, long, float)):
251
from calendar import timegm as _timegm
255
class _ExtendedMorsel(Morsel):
256
_reserved = {'httponly': 'HttpOnly'}
257
_reserved.update(Morsel._reserved)
259
def __init__(self, name=None, value=None):
260
Morsel.__init__(self)
262
self.set(name, value, value)
264
def OutputString(self, attrs=None):
265
httponly = self.pop('httponly', False)
266
result = Morsel.OutputString(self, attrs).rstrip('\t ;')
268
result += '; HttpOnly'
272
class _ExtendedCookie(BaseCookie):
273
"""Form of the base cookie that doesn't raise a `CookieError` for
274
malformed keys. This has the advantage that broken cookies submitted
275
by nonstandard browsers don't cause the cookie to be empty.
278
def _BaseCookie__set(self, key, real_value, coded_value):
279
morsel = self.get(key, _ExtendedMorsel())
281
morsel.set(key, real_value, coded_value)
284
dict.__setitem__(self, key, morsel)
287
class _DictAccessorProperty(object):
288
"""Baseclass for `environ_property` and `header_property`."""
291
def __init__(self, name, default=None, load_func=None, dump_func=None,
292
read_only=None, doc=None):
294
self.default = default
295
self.load_func = load_func
296
self.dump_func = dump_func
297
if read_only is not None:
298
self.read_only = read_only
301
def __get__(self, obj, type=None):
304
storage = self.lookup(obj)
305
if self.name not in storage:
307
rv = storage[self.name]
308
if self.load_func is not None:
310
rv = self.load_func(rv)
311
except (ValueError, TypeError):
315
def __set__(self, obj, value):
317
raise AttributeError('read only property')
318
if self.dump_func is not None:
319
value = self.dump_func(value)
320
self.lookup(obj)[self.name] = value
322
def __delete__(self, obj):
324
raise AttributeError('read only property')
325
self.lookup(obj).pop(self.name, None)
329
self.__class__.__name__,
335
"""Like the name says. But who knows how it works?"""
336
gyver = '\n'.join([x + (77 - len(x)) * ' ' for x in '''
337
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
367
7f2zLkGNv8b191cD/3vs9Q833z8t'''.decode('base64').decode('zlib').splitlines()])
368
def easteregged(environ, start_response):
369
def injecting_start_response(status, headers, exc_info=None):
370
headers.append(('X-Powered-By', 'Werkzeug'))
371
return start_response(status, headers, exc_info)
372
if environ.get('QUERY_STRING') != 'macgybarchakku':
373
return app(environ, injecting_start_response)
374
injecting_start_response('200 OK', [('Content-Type', 'text/html')])
375
return ['''<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN">
376
<title>About Werkzeug</>
377
<style type="text/css">
378
body { font: 15px Georgia, serif; text-align: center; }
379
a { color: #333; text-decoration: none; }
380
h1 { font-size: 30px; margin: 20px 0 10px 0; }
381
p { margin: 0 0 30px 0; }
382
pre { font: 11px 'Consolas', 'Monaco', monospace; line-height: 0.95; }
384
<h1><a href="http://werkzeug.pocoo.org/">Werkzeug</a></h1>
385
<p>the Swiss Army knife of Python web development.
386
<pre>%s\n\n\n</>''' % gyver]