~ubuntu-branches/ubuntu/natty/moin/natty-updates

« back to all changes in this revision

Viewing changes to MoinMoin/support/werkzeug/utils.py

  • Committer: Bazaar Package Importer
  • Author(s): Jonas Smedegaard
  • Date: 2008-06-22 21:17:13 UTC
  • mto: This revision was merged to the branch mainline in revision 18.
  • Revision ID: james.westby@ubuntu.com-20080622211713-inlv5k4eifxckelr
ImportĀ upstreamĀ versionĀ 1.7.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- coding: utf-8 -*-
2
 
"""
3
 
    werkzeug.utils
4
 
    ~~~~~~~~~~~~~~
5
 
 
6
 
    This module implements various utilities for WSGI applications.  Most of
7
 
    them are used by the request and response wrappers but especially for
8
 
    middleware development it makes sense to use them without the wrappers.
9
 
 
10
 
    :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
11
 
    :license: BSD, see LICENSE for more details.
12
 
"""
13
 
import re
14
 
import os
15
 
import sys
16
 
import urllib
17
 
import urlparse
18
 
import posixpath
19
 
import mimetypes
20
 
from zlib import adler32
21
 
from time import time, mktime
22
 
from datetime import datetime, timedelta
23
 
 
24
 
from werkzeug._internal import _patch_wrapper, _decode_unicode, \
25
 
     _empty_stream, _iter_modules, _ExtendedCookie, _ExtendedMorsel, \
26
 
     _DictAccessorProperty, _dump_date, _parse_signature, _missing
27
 
 
28
 
 
29
 
_format_re = re.compile(r'\$(?:(%s)|\{(%s)\})' % (('[a-zA-Z_][a-zA-Z0-9_]*',) * 2))
30
 
_entity_re = re.compile(r'&([^;]+);')
31
 
_filename_ascii_strip_re = re.compile(r'[^A-Za-z0-9_.-]')
32
 
_windows_device_files = ('CON', 'AUX', 'COM1', 'COM2', 'COM3', 'COM4', 'LPT1',
33
 
                         'LPT2', 'LPT3', 'PRN', 'NUL')
34
 
 
35
 
 
36
 
class FileStorage(object):
37
 
    """The :class:`FileStorage` class is a thin wrapper over incoming files.
38
 
    It is used by the request object to represent uploaded files.  All the
39
 
    attributes of the wrapper stream are proxied by the file storage so
40
 
    it's possible to do ``storage.read()`` instead of the long form
41
 
    ``storage.stream.read()``.
42
 
    """
43
 
 
44
 
    def __init__(self, stream=None, filename=None, name=None,
45
 
                 content_type='application/octet-stream', content_length=-1):
46
 
        self.name = name
47
 
        self.stream = stream or _empty_stream
48
 
        self.filename = filename or getattr(stream, 'name', None)
49
 
        self.content_type = content_type
50
 
        self.content_length = content_length
51
 
 
52
 
    def save(self, dst, buffer_size=16384):
53
 
        """Save the file to a destination path or file object.  If the
54
 
        destination is a file object you have to close it yourself after the
55
 
        call.  The buffer size is the number of bytes held in memory during
56
 
        the copy process.  It defaults to 16KB.
57
 
 
58
 
        For secure file saving also have a look at :func:`secure_filename`.
59
 
 
60
 
        :param dst: a filename or open file object the uploaded file
61
 
                    is saved to.
62
 
        :param buffer_size: the size of the buffer.  This works the same as
63
 
                            the `length` parameter of
64
 
                            :func:`shutil.copyfileobj`.
65
 
        """
66
 
        from shutil import copyfileobj
67
 
        close_dst = False
68
 
        if isinstance(dst, basestring):
69
 
            dst = file(dst, 'wb')
70
 
            close_dst = True
71
 
        try:
72
 
            copyfileobj(self.stream, dst, buffer_size)
73
 
        finally:
74
 
            if close_dst:
75
 
                dst.close()
76
 
 
77
 
    def close(self):
78
 
        """Close the underlaying file if possible."""
79
 
        try:
80
 
            self.stream.close()
81
 
        except:
82
 
            pass
83
 
 
84
 
    def __nonzero__(self):
85
 
        return bool(self.filename)
86
 
 
87
 
    def __getattr__(self, name):
88
 
        return getattr(self.stream, name)
89
 
 
90
 
    def __iter__(self):
91
 
        return iter(self.readline, '')
92
 
 
93
 
    def __repr__(self):
94
 
        return '<%s: %r (%r)>' % (
95
 
            self.__class__.__name__,
96
 
            self.filename,
97
 
            self.content_type
98
 
        )
99
 
 
100
 
 
101
 
class SharedDataMiddleware(object):
102
 
    """A WSGI middleware that provides static content for development
103
 
    environments or simple server setups. Usage is quite simple::
104
 
 
105
 
        import os
106
 
        from werkzeug import SharedDataMiddleware
107
 
 
108
 
        app = SharedDataMiddleware(app, {
109
 
            '/shared': os.path.join(os.path.dirname(__file__), 'shared')
110
 
        })
111
 
 
112
 
    The contents of the folder ``./shared`` will now be available on
113
 
    ``http://example.com/shared/``.  This is pretty useful during development
114
 
    because a standalone media server is not required.  One can also mount
115
 
    files on the root folder and still continue to use the application because
116
 
    the shared data middleware forwards all unhandled requests to the
117
 
    application, even if the requests are below one of the shared folders.
118
 
 
119
 
    If `pkg_resources` is available you can also tell the middleware to serve
120
 
    files from package data::
121
 
 
122
 
        app = SharedDataMiddleware(app, {
123
 
            '/shared': ('myapplication', 'shared_files')
124
 
        })
125
 
 
126
 
    This will then serve the ``shared_files`` folder in the `myapplication`
127
 
    Python package.
128
 
 
129
 
    The optional `disallow` parameter can be a list of :func:`~fnmatch.fnmatch`
130
 
    rules for files that are not accessible from the web.  If `cache` is set to
131
 
    `False` no caching headers are sent.
132
 
 
133
 
    Currently the middleware does not support non ASCII filenames.  If the
134
 
    encoding on the file system happens to be the encoding of the URI it may
135
 
    work but this could also be by accident.  We strongly suggest using ASCII
136
 
    only file names for static files.
137
 
 
138
 
    .. versionchanged:: 0.5
139
 
       The cache timeout is configurable now.
140
 
 
141
 
    :param app: the application to wrap.  If you don't want to wrap an
142
 
                application you can pass it :exc:`NotFound`.
143
 
    :param exports: a dict of exported files and folders.
144
 
    :param diallow: a list of :func:`~fnmatch.fnmatch` rules.
145
 
    :param cache: enable or disable caching headers.
146
 
    :Param cache_timeout: the cache timeout in seconds for the headers.
147
 
    """
148
 
 
149
 
    def __init__(self, app, exports, disallow=None, cache=True,
150
 
                 cache_timeout=60 * 60 * 12):
151
 
        self.app = app
152
 
        self.exports = {}
153
 
        self.cache = cache
154
 
        self.cache_timeout = cache_timeout
155
 
        for key, value in exports.iteritems():
156
 
            if isinstance(value, tuple):
157
 
                loader = self.get_package_loader(*value)
158
 
            elif isinstance(value, basestring):
159
 
                if os.path.isfile(value):
160
 
                    loader = self.get_file_loader(value)
161
 
                else:
162
 
                    loader = self.get_directory_loader(value)
163
 
            else:
164
 
                raise TypeError('unknown def %r' % value)
165
 
            self.exports[key] = loader
166
 
        if disallow is not None:
167
 
            from fnmatch import fnmatch
168
 
            self.is_allowed = lambda x: not fnmatch(x, disallow)
169
 
 
170
 
    def is_allowed(self, filename):
171
 
        """Subclasses can override this method to disallow the access to
172
 
        certain files.  However by providing `disallow` in the constructor
173
 
        this method is overwritten.
174
 
        """
175
 
        return True
176
 
 
177
 
    def _opener(self, filename):
178
 
        return lambda: (
179
 
            open(filename, 'rb'),
180
 
            datetime.utcfromtimestamp(os.path.getmtime(filename)),
181
 
            int(os.path.getsize(filename))
182
 
        )
183
 
 
184
 
    def get_file_loader(self, filename):
185
 
        return lambda x: (os.path.basename(filename), self._opener(filename))
186
 
 
187
 
    def get_package_loader(self, package, package_path):
188
 
        from pkg_resources import DefaultProvider, ResourceManager, \
189
 
             get_provider
190
 
        loadtime = datetime.utcnow()
191
 
        provider = get_provider(package)
192
 
        manager = ResourceManager()
193
 
        filesystem_bound = isinstance(provider, DefaultProvider)
194
 
        def loader(path):
195
 
            path = posixpath.join(package_path, path)
196
 
            if path is None or not provider.has_resource(path):
197
 
                return None, None
198
 
            basename = posixpath.basename(path)
199
 
            if filesystem_bound:
200
 
                return basename, self._opener(
201
 
                    provider.get_resource_filename(manager, path))
202
 
            return basename, lambda: (
203
 
                provider.get_resource_stream(manager, path),
204
 
                loadtime,
205
 
                0
206
 
            )
207
 
        return loader
208
 
 
209
 
    def get_directory_loader(self, directory):
210
 
        def loader(path):
211
 
            if path is not None:
212
 
                path = os.path.join(directory, path)
213
 
            else:
214
 
                path = directory
215
 
            if os.path.isfile(path):
216
 
                return os.path.basename(path), self._opener(path)
217
 
            return None, None
218
 
        return loader
219
 
 
220
 
    def generate_etag(self, mtime, file_size, real_filename):
221
 
        return 'wzsdm-%d-%s-%s' % (
222
 
            mktime(mtime.timetuple()),
223
 
            file_size,
224
 
            adler32(real_filename) & 0xffffffff
225
 
        )
226
 
 
227
 
    def __call__(self, environ, start_response):
228
 
        # sanitize the path for non unix systems
229
 
        cleaned_path = environ.get('PATH_INFO', '').strip('/')
230
 
        for sep in os.sep, os.altsep:
231
 
            if sep and sep != '/':
232
 
                cleaned_path = cleaned_path.replace(sep, '/')
233
 
        path = '/'.join([''] + [x for x in cleaned_path.split('/')
234
 
                                if x and x != '..'])
235
 
        file_loader = None
236
 
        for search_path, loader in self.exports.iteritems():
237
 
            if search_path == path:
238
 
                real_filename, file_loader = loader(None)
239
 
                if file_loader is not None:
240
 
                    break
241
 
            if not search_path.endswith('/'):
242
 
                search_path += '/'
243
 
            if path.startswith(search_path):
244
 
                real_filename, file_loader = loader(path[len(search_path):])
245
 
                if file_loader is not None:
246
 
                    break
247
 
        if file_loader is None or not self.is_allowed(real_filename):
248
 
            return self.app(environ, start_response)
249
 
 
250
 
        guessed_type = mimetypes.guess_type(real_filename)
251
 
        mime_type = guessed_type[0] or 'text/plain'
252
 
        f, mtime, file_size = file_loader()
253
 
 
254
 
        headers = [('Date', http_date())]
255
 
        if self.cache:
256
 
            timeout = self.cache_timeout
257
 
            etag = self.generate_etag(mtime, file_size, real_filename)
258
 
            headers += [
259
 
                ('Etag', '"%s"' % etag),
260
 
                ('Cache-Control', 'max-age=%d, public' % timeout)
261
 
            ]
262
 
            if not is_resource_modified(environ, etag, last_modified=mtime):
263
 
                f.close()
264
 
                start_response('304 Not Modified', headers)
265
 
                return []
266
 
            headers.append(('Expires', http_date(time() + timeout)))
267
 
        else:
268
 
            headers.append(('Cache-Control', 'public'))
269
 
 
270
 
        headers.extend((
271
 
            ('Content-Type', mime_type),
272
 
            ('Content-Length', str(file_size)),
273
 
            ('Last-Modified', http_date(mtime))
274
 
        ))
275
 
        start_response('200 OK', headers)
276
 
        return wrap_file(environ, f)
277
 
 
278
 
 
279
 
class DispatcherMiddleware(object):
280
 
    """Allows one to mount middlewares or application in a WSGI application.
281
 
    This is useful if you want to combine multiple WSGI applications::
282
 
 
283
 
        app = DispatcherMiddleware(app, {
284
 
            '/app2':        app2,
285
 
            '/app3':        app3
286
 
        })
287
 
    """
288
 
 
289
 
    def __init__(self, app, mounts=None):
290
 
        self.app = app
291
 
        self.mounts = mounts or {}
292
 
 
293
 
    def __call__(self, environ, start_response):
294
 
        script = environ.get('PATH_INFO', '')
295
 
        path_info = ''
296
 
        while '/' in script:
297
 
            if script in self.mounts:
298
 
                app = self.mounts[script]
299
 
                break
300
 
            items = script.split('/')
301
 
            script = '/'.join(items[:-1])
302
 
            path_info = '/%s%s' % (items[-1], path_info)
303
 
        else:
304
 
            app = self.mounts.get(script, self.app)
305
 
        original_script_name = environ.get('SCRIPT_NAME', '')
306
 
        environ['SCRIPT_NAME'] = original_script_name + script
307
 
        environ['PATH_INFO'] = path_info
308
 
        return app(environ, start_response)
309
 
 
310
 
 
311
 
class ClosingIterator(object):
312
 
    """The WSGI specification requires that all middlewares and gateways
313
 
    respect the `close` callback of an iterator.  Because it is useful to add
314
 
    another close action to a returned iterator and adding a custom iterator
315
 
    is a boring task this class can be used for that::
316
 
 
317
 
        return ClosingIterator(app(environ, start_response), [cleanup_session,
318
 
                                                              cleanup_locals])
319
 
 
320
 
    If there is just one close function it can be passed instead of the list.
321
 
 
322
 
    A closing iterator is not needed if the application uses response objects
323
 
    and finishes the processing if the response is started::
324
 
 
325
 
        try:
326
 
            return response(environ, start_response)
327
 
        finally:
328
 
            cleanup_session()
329
 
            cleanup_locals()
330
 
    """
331
 
 
332
 
    def __init__(self, iterable, callbacks=None):
333
 
        iterator = iter(iterable)
334
 
        self._next = iterator.next
335
 
        if callbacks is None:
336
 
            callbacks = []
337
 
        elif callable(callbacks):
338
 
            callbacks = [callbacks]
339
 
        else:
340
 
            callbacks = list(callbacks)
341
 
        iterable_close = getattr(iterator, 'close', None)
342
 
        if iterable_close:
343
 
            callbacks.insert(0, iterable_close)
344
 
        self._callbacks = callbacks
345
 
 
346
 
    def __iter__(self):
347
 
        return self
348
 
 
349
 
    def next(self):
350
 
        return self._next()
351
 
 
352
 
    def close(self):
353
 
        for callback in self._callbacks:
354
 
            callback()
355
 
 
356
 
 
357
 
class FileWrapper(object):
358
 
    """This class can be used to convert a :class:`file`-like object into
359
 
    an iterable.  It yields `buffer_size` blocks until the file is fully
360
 
    read.
361
 
 
362
 
    You should not use this class directly but rather use the
363
 
    :func:`wrap_file` function that uses the WSGI server's file wrapper
364
 
    support if it's available.
365
 
 
366
 
    .. versionadded:: 0.5
367
 
 
368
 
    :param file: a :class:`file`-like object with a :meth:`~file.read` method.
369
 
    :param buffer_size: number of bytes for one iteration.
370
 
    """
371
 
 
372
 
    def __init__(self, file, buffer_size=8192):
373
 
        self.file = file
374
 
        self.buffer_size = buffer_size
375
 
 
376
 
    def close(self):
377
 
        if hasattr(self.file, 'close'):
378
 
            self.file.close()
379
 
 
380
 
    def __iter__(self):
381
 
        return self
382
 
 
383
 
    def next(self):
384
 
        data = self.file.read(self.buffer_size)
385
 
        if data:
386
 
            return data
387
 
        raise StopIteration()
388
 
 
389
 
 
390
 
 
391
 
def make_line_iter(stream, limit=None, buffer_size=10 * 1024):
392
 
    """Savely iterates line-based over an input stream.  If the input stream
393
 
    is not a :class:`LimitedStream` the `limit` parameter is mandatory.
394
 
 
395
 
    This uses the stream's :meth:`~file.read` method internally as opposite
396
 
    to the :meth:`~file.readline` method that is unsafe and can only be used
397
 
    in violation of the WSGI specification.  The same problem applies to the
398
 
    `__iter__` function of the input stream which calls :meth:`~file.readline`
399
 
    without arguments.
400
 
 
401
 
    If you need line-by-line processing it's strongly recommended to iterate
402
 
    over the input stream using this helper function.
403
 
 
404
 
    :param stream: the stream to iterate over.
405
 
    :param limit: the limit in bytes for the stream.  (Usually
406
 
                  content length.  Not necessary if the `stream`
407
 
                  is a :class:`LimitedStream`.
408
 
    :param buffer_size: The optional buffer size.
409
 
    """
410
 
    if not isinstance(stream, LimitedStream):
411
 
        if limit is None:
412
 
            raise TypeError('stream not limited and no limit provided.')
413
 
        stream = LimitedStream(stream, limit)
414
 
    buffer = []
415
 
    while 1:
416
 
        if len(buffer) > 1:
417
 
            yield buffer.pop(0)
418
 
            continue
419
 
        chunks = stream.read(buffer_size).splitlines(True)
420
 
        first_chunk = buffer and buffer[0] or ''
421
 
        if chunks:
422
 
            first_chunk += chunks.pop(0)
423
 
        buffer = chunks
424
 
        if not first_chunk:
425
 
            return
426
 
        yield first_chunk
427
 
 
428
 
 
429
 
class LimitedStream(object):
430
 
    """Wraps a stream so that it doesn't read more than n bytes.  If the
431
 
    stream is exhausted and the caller tries to get more bytes from it
432
 
    :func:`on_exhausted` is called which by default returns an empty
433
 
    string or raises :exc:`~werkzeug.exceptions.BadRequest` if silent
434
 
    is set to `False`.  The return value of that function is forwarded
435
 
    to the reader function.  So if it returns an empty string
436
 
    :meth:`read` will return an empty string as well.
437
 
 
438
 
    The limit however must never be higher than what the stream can
439
 
    output.  Otherwise :meth:`readlines` will try to read past the
440
 
    limit.
441
 
 
442
 
    The `silent` parameter has no effect if :meth:`is_exhausted` is
443
 
    overriden by a subclass.
444
 
 
445
 
    .. admonition:: Note on WSGI compliance
446
 
 
447
 
       calls to :meth:`readline` and :meth:`readlines` are not
448
 
       WSGI compliant because it passes a size argument to the
449
 
       readline methods.  Unfortunately the WSGI PEP is not safely
450
 
       implementable without a size argument to :meth:`readline`
451
 
       because there is no EOF marker in the stream.  As a result
452
 
       of that the use of :meth:`readline` is discouraged.
453
 
 
454
 
       For the same reason iterating over the :class:`LimitedStream`
455
 
       is not portable.  It internally calls :meth:`readline`.
456
 
 
457
 
       We strongly suggest using :meth:`read` only or using the
458
 
       :func:`make_line_iter` which savely iterates line-based
459
 
       over a WSGI input stream.
460
 
 
461
 
    :param stream: the stream to wrap.
462
 
    :param limit: the limit for the stream, must not be longer than
463
 
                  what the string can provide if the stream does not
464
 
                  end with `EOF` (like `wsgi.input`)
465
 
    :param silent: If set to `True` the stream will allow reading
466
 
                   past the limit and will return an empty string.
467
 
    """
468
 
 
469
 
    def __init__(self, stream, limit, silent=True):
470
 
        self._stream = stream
471
 
        self._pos = 0
472
 
        self.limit = limit
473
 
        self.silent = silent
474
 
 
475
 
    def __iter__(self):
476
 
        return self
477
 
 
478
 
    @property
479
 
    def is_exhausted(self):
480
 
        """If the stream is exhausted this attribute is `True`."""
481
 
        return self._pos >= self.limit
482
 
 
483
 
    def on_exhausted(self):
484
 
        """This is called when the stream tries to read past the limit.
485
 
        The return value of this function is returned from the reading
486
 
        function.
487
 
 
488
 
        Per default this raises a :exc:`~werkzeug.exceptions.BadRequest`.
489
 
        """
490
 
        if self.silent:
491
 
            return ''
492
 
        raise BadRequest('input stream exhausted')
493
 
 
494
 
    def exhaust(self, chunk_size=1024 * 16):
495
 
        """Exhaust the stream.  This consumes all the data left until the
496
 
        limit is reached.
497
 
 
498
 
        :param chunk_size: the size for a chunk.  It will read the chunk
499
 
                           until the stream is exhausted and throw away
500
 
                           the results.
501
 
        """
502
 
        to_read = self.limit - self._pos
503
 
        chunk = chunk_size
504
 
        while to_read > 0:
505
 
            chunk = min(to_read, chunk)
506
 
            self.read(chunk)
507
 
            to_read -= chunk
508
 
 
509
 
    def read(self, size=None):
510
 
        """Read `size` bytes or if size is not provided everything is read.
511
 
 
512
 
        :param size: the number of bytes read.
513
 
        """
514
 
        if self._pos >= self.limit:
515
 
            return self.on_exhausted()
516
 
        if size is None:
517
 
            size = self.limit
518
 
        read = self._stream.read(min(self.limit - self._pos, size))
519
 
        self._pos += len(read)
520
 
        return read
521
 
 
522
 
    def readline(self, size=None):
523
 
        """Reads one line from the stream."""
524
 
        if self._pos >= self.limit:
525
 
            return self.on_exhausted()
526
 
        if size is None:
527
 
            size = self.limit - self._pos
528
 
        else:
529
 
            size = min(size, self.limit - self._pos)
530
 
        line = self._stream.readline(size)
531
 
        self._pos += len(line)
532
 
        return line
533
 
 
534
 
    def readlines(self, size=None):
535
 
        """Reads a file into a list of strings.  It calls :meth:`readline`
536
 
        until the file is read to the end.  It does support the optional
537
 
        `size` argument if the underlaying stream supports it for
538
 
        `readline`.
539
 
        """
540
 
        last_pos = self._pos
541
 
        result = []
542
 
        if size is not None:
543
 
            end = min(self.limit, last_pos + size)
544
 
        else:
545
 
            end = self.limit
546
 
        while 1:
547
 
            if size is not None:
548
 
                size -= last_pos - self._pos
549
 
            if self._pos >= end:
550
 
                break
551
 
            result.append(self.readline(size))
552
 
            if size is not None:
553
 
                last_pos = self._pos
554
 
        return result
555
 
 
556
 
    def next(self):
557
 
        line = self.readline()
558
 
        if line is None:
559
 
            raise StopIteration()
560
 
        return line
561
 
 
562
 
 
563
 
class Href(object):
564
 
    """Implements a callable that constructs URLs with the given base. The
565
 
    function can be called with any number of positional and keyword
566
 
    arguments which than are used to assemble the URL.  Works with URLs
567
 
    and posix paths.
568
 
 
569
 
    Positional arguments are appended as individual segments to
570
 
    the path of the URL:
571
 
 
572
 
    >>> href = Href('/foo')
573
 
    >>> href('bar', 23)
574
 
    '/foo/bar/23'
575
 
    >>> href('foo', bar=23)
576
 
    '/foo/foo?bar=23'
577
 
 
578
 
    If any of the arguments (positional or keyword) evaluates to `None` it
579
 
    will be skipped.  If no keyword arguments are given the last argument
580
 
    can be a :class:`dict` or :class:`MultiDict` (or any other dict subclass),
581
 
    otherwise the keyword arguments are used for the query parameters, cutting
582
 
    off the first trailing underscore of the parameter name:
583
 
 
584
 
    >>> href(is_=42)
585
 
    '/foo?is=42'
586
 
    >>> href({'foo': 'bar'})
587
 
    '/foo?foo=bar'
588
 
 
589
 
    Combining of both methods is not allowed:
590
 
 
591
 
    >>> href({'foo': 'bar'}, bar=42)
592
 
    Traceback (most recent call last):
593
 
      ...
594
 
    TypeError: keyword arguments and query-dicts can't be combined
595
 
 
596
 
    Accessing attributes on the href object creates a new href object with
597
 
    the attribute name as prefix:
598
 
 
599
 
    >>> bar_href = href.bar
600
 
    >>> bar_href("blub")
601
 
    '/foo/bar/blub'
602
 
 
603
 
    If `sort` is set to `True` the items are sorted by `key` or the default
604
 
    sorting algorithm:
605
 
 
606
 
    >>> href = Href("/", sort=True)
607
 
    >>> href(a=1, b=2, c=3)
608
 
    '/?a=1&b=2&c=3'
609
 
 
610
 
    .. versionadded:: 0.5
611
 
        `sort` and `key` were added.
612
 
    """
613
 
 
614
 
    def __init__(self, base='./', charset='utf-8', sort=False, key=None):
615
 
        if not base:
616
 
            base = './'
617
 
        self.base = base
618
 
        self.charset = charset
619
 
        self.sort = sort
620
 
        self.key = key
621
 
 
622
 
    def __getattr__(self, name):
623
 
        if name[:2] == '__':
624
 
            raise AttributeError(name)
625
 
        base = self.base
626
 
        if base[-1:] != '/':
627
 
            base += '/'
628
 
        return Href(urlparse.urljoin(base, name), self.charset, self.sort,
629
 
                    self.key)
630
 
 
631
 
    def __call__(self, *path, **query):
632
 
        if path and isinstance(path[-1], dict):
633
 
            if query:
634
 
                raise TypeError('keyword arguments and query-dicts '
635
 
                                'can\'t be combined')
636
 
            query, path = path[-1], path[:-1]
637
 
        elif query:
638
 
            query = dict([(k.endswith('_') and k[:-1] or k, v)
639
 
                          for k, v in query.items()])
640
 
        path = '/'.join([url_quote(x, self.charset) for x in path
641
 
                         if x is not None]).lstrip('/')
642
 
        rv = self.base
643
 
        if path:
644
 
            if not rv.endswith('/'):
645
 
                rv += '/'
646
 
            rv = urlparse.urljoin(rv, path)
647
 
        if query:
648
 
            rv += '?' + url_encode(query, self.charset, sort=self.sort,
649
 
                                   key=self.key)
650
 
        return str(rv)
651
 
 
652
 
 
653
 
class cached_property(object):
654
 
    """A decorator that converts a function into a lazy property.  The
655
 
    function wrapped is called the first time to retrieve the result
656
 
    and than that calculated result is used the next time you access
657
 
    the value::
658
 
 
659
 
        class Foo(object):
660
 
 
661
 
            @cached_property
662
 
            def foo(self):
663
 
                # calculate something important here
664
 
                return 42
665
 
 
666
 
    .. versionchanged:: 0.5
667
 
       cached properties are now optionally writeable.
668
 
    """
669
 
 
670
 
    def __init__(self, func, name=None, doc=None, writeable=False):
671
 
        self.func = func
672
 
        self.writeable = writeable
673
 
        self.__name__ = name or func.__name__
674
 
        self.__doc__ = doc or func.__doc__
675
 
 
676
 
    def __get__(self, obj, type=None):
677
 
        if obj is None:
678
 
            return self
679
 
        value = obj.__dict__.get(self.__name__, _missing)
680
 
        if value is _missing:
681
 
            value = self.func(obj)
682
 
            obj.__dict__[self.__name__] = value
683
 
        return value
684
 
 
685
 
    def __set__(self, obj, value):
686
 
        if not self.writeable:
687
 
            raise TypeError('read only attribute')
688
 
        obj.__dict__[self.__name__] = value
689
 
 
690
 
 
691
 
class environ_property(_DictAccessorProperty):
692
 
    """Maps request attributes to environment variables. This works not only
693
 
    for the Werzeug request object, but also any other class with an
694
 
    environ attribute:
695
 
 
696
 
    >>> class Test(object):
697
 
    ...     environ = {'key': 'value'}
698
 
    ...     test = environ_property('key')
699
 
    >>> var = Test()
700
 
    >>> var.test
701
 
    'value'
702
 
 
703
 
    If you pass it a second value it's used as default if the key does not
704
 
    exist, the third one can be a converter that takes a value and converts
705
 
    it.  If it raises :exc:`ValueError` or :exc:`TypeError` the default value
706
 
    is used. If no default value is provided `None` is used.
707
 
 
708
 
    Per default the property is read only.  You have to explicitly enable it
709
 
    by passing ``read_only=False`` to the constructor.
710
 
    """
711
 
 
712
 
    read_only = True
713
 
 
714
 
    def lookup(self, obj):
715
 
        return obj.environ
716
 
 
717
 
 
718
 
class header_property(_DictAccessorProperty):
719
 
    """Like `environ_property` but for headers."""
720
 
 
721
 
    def lookup(self, obj):
722
 
        return obj.headers
723
 
 
724
 
 
725
 
class HTMLBuilder(object):
726
 
    """Helper object for HTML generation.
727
 
 
728
 
    Per default there are two instances of that class.  The `html` one, and
729
 
    the `xhtml` one for those two dialects.  The class uses keyword parameters
730
 
    and positional parameters to generate small snippets of HTML.
731
 
 
732
 
    Keyword parameters are converted to XML/SGML attributes, positional
733
 
    arguments are used as children.  Because Python accepts positional
734
 
    arguments before keyword arguments it's a good idea to use a list with the
735
 
    star-syntax for some children:
736
 
 
737
 
    >>> html.p(class_='foo', *[html.a('foo', href='foo.html'), ' ',
738
 
    ...                        html.a('bar', href='bar.html')])
739
 
    u'<p class="foo"><a href="foo.html">foo</a> <a href="bar.html">bar</a></p>'
740
 
 
741
 
    This class works around some browser limitations and can not be used for
742
 
    arbitrary SGML/XML generation.  For that purpose lxml and similar
743
 
    libraries exist.
744
 
 
745
 
    Calling the builder escapes the string passed:
746
 
 
747
 
    >>> html.p(html("<foo>"))
748
 
    u'<p>&lt;foo&gt;</p>'
749
 
    """
750
 
 
751
 
    from htmlentitydefs import name2codepoint
752
 
    _entity_re = re.compile(r'&([^;]+);')
753
 
    _entities = name2codepoint.copy()
754
 
    _entities['apos'] = 39
755
 
    _empty_elements = set([
756
 
        'area', 'base', 'basefont', 'br', 'col', 'frame', 'hr', 'img',
757
 
        'input', 'isindex', 'link', 'meta', 'param'
758
 
    ])
759
 
    _boolean_attributes = set([
760
 
        'selected', 'checked', 'compact', 'declare', 'defer', 'disabled',
761
 
        'ismap', 'multiple', 'nohref', 'noresize', 'noshade', 'nowrap'
762
 
    ])
763
 
    _plaintext_elements = set(['textarea'])
764
 
    _c_like_cdata = set(['script', 'style'])
765
 
    del name2codepoint
766
 
 
767
 
    def __init__(self, dialect):
768
 
        self._dialect = dialect
769
 
 
770
 
    def __call__(self, s):
771
 
        return escape(s)
772
 
 
773
 
    def __getattr__(self, tag):
774
 
        if tag[:2] == '__':
775
 
            raise AttributeError(tag)
776
 
        def proxy(*children, **arguments):
777
 
            buffer = ['<' + tag]
778
 
            write = buffer.append
779
 
            for key, value in arguments.iteritems():
780
 
                if value is None:
781
 
                    continue
782
 
                if key.endswith('_'):
783
 
                    key = key[:-1]
784
 
                if key in self._boolean_attributes:
785
 
                    if not value:
786
 
                        continue
787
 
                    value = self._dialect == 'xhtml' and '="%s"' % key or ''
788
 
                else:
789
 
                    value = '="%s"' % escape(value, True)
790
 
                write(' ' + key + value)
791
 
            if not children and tag in self._empty_elements:
792
 
                write(self._dialect == 'xhtml' and ' />' or '>')
793
 
                return ''.join(buffer)
794
 
            write('>')
795
 
            children_as_string = ''.join(unicode(x) for x in children
796
 
                                         if x is not None)
797
 
            if children_as_string:
798
 
                if tag in self._plaintext_elements:
799
 
                    children_as_string = escape(children_as_string)
800
 
                elif tag in self._c_like_cdata and self._dialect == 'xhtml':
801
 
                    children_as_string = '/*<![CDATA[*/%s/*]]>*/' % \
802
 
                                         children_as_string
803
 
            buffer.extend((children_as_string, '</%s>' % tag))
804
 
            return ''.join(buffer)
805
 
        return proxy
806
 
 
807
 
    def __repr__(self):
808
 
        return '<%s for %r>' % (
809
 
            self.__class__.__name__,
810
 
            self._dialect
811
 
        )
812
 
 
813
 
 
814
 
html = HTMLBuilder('html')
815
 
xhtml = HTMLBuilder('xhtml')
816
 
 
817
 
 
818
 
def parse_form_data(environ, stream_factory=None, charset='utf-8',
819
 
                    errors='ignore', max_form_memory_size=None,
820
 
                    max_content_length=None, cls=None,
821
 
                    silent=True):
822
 
    """Parse the form data in the environ and return it as tuple in the form
823
 
    ``(stream, form, files)``.  You should only call this method if the
824
 
    transport method is `POST` or `PUT`.
825
 
 
826
 
    If the mimetype of the data transmitted is `multipart/form-data` the
827
 
    files multidict will be filled with `FileStorage` objects.  If the
828
 
    mimetype is unknown the input stream is wrapped and returned as first
829
 
    argument, else the stream is empty.
830
 
 
831
 
    This function does not raise exceptions, even if the input data is
832
 
    malformed.
833
 
 
834
 
    Have a look at :ref:`dealing-with-request-data` for more details.
835
 
 
836
 
    .. versionadded:: 0.5
837
 
       The `max_form_memory_size`, `max_content_length` and
838
 
       `cls` parameters were added.
839
 
 
840
 
    .. versionadded:: 0.5.1
841
 
       The optional `silent` flag was added.
842
 
 
843
 
    :param environ: the WSGI environment to be used for parsing.
844
 
    :param stream_factory: An optional callable that returns a new read and
845
 
                           writeable file descriptor.  This callable works
846
 
                           the same as :meth:`~BaseResponse._get_file_stream`.
847
 
    :param charset: The character set for URL and url encoded form data.
848
 
    :param errors: The encoding error behavior.
849
 
    :param max_form_memory_size: the maximum number of bytes to be accepted for
850
 
                           in-memory stored form data.  If the data
851
 
                           exceeds the value specified an
852
 
                           :exc:`~exceptions.RequestURITooLarge`
853
 
                           exception is raised.
854
 
    :param max_content_length: If this is provided and the transmitted data
855
 
                               is longer than this value an
856
 
                               :exc:`~exceptions.RequestEntityTooLarge`
857
 
                               exception is raised.
858
 
    :param cls: an optional dict class to use.  If this is not specified
859
 
                       or `None` the default :class:`MultiDict` is used.
860
 
    :param silent: If set to False parsing errors will not be catched.
861
 
    :return: A tuple in the form ``(stream, form, files)``.
862
 
    """
863
 
    content_type, extra = parse_options_header(environ.get('CONTENT_TYPE', ''))
864
 
    try:
865
 
        content_length = int(environ['CONTENT_LENGTH'])
866
 
    except (KeyError, ValueError):
867
 
        content_length = 0
868
 
 
869
 
    if cls is None:
870
 
        cls = MultiDict
871
 
 
872
 
    if max_content_length is not None and content_length > max_content_length:
873
 
        raise RequestEntityTooLarge()
874
 
 
875
 
    stream = _empty_stream
876
 
    files = ()
877
 
 
878
 
    if content_type == 'multipart/form-data':
879
 
        try:
880
 
            form, files = parse_multipart(environ['wsgi.input'],
881
 
                                          extra.get('boundary'),
882
 
                                          content_length, stream_factory,
883
 
                                          charset, errors,
884
 
                                          max_form_memory_size=max_form_memory_size)
885
 
        except ValueError, e:
886
 
            if not silent:
887
 
                raise
888
 
            form = cls()
889
 
        else:
890
 
            form = cls(form)
891
 
    elif content_type == 'application/x-www-form-urlencoded' or \
892
 
         content_type == 'application/x-url-encoded':
893
 
        if max_form_memory_size is not None and \
894
 
           content_length > max_form_memory_size:
895
 
            raise RequestEntityTooLarge()
896
 
        form = url_decode(environ['wsgi.input'].read(content_length),
897
 
                          charset, errors=errors, cls=cls)
898
 
    else:
899
 
        form = cls()
900
 
        stream = LimitedStream(environ['wsgi.input'], content_length)
901
 
 
902
 
    return stream, form, cls(files)
903
 
 
904
 
 
905
 
def get_content_type(mimetype, charset):
906
 
    """Return the full content type string with charset for a mimetype.
907
 
 
908
 
    If the mimetype represents text the charset will be appended as charset
909
 
    parameter, otherwise the mimetype is returned unchanged.
910
 
 
911
 
    :param mimetype: the mimetype to be used as content type.
912
 
    :param charset: the charset to be appended in case it was a text mimetype.
913
 
    :return: the content type.
914
 
    """
915
 
    if mimetype.startswith('text/') or \
916
 
       mimetype == 'application/xml' or \
917
 
       (mimetype.startswith('application/') and
918
 
        mimetype.endswith('+xml')):
919
 
        mimetype += '; charset=' + charset
920
 
    return mimetype
921
 
 
922
 
 
923
 
def format_string(string, context):
924
 
    """String-template format a string:
925
 
 
926
 
    >>> format_string('$foo and ${foo}s', dict(foo=42))
927
 
    '42 and 42s'
928
 
 
929
 
    This does not do any attribute lookup etc.  For more advanced string
930
 
    formattings have a look at the `werkzeug.template` module.
931
 
 
932
 
    :param string: the format string.
933
 
    :param context: a dict with the variables to insert.
934
 
    """
935
 
    def lookup_arg(match):
936
 
        x = context[match.group(1) or match.group(2)]
937
 
        if not isinstance(x, basestring):
938
 
            x = type(string)(x)
939
 
        return x
940
 
    return _format_re.sub(lookup_arg, string)
941
 
 
942
 
 
943
 
def url_decode(s, charset='utf-8', decode_keys=False, include_empty=True,
944
 
               errors='ignore', separator='&', cls=None):
945
 
    """Parse a querystring and return it as :class:`MultiDict`.  Per default
946
 
    only values are decoded into unicode strings.  If `decode_keys` is set to
947
 
    `True` the same will happen for keys.
948
 
 
949
 
    Per default a missing value for a key will default to an empty key.  If
950
 
    you don't want that behavior you can set `include_empty` to `False`.
951
 
 
952
 
    Per default encoding errors are ignored.  If you want a different behavior
953
 
    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
954
 
    `HTTPUnicodeError` is raised.
955
 
 
956
 
    .. versionchanged:: 0.5
957
 
       In previous versions ";" and "&" could be used for url decoding.
958
 
       This changed in 0.5 where only "&" is supported.  If you want to
959
 
       use ";" instead a different `separator` can be provided.
960
 
 
961
 
       The `cls` parameter was added.
962
 
 
963
 
    :param s: a string with the query string to decode.
964
 
    :param charset: the charset of the query string.
965
 
    :param decode_keys: set to `True` if you want the keys to be decoded
966
 
                        as well.
967
 
    :param include_empty: Set to `False` if you don't want empty values to
968
 
                          appear in the dict.
969
 
    :param errors: the decoding error behavior.
970
 
    :param separator: the pair separator to be used, defaults to ``&``
971
 
    :param cls: an optional dict class to use.  If this is not specified
972
 
                       or `None` the default :class:`MultiDict` is used.
973
 
    """
974
 
    if cls is None:
975
 
        cls = MultiDict
976
 
    result = []
977
 
    for pair in str(s).split(separator):
978
 
        if not pair:
979
 
            continue
980
 
        if '=' in pair:
981
 
            key, value = pair.split('=', 1)
982
 
        else:
983
 
            key = pair
984
 
            value = ''
985
 
        key = urllib.unquote_plus(key)
986
 
        if decode_keys:
987
 
            key = _decode_unicode(key, charset, errors)
988
 
        result.append((key, url_unquote_plus(value, charset, errors)))
989
 
    return cls(result)
990
 
 
991
 
 
992
 
def url_encode(obj, charset='utf-8', encode_keys=False, sort=False, key=None,
993
 
               separator='&'):
994
 
    """URL encode a dict/`MultiDict`.  If a value is `None` it will not appear
995
 
    in the result string.  Per default only values are encoded into the target
996
 
    charset strings.  If `encode_keys` is set to ``True`` unicode keys are
997
 
    supported too.
998
 
 
999
 
    If `sort` is set to `True` the items are sorted by `key` or the default
1000
 
    sorting algorithm.
1001
 
 
1002
 
    .. versionadded:: 0.5
1003
 
        `sort`, `key`, and `separator` were added.
1004
 
 
1005
 
    :param obj: the object to encode into a query string.
1006
 
    :param charset: the charset of the query string.
1007
 
    :param encode_keys: set to `True` if you have unicode keys.
1008
 
    :param sort: set to `True` if you want parameters to be sorted by `key`.
1009
 
    :param separator: the separator to be used for the pairs.
1010
 
    :param key: an optional function to be used for sorting.  For more details
1011
 
                check out the :func:`sorted` documentation.
1012
 
    """
1013
 
    if isinstance(obj, MultiDict):
1014
 
        items = obj.lists()
1015
 
    elif isinstance(obj, dict):
1016
 
        items = []
1017
 
        for k, v in obj.iteritems():
1018
 
            if not isinstance(v, (tuple, list)):
1019
 
                v = [v]
1020
 
            items.append((k, v))
1021
 
    else:
1022
 
        items = obj or ()
1023
 
    if sort:
1024
 
        items.sort(key=key)
1025
 
    tmp = []
1026
 
    for key, values in items:
1027
 
        if encode_keys and isinstance(key, unicode):
1028
 
            key = key.encode(charset)
1029
 
        else:
1030
 
            key = str(key)
1031
 
        for value in values:
1032
 
            if value is None:
1033
 
                continue
1034
 
            elif isinstance(value, unicode):
1035
 
                value = value.encode(charset)
1036
 
            else:
1037
 
                value = str(value)
1038
 
            tmp.append('%s=%s' % (urllib.quote(key),
1039
 
                                  urllib.quote_plus(value)))
1040
 
    return separator.join(tmp)
1041
 
 
1042
 
 
1043
 
def url_quote(s, charset='utf-8', safe='/:'):
1044
 
    """URL encode a single string with a given encoding.
1045
 
 
1046
 
    :param s: the string to quote.
1047
 
    :param charset: the charset to be used.
1048
 
    :param safe: an optional sequence of safe characters.
1049
 
    """
1050
 
    if isinstance(s, unicode):
1051
 
        s = s.encode(charset)
1052
 
    elif not isinstance(s, str):
1053
 
        s = str(s)
1054
 
    return urllib.quote(s, safe=safe)
1055
 
 
1056
 
 
1057
 
def url_quote_plus(s, charset='utf-8', safe=''):
1058
 
    """URL encode a single string with the given encoding and convert
1059
 
    whitespace to "+".
1060
 
 
1061
 
    :param s: the string to quote.
1062
 
    :param charset: the charset to be used.
1063
 
    :param safe: an optional sequence of safe characters.
1064
 
    """
1065
 
    if isinstance(s, unicode):
1066
 
        s = s.encode(charset)
1067
 
    elif not isinstance(s, str):
1068
 
        s = str(s)
1069
 
    return urllib.quote_plus(s, safe=safe)
1070
 
 
1071
 
 
1072
 
def url_unquote(s, charset='utf-8', errors='ignore'):
1073
 
    """URL decode a single string with a given decoding.
1074
 
 
1075
 
    Per default encoding errors are ignored.  If you want a different behavior
1076
 
    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
1077
 
    `HTTPUnicodeError` is raised.
1078
 
 
1079
 
    :param s: the string to unquote.
1080
 
    :param charset: the charset to be used.
1081
 
    :param errors: the error handling for the charset decoding.
1082
 
    """
1083
 
    return _decode_unicode(urllib.unquote(s), charset, errors)
1084
 
 
1085
 
 
1086
 
def url_unquote_plus(s, charset='utf-8', errors='ignore'):
1087
 
    """URL decode a single string with the given decoding and decode
1088
 
    a "+" to whitespace.
1089
 
 
1090
 
    Per default encoding errors are ignored.  If you want a different behavior
1091
 
    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
1092
 
    `HTTPUnicodeError` is raised.
1093
 
 
1094
 
    :param s: the string to unquote.
1095
 
    :param charset: the charset to be used.
1096
 
    :param errors: the error handling for the charset decoding.
1097
 
    """
1098
 
    return _decode_unicode(urllib.unquote_plus(s), charset, errors)
1099
 
 
1100
 
 
1101
 
def url_fix(s, charset='utf-8'):
1102
 
    r"""Sometimes you get an URL by a user that just isn't a real URL because
1103
 
    it contains unsafe characters like ' ' and so on.  This function can fix
1104
 
    some of the problems in a similar way browsers handle data entered by the
1105
 
    user:
1106
 
 
1107
 
    >>> url_fix(u'http://de.wikipedia.org/wiki/Elf (Begriffskl\xe4rung)')
1108
 
    'http://de.wikipedia.org/wiki/Elf%20%28Begriffskl%C3%A4rung%29'
1109
 
 
1110
 
    :param s: the string with the URL to fix.
1111
 
    :param charset: The target charset for the URL if the url was given as
1112
 
                    unicode string.
1113
 
    """
1114
 
    if isinstance(s, unicode):
1115
 
        s = s.encode(charset, 'ignore')
1116
 
    scheme, netloc, path, qs, anchor = urlparse.urlsplit(s)
1117
 
    path = urllib.quote(path, '/%')
1118
 
    qs = urllib.quote_plus(qs, ':&=')
1119
 
    return urlparse.urlunsplit((scheme, netloc, path, qs, anchor))
1120
 
 
1121
 
 
1122
 
def secure_filename(filename):
1123
 
    r"""Pass it a filename and it will return a secure version of it.  This
1124
 
    filename can then savely be stored on a regular file system and passed
1125
 
    to :func:`os.path.join`.  The filename returned is an ASCII only string
1126
 
    for maximum portability.
1127
 
 
1128
 
    On windows system the function also makes sure that the file is not
1129
 
    named after one of the special device files.
1130
 
 
1131
 
    >>> secure_filename("My cool movie.mov")
1132
 
    'My_cool_movie.mov'
1133
 
    >>> secure_filename("../../../etc/passwd")
1134
 
    'etc_passwd'
1135
 
    >>> secure_filename(u'i contain cool \xfcml\xe4uts.txt')
1136
 
    'i_contain_cool_umlauts.txt'
1137
 
 
1138
 
    .. versionadded:: 0.5
1139
 
 
1140
 
    :param filename: the filename to secure
1141
 
    """
1142
 
    if isinstance(filename, unicode):
1143
 
        from unicodedata import normalize
1144
 
        filename = normalize('NFKD', filename).encode('ascii', 'ignore')
1145
 
    for sep in os.path.sep, os.path.altsep:
1146
 
        if sep:
1147
 
            filename = filename.replace(sep, ' ')
1148
 
    filename = str(_filename_ascii_strip_re.sub('', '_'.join(
1149
 
                   filename.split()))).strip('._')
1150
 
 
1151
 
    # on nt a couple of special files are present in each folder.  We
1152
 
    # have to ensure that the target file is not such a filename.  In
1153
 
    # this case we prepend an underline
1154
 
    if os.name == 'nt':
1155
 
        if filename.split('.')[0].upper() in _windows_device_files:
1156
 
            filename = '_' + filename
1157
 
 
1158
 
    return filename
1159
 
 
1160
 
 
1161
 
def escape(s, quote=False):
1162
 
    """Replace special characters "&", "<" and ">" to HTML-safe sequences.  If
1163
 
    the optional flag `quote` is `True`, the quotation mark character (") is
1164
 
    also translated.
1165
 
 
1166
 
    There is a special handling for `None` which escapes to an empty string.
1167
 
 
1168
 
    :param s: the string to escape.
1169
 
    :param quote: set to true to also escape double quotes.
1170
 
    """
1171
 
    if s is None:
1172
 
        return ''
1173
 
    elif hasattr(s, '__html__'):
1174
 
        return s.__html__()
1175
 
    elif not isinstance(s, basestring):
1176
 
        s = unicode(s)
1177
 
    s = s.replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;')
1178
 
    if quote:
1179
 
        s = s.replace('"', "&quot;")
1180
 
    return s
1181
 
 
1182
 
 
1183
 
def unescape(s):
1184
 
    """The reverse function of `escape`.  This unescapes all the HTML
1185
 
    entities, not only the XML entities inserted by `escape`.
1186
 
 
1187
 
    :param s: the string to unescape.
1188
 
    """
1189
 
    def handle_match(m):
1190
 
        name = m.group(1)
1191
 
        if name in HTMLBuilder._entities:
1192
 
            return unichr(HTMLBuilder._entities[name])
1193
 
        try:
1194
 
            if name[:2] in ('#x', '#X'):
1195
 
                return unichr(int(name[2:], 16))
1196
 
            elif name.startswith('#'):
1197
 
                return unichr(int(name[1:]))
1198
 
        except ValueError:
1199
 
            pass
1200
 
        return u''
1201
 
    return _entity_re.sub(handle_match, s)
1202
 
 
1203
 
 
1204
 
def get_host(environ):
1205
 
    """Return the real host for the given WSGI environment.  This takes care
1206
 
    of the `X-Forwarded-Host` header.
1207
 
 
1208
 
    :param environ: the WSGI environment to get the host of.
1209
 
    """
1210
 
    if 'HTTP_X_FORWARDED_HOST' in environ:
1211
 
        return environ['HTTP_X_FORWARDED_HOST']
1212
 
    elif 'HTTP_HOST' in environ:
1213
 
        return environ['HTTP_HOST']
1214
 
    result = environ['SERVER_NAME']
1215
 
    if (environ['wsgi.url_scheme'], environ['SERVER_PORT']) not \
1216
 
       in (('https', '443'), ('http', '80')):
1217
 
        result += ':' + environ['SERVER_PORT']
1218
 
    return result
1219
 
 
1220
 
 
1221
 
def get_current_url(environ, root_only=False, strip_querystring=False,
1222
 
                    host_only=False):
1223
 
    """A handy helper function that recreates the full URL for the current
1224
 
    request or parts of it.  Here an example:
1225
 
 
1226
 
    >>> env = create_environ("/?param=foo", "http://localhost/script")
1227
 
    >>> get_current_url(env)
1228
 
    'http://localhost/script/?param=foo'
1229
 
    >>> get_current_url(env, root_only=True)
1230
 
    'http://localhost/script/'
1231
 
    >>> get_current_url(env, host_only=True)
1232
 
    'http://localhost/'
1233
 
    >>> get_current_url(env, strip_querystring=True)
1234
 
    'http://localhost/script/'
1235
 
 
1236
 
    :param environ: the WSGI environment to get the current URL from.
1237
 
    :param root_only: set `True` if you only want the root URL.
1238
 
    :param strip_querystring: set to `True` if you don't want the querystring.
1239
 
    :param host_only: set to `True` if the host URL should be returned.
1240
 
    """
1241
 
    tmp = [environ['wsgi.url_scheme'], '://', get_host(environ)]
1242
 
    cat = tmp.append
1243
 
    if host_only:
1244
 
        return ''.join(tmp) + '/'
1245
 
    cat(urllib.quote(environ.get('SCRIPT_NAME', '').rstrip('/')))
1246
 
    if root_only:
1247
 
        cat('/')
1248
 
    else:
1249
 
        cat(urllib.quote('/' + environ.get('PATH_INFO', '').lstrip('/')))
1250
 
        if not strip_querystring:
1251
 
            qs = environ.get('QUERY_STRING')
1252
 
            if qs:
1253
 
                cat('?' + qs)
1254
 
    return ''.join(tmp)
1255
 
 
1256
 
 
1257
 
def pop_path_info(environ):
1258
 
    """Removes and returns the next segment of `PATH_INFO`, pushing it onto
1259
 
    `SCRIPT_NAME`.  Returns `None` if there is nothing left on `PATH_INFO`.
1260
 
 
1261
 
    If there are empty segments (``'/foo//bar``) these are ignored but
1262
 
    properly pushed to the `SCRIPT_NAME`:
1263
 
 
1264
 
    >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'}
1265
 
    >>> pop_path_info(env)
1266
 
    'a'
1267
 
    >>> env['SCRIPT_NAME']
1268
 
    '/foo/a'
1269
 
    >>> pop_path_info(env)
1270
 
    'b'
1271
 
    >>> env['SCRIPT_NAME']
1272
 
    '/foo/a/b'
1273
 
 
1274
 
    .. versionadded:: 0.5
1275
 
 
1276
 
    :param environ: the WSGI environment that is modified.
1277
 
    """
1278
 
    path = environ.get('PATH_INFO')
1279
 
    if not path:
1280
 
        return None
1281
 
 
1282
 
    script_name = environ.get('SCRIPT_NAME', '')
1283
 
 
1284
 
    # shift multiple leading slashes over
1285
 
    old_path = path
1286
 
    path = path.lstrip('/')
1287
 
    if path != old_path:
1288
 
        script_name += '/' * (len(old_path) - len(path))
1289
 
 
1290
 
    if '/' not in path:
1291
 
        environ['PATH_INFO'] = ''
1292
 
        environ['SCRIPT_NAME'] = script_name + path
1293
 
        return path
1294
 
 
1295
 
    segment, path = path.split('/', 1)
1296
 
    environ['PATH_INFO'] = '/' + path
1297
 
    environ['SCRIPT_NAME'] = script_name + segment
1298
 
    return segment
1299
 
 
1300
 
 
1301
 
def peek_path_info(environ):
1302
 
    """Returns the next segment on the `PATH_INFO` or `None` if there
1303
 
    is none.  Works like :func:`pop_path_info` without modifying the
1304
 
    environment:
1305
 
 
1306
 
    >>> env = {'SCRIPT_NAME': '/foo', 'PATH_INFO': '/a/b'}
1307
 
    >>> peek_path_info(env)
1308
 
    'a'
1309
 
    >>> peek_path_info(env)
1310
 
    'a'
1311
 
 
1312
 
    .. versionadded:: 0.5
1313
 
 
1314
 
    :param environ: the WSGI environment that is checked.
1315
 
    """
1316
 
    segments = environ.get('PATH_INFO', '').lstrip('/').split('/', 1)
1317
 
    if segments:
1318
 
        return segments[0]
1319
 
 
1320
 
 
1321
 
def cookie_date(expires=None):
1322
 
    """Formats the time to ensure compatibility with Netscape's cookie
1323
 
    standard.
1324
 
 
1325
 
    Accepts a floating point number expressed in seconds since the epoc in, a
1326
 
    datetime object or a timetuple.  All times in UTC.  The :func:`parse_date`
1327
 
    function can be used to parse such a date.
1328
 
 
1329
 
    Outputs a string in the format ``Wdy, DD-Mon-YYYY HH:MM:SS GMT``.
1330
 
 
1331
 
    :param expires: If provided that date is used, otherwise the current.
1332
 
    """
1333
 
    return _dump_date(expires, '-')
1334
 
 
1335
 
 
1336
 
def parse_cookie(header, charset='utf-8', errors='ignore',
1337
 
                 cls=None):
1338
 
    """Parse a cookie.  Either from a string or WSGI environ.
1339
 
 
1340
 
    Per default encoding errors are ignored.  If you want a different behavior
1341
 
    you can set `errors` to ``'replace'`` or ``'strict'``.  In strict mode a
1342
 
    :exc:`HTTPUnicodeError` is raised.
1343
 
 
1344
 
    .. versionchanged:: 0.5
1345
 
       This function now returns a :class:`TypeConversionDict` instead of a
1346
 
       regular dict.  The `cls` parameter was added.
1347
 
 
1348
 
    :param header: the header to be used to parse the cookie.  Alternatively
1349
 
                   this can be a WSGI environment.
1350
 
    :param charset: the charset for the cookie values.
1351
 
    :param errors: the error behavior for the charset decoding.
1352
 
    :param cls: an optional dict class to use.  If this is not specified
1353
 
                       or `None` the default :class:`TypeConversionDict` is
1354
 
                       used.
1355
 
    """
1356
 
    if isinstance(header, dict):
1357
 
        header = header.get('HTTP_COOKIE', '')
1358
 
    if cls is None:
1359
 
        cls = TypeConversionDict
1360
 
    cookie = _ExtendedCookie()
1361
 
    cookie.load(header)
1362
 
    result = {}
1363
 
 
1364
 
    # decode to unicode and skip broken items.  Our extended morsel
1365
 
    # and extended cookie will catch CookieErrors and convert them to
1366
 
    # `None` items which we have to skip here.
1367
 
    for key, value in cookie.iteritems():
1368
 
        if value.value is not None:
1369
 
            result[key] = _decode_unicode(value.value, charset, errors)
1370
 
 
1371
 
    return cls(result)
1372
 
 
1373
 
 
1374
 
def dump_cookie(key, value='', max_age=None, expires=None, path='/',
1375
 
                domain=None, secure=None, httponly=False, charset='utf-8',
1376
 
                sync_expires=True):
1377
 
    """Creates a new Set-Cookie header without the ``Set-Cookie`` prefix
1378
 
    The parameters are the same as in the cookie Morsel object in the
1379
 
    Python standard library but it accepts unicode data, too.
1380
 
 
1381
 
    :param max_age: should be a number of seconds, or `None` (default) if
1382
 
                    the cookie should last only as long as the client's
1383
 
                    browser session.  Additionally `timedelta` objects
1384
 
                    are accepted, too.
1385
 
    :param expires: should be a `datetime` object or unix timestamp.
1386
 
    :param path: limits the cookie to a given path, per default it will
1387
 
                 span the whole domain.
1388
 
    :param domain: Use this if you want to set a cross-domain cookie. For
1389
 
                   example, ``domain=".example.com"`` will set a cookie
1390
 
                   that is readable by the domain ``www.example.com``,
1391
 
                   ``foo.example.com`` etc. Otherwise, a cookie will only
1392
 
                   be readable by the domain that set it.
1393
 
    :param secure: The cookie will only be available via HTTPS
1394
 
    :param httponly: disallow JavaScript to access the cookie.  This is an
1395
 
                     extension to the cookie standard and probably not
1396
 
                     supported by all browsers.
1397
 
    :param charset: the encoding for unicode values.
1398
 
    :param sync_expires: automatically set expires if max_age is defined
1399
 
                         but expires not.
1400
 
    """
1401
 
    try:
1402
 
        key = str(key)
1403
 
    except UnicodeError:
1404
 
        raise TypeError('invalid key %r' % key)
1405
 
    if isinstance(value, unicode):
1406
 
        value = value.encode(charset)
1407
 
    morsel = _ExtendedMorsel(key, value)
1408
 
    if isinstance(max_age, timedelta):
1409
 
        max_age = (max_age.days * 60 * 60 * 24) + max_age.seconds
1410
 
    if expires is not None:
1411
 
        if not isinstance(expires, basestring):
1412
 
            expires = cookie_date(expires)
1413
 
        morsel['expires'] = expires
1414
 
    elif max_age is not None and sync_expires:
1415
 
        morsel['expires'] = cookie_date(time() + max_age)
1416
 
    for k, v in (('path', path), ('domain', domain), ('secure', secure),
1417
 
                 ('max-age', max_age), ('httponly', httponly)):
1418
 
        if v is not None and v is not False:
1419
 
            morsel[k] = str(v)
1420
 
    return morsel.output(header='').lstrip()
1421
 
 
1422
 
 
1423
 
def http_date(timestamp=None):
1424
 
    """Formats the time to match the RFC1123 date format.
1425
 
 
1426
 
    Accepts a floating point number expressed in seconds since the epoc in, a
1427
 
    datetime object or a timetuple.  All times in UTC.  The :func:`parse_date`
1428
 
    function can be used to parse such a date.
1429
 
 
1430
 
    Outputs a string in the format ``Wdy, DD Mon YYYY HH:MM:SS GMT``.
1431
 
 
1432
 
    :param timestamp: If provided that date is used, otherwise the current.
1433
 
    """
1434
 
    return _dump_date(timestamp, ' ')
1435
 
 
1436
 
 
1437
 
def redirect(location, code=302):
1438
 
    """Return a response object (a WSGI application) that, if called,
1439
 
    redirects the client to the target location.  Supported codes are 301,
1440
 
    302, 303, 305, and 307.  300 is not supported because it's not a real
1441
 
    redirect and 304 because it's the answer for a request with a request
1442
 
    with defined If-Modified-Since headers.
1443
 
 
1444
 
    :param location: the location the response should redirect to.
1445
 
    :param code: the redirect status code.
1446
 
    """
1447
 
    assert code in (301, 302, 303, 305, 307), 'invalid code'
1448
 
    from werkzeug.wrappers import BaseResponse
1449
 
    response = BaseResponse(
1450
 
        '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">\n'
1451
 
        '<title>Redirecting...</title>\n'
1452
 
        '<h1>Redirecting...</h1>\n'
1453
 
        '<p>You should be redirected automatically to target URL: '
1454
 
        '<a href="%s">%s</a>.  If not click the link.' %
1455
 
        ((escape(location),) * 2), code, mimetype='text/html')
1456
 
    response.headers['Location'] = location
1457
 
    return response
1458
 
 
1459
 
 
1460
 
def append_slash_redirect(environ, code=301):
1461
 
    """Redirect to the same URL but with a slash appended.  The behavior
1462
 
    of this function is undefined if the path ends with a slash already.
1463
 
 
1464
 
    :param environ: the WSGI environment for the request that triggers
1465
 
                    the redirect.
1466
 
    :param code: the status code for the redirect.
1467
 
    """
1468
 
    new_path = environ['PATH_INFO'].strip('/') + '/'
1469
 
    query_string = environ['QUERY_STRING']
1470
 
    if query_string:
1471
 
        new_path += '?' + query_string
1472
 
    return redirect(new_path, code)
1473
 
 
1474
 
 
1475
 
def responder(f):
1476
 
    """Marks a function as responder.  Decorate a function with it and it
1477
 
    will automatically call the return value as WSGI application.
1478
 
 
1479
 
    Example::
1480
 
 
1481
 
        @responder
1482
 
        def application(environ, start_response):
1483
 
            return Response('Hello World!')
1484
 
    """
1485
 
    return _patch_wrapper(f, lambda *a: f(*a)(*a[-2:]))
1486
 
 
1487
 
 
1488
 
def wrap_file(environ, file, buffer_size=8192):
1489
 
    """Wraps a file.  This uses the WSGI server's file wrapper if available
1490
 
    or otherwise the generic :class:`FileWrapper`.
1491
 
 
1492
 
    .. versionadded:: 0.5
1493
 
 
1494
 
    If the file wrapper from the WSGI server is used it's important to not
1495
 
    iterate over it from inside the application but to pass it through
1496
 
    unchanged.  If you want to pass out a file wrapper inside a response
1497
 
    object you have to set :attr:`~BaseResponse.direct_passthrough` to `True`.
1498
 
 
1499
 
    More information about file wrappers are available in :pep:`333`.
1500
 
 
1501
 
    :param file: a :class:`file`-like object with a :meth:`~file.read` method.
1502
 
    :param buffer_size: number of bytes for one iteration.
1503
 
    """
1504
 
    return environ.get('wsgi.file_wrapper', FileWrapper)(file, buffer_size)
1505
 
 
1506
 
 
1507
 
def import_string(import_name, silent=False):
1508
 
    """Imports an object based on a string.  This is useful if you want to
1509
 
    use import paths as endpoints or something similar.  An import path can
1510
 
    be specified either in dotted notation (``xml.sax.saxutils.escape``)
1511
 
    or with a colon as object delimiter (``xml.sax.saxutils:escape``).
1512
 
 
1513
 
    If `silent` is True the return value will be `None` if the import fails.
1514
 
 
1515
 
    :param import_name: the dotted name for the object to import.
1516
 
    :param silent: if set to `True` import errors are ignored and
1517
 
                   `None` is returned instead.
1518
 
    :return: imported object
1519
 
    """
1520
 
    try:
1521
 
        if ':' in import_name:
1522
 
            module, obj = import_name.split(':', 1)
1523
 
        elif '.' in import_name:
1524
 
            module, obj = import_name.rsplit('.', 1)
1525
 
        else:
1526
 
            return __import__(import_name)
1527
 
        return getattr(__import__(module, None, None, [obj]), obj)
1528
 
    except (ImportError, AttributeError):
1529
 
        if not silent:
1530
 
            raise
1531
 
 
1532
 
 
1533
 
def find_modules(import_path, include_packages=False, recursive=False):
1534
 
    """Find all the modules below a package.  This can be useful to
1535
 
    automatically import all views / controllers so that their metaclasses /
1536
 
    function decorators have a chance to register themselves on the
1537
 
    application.
1538
 
 
1539
 
    Packages are not returned unless `include_packages` is `True`.  This can
1540
 
    also recursively list modules but in that case it will import all the
1541
 
    packages to get the correct load path of that module.
1542
 
 
1543
 
    :param import_name: the dotted name for the package to find child modules.
1544
 
    :param include_packages: set to `True` if packages should be returned, too.
1545
 
    :param recursive: set to `True` if recursion should happen.
1546
 
    :return: generator
1547
 
    """
1548
 
    module = import_string(import_path)
1549
 
    path = getattr(module, '__path__', None)
1550
 
    if path is None:
1551
 
        raise ValueError('%r is not a package' % import_path)
1552
 
    basename = module.__name__ + '.'
1553
 
    for modname, ispkg in _iter_modules(path):
1554
 
        modname = basename + modname
1555
 
        if ispkg:
1556
 
            if include_packages:
1557
 
                yield modname
1558
 
            if recursive:
1559
 
                for item in find_modules(modname, include_packages, True):
1560
 
                    yield item
1561
 
        else:
1562
 
            yield modname
1563
 
 
1564
 
 
1565
 
def validate_arguments(func, args, kwargs, drop_extra=True):
1566
 
    """Check if the function accepts the arguments and keyword arguments.
1567
 
    Returns a new ``(args, kwargs)`` tuple that can savely be passed to
1568
 
    the function without causing a `TypeError` because the function signature
1569
 
    is incompatible.  If `drop_extra` is set to `True` (which is the default)
1570
 
    any extra positional or keyword arguments are dropped automatically.
1571
 
 
1572
 
    The exception raised provides three attributes:
1573
 
 
1574
 
    `missing`
1575
 
        A set of argument names that the function expected but where
1576
 
        missing.
1577
 
 
1578
 
    `extra`
1579
 
        A dict of keyword arguments that the function can not handle but
1580
 
        where provided.
1581
 
 
1582
 
    `extra_positional`
1583
 
        A list of values that where given by positional argument but the
1584
 
        function cannot accept.
1585
 
 
1586
 
    This can be useful for decorators that forward user submitted data to
1587
 
    a view function::
1588
 
 
1589
 
        from werkzeug import ArgumentValidationError, validate_arguments
1590
 
 
1591
 
        def sanitize(f):
1592
 
            def proxy(request):
1593
 
                data = request.values.to_dict()
1594
 
                try:
1595
 
                    args, kwargs = validate_arguments(f, (request,), data)
1596
 
                except ArgumentValidationError:
1597
 
                    raise BadRequest('The browser failed to transmit all '
1598
 
                                     'the data expected.')
1599
 
                return f(*args, **kwargs)
1600
 
            return proxy
1601
 
 
1602
 
    :param func: the function the validation is performed against.
1603
 
    :param args: a tuple of positional arguments.
1604
 
    :param kwargs: a dict of keyword arguments.
1605
 
    :param drop_extra: set to `False` if you don't want extra arguments
1606
 
                       to be silently dropped.
1607
 
    :return: tuple in the form ``(args, kwargs)``.
1608
 
    """
1609
 
    parser = _parse_signature(func)
1610
 
    args, kwargs, missing, extra, extra_positional = parser(args, kwargs)[:5]
1611
 
    if missing:
1612
 
        raise ArgumentValidationError(tuple(missing))
1613
 
    elif (extra or extra_positional) and not drop_extra:
1614
 
        raise ArgumentValidationError(None, extra, extra_positional)
1615
 
    return tuple(args), kwargs
1616
 
 
1617
 
 
1618
 
def bind_arguments(func, args, kwargs):
1619
 
    """Bind the arguments provided into a dict.  When passed a function,
1620
 
    a tuple of arguments and a dict of keyword arguments `bind_arguments`
1621
 
    returns a dict of names as the function would see it.  This can be useful
1622
 
    to implement a cache decorator that uses the function arguments to build
1623
 
    the cache key based on the values of the arguments.
1624
 
 
1625
 
    :param func: the function the arguments should be bound for.
1626
 
    :param args: tuple of positional arguments.
1627
 
    :param kwargs: a dict of keyword arguments.
1628
 
    :return: a :class:`dict` of bound keyword arguments.
1629
 
    """
1630
 
    args, kwargs, missing, extra, extra_positional, \
1631
 
        arg_spec, vararg_var, kwarg_var = _parse_signature(func)(args, kwargs)
1632
 
    values = {}
1633
 
    for (name, has_default, default), value in zip(arg_spec, args):
1634
 
        values[name] = value
1635
 
    if vararg_var is not None:
1636
 
        values[vararg_var] = tuple(extra_positional)
1637
 
    elif extra_positional:
1638
 
        raise TypeError('too many positional arguments')
1639
 
    if kwarg_var is not None:
1640
 
        multikw = set(extra) & set([x[0] for x in arg_spec])
1641
 
        if multikw:
1642
 
            raise TypeError('got multiple values for keyword argument ' +
1643
 
                            repr(iter(multikw).next()))
1644
 
        values[kwarg_var] = extra
1645
 
    elif extra:
1646
 
        raise TypeError('got unexpected keyword argument ' +
1647
 
                        repr(iter(extra).next()))
1648
 
    return values
1649
 
 
1650
 
 
1651
 
class ArgumentValidationError(ValueError):
1652
 
    """Raised if :func:`validate_arguments` fails to validate"""
1653
 
 
1654
 
    def __init__(self, missing=None, extra=None, extra_positional=None):
1655
 
        self.missing = set(missing or ())
1656
 
        self.extra = extra or {}
1657
 
        self.extra_positional = extra_positional or []
1658
 
        ValueError.__init__(self, 'function arguments invalid.  ('
1659
 
                            '%d missing, %d additional)' % (
1660
 
            len(self.missing),
1661
 
            len(self.extra) + len(self.extra_positional)
1662
 
        ))
1663
 
 
1664
 
 
1665
 
# circular dependency fun
1666
 
from werkzeug.http import parse_multipart, parse_options_header, \
1667
 
     is_resource_modified
1668
 
from werkzeug.exceptions import BadRequest, RequestEntityTooLarge
1669
 
from werkzeug.datastructures import MultiDict, TypeConversionDict
1670
 
 
1671
 
 
1672
 
# DEPRECATED
1673
 
# these objects were previously in this module as well.  we import
1674
 
# them here for backwards compatibility.  Will go away in 0.6
1675
 
from werkzeug.datastructures import MultiDict, CombinedMultiDict, \
1676
 
     Headers, EnvironHeaders
1677
 
 
1678
 
def create_environ(*args, **kwargs):
1679
 
    """backward compatibility."""
1680
 
    from werkzeug.test import create_environ
1681
 
    return create_environ(*args, **kwargs)
1682
 
 
1683
 
def run_wsgi_app(*args, **kwargs):
1684
 
    """backwards compatibility."""
1685
 
    from werkzeug.test import run_wsgi_app
1686
 
    return run_wsgi_app(*args, **kwargs)