Codebase list python-werkzeug / f4ecffb
Import python-werkzeug_0.13+dfsg1.orig.tar.gz Ondřej Nový 6 years ago
63 changed file(s) with 3022 addition(s) and 2403 deletion(s). Raw diff Collapse all Expand all
0 environment:
1 global:
2 TOXENV: "py"
3
4 matrix:
5 - PYTHON: "C:\\Python27"
6 - PYTHON: "C:\\Python36"
7
8 install:
9 - "%PYTHON%\\python.exe -m pip install -U pip setuptools wheel tox"
10
11 build: false
12
13 test_script:
14 - "%PYTHON%\\python.exe -m tox"
0 [run]
1 branch = True
2 source =
3 werkzeug
4 tests
5
6 [paths]
7 source =
8 werkzeug
9 .tox/*/lib/python*/site-packages/werkzeug
10 .tox/pypy/site-packages/werkzeug
0 tests/res/chunked.txt binary
1
55 *.pyo
66 env
77 .DS_Store
8 .tox
98 docs/_build
109 bench/a
1110 bench/b
11 .tox
1212 .coverage
13 .coverage.*
1314 coverage_out
15 htmlcov
1416 .cache
1517 .xprocess
16 htmlcov
1718 .hypothesis
19 test_uwsgi_failed
20 .idea
0 os: linux
01 sudo: false
12 language: python
2 python:
3 - 2.6
4 - 2.7
5 - pypy
6 - 3.3
7 - 3.4
8 - 3.5
9 - 3.6
10 - nightly
11 env:
12 - TOXENV_SUFFIX=normal
13 - TOXENV_SUFFIX=stylecheck
14 - TOXENV_SUFFIX=uwsgi
153
164 matrix:
17 exclude:
18 - python: pypy
19 env: TOXENV_SUFFIX=uwsgi
20 - python: 2.6 # flake8 doesn't run on 2.6
21 env: TOXENV_SUFFIX=stylecheck
22 include:
23 - os: osx
24 language: generic
25 env: TOXENV_SUFFIX=normal
26
27 os: linux
5 include:
6 - python: 3.6
7 env: TOXENV=hypothesis-uwsgi,codecov,stylecheck,docs-html
8 - python: 3.5
9 env: TOXENV=py,codecov
10 - python: 3.4
11 env: TOXENV=py,codecov
12 - python: 2.7
13 env: TOXENV=py,codecov
14 - python: pypy
15 env: TOXENV=py,codecov
16 - python: nightly
17 env: TOXENV=py
18 - os: osx
19 language: generic
20 env: TOXENV=py
21 allow_failures:
22 - os: osx
23 language: generic
24 env: TOXENV=py
25 fast_finish: true
2826
2927 before_install:
3028 - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then
3129 brew update;
3230 brew install python3 redis memcached;
33 virtualenv ~/py-env -p python3;
31 virtualenv -p python3 ~/py-env;
3432 . ~/py-env/bin/activate;
3533 fi
36 # Travis uses an outdated PyPy, this installs the most recent one.
37 # This makes the tests run on Travis' legacy infrastructure, but so be it.
38 # temporary pyenv installation to get pypy-2.6 before container infra upgrade
39 - if [ "$TRAVIS_PYTHON_VERSION" = "pypy" ]; then
40 git clone https://github.com/yyuu/pyenv.git ~/.pyenv;
34 # Travis uses an outdated PyPy, this installs a more recent one.
35 - if [[ "$TRAVIS_PYTHON_VERSION" == "pypy" ]]; then
36 git clone https://github.com/pyenv/pyenv.git ~/.pyenv;
4137 PYENV_ROOT="$HOME/.pyenv";
4238 PATH="$PYENV_ROOT/bin:$PATH";
4339 eval "$(pyenv init -)";
44 pyenv install pypy-4.0.1;
45 pyenv global pypy-4.0.1;
40 pyenv install pypy2.7-5.8.0;
41 pyenv global pypy2.7-5.8.0;
4642 fi
47 - python --version
43 - if [[ "$TRAVIS_PYTHON_VERSION" == "pypy3" ]]; then
44 git clone https://github.com/pyenv/pyenv.git ~/.pyenv;
45 PYENV_ROOT="$HOME/.pyenv";
46 PATH="$PYENV_ROOT/bin:$PATH";
47 eval "$(pyenv init -)";
48 pyenv install pypy3.5-5.8.0;
49 pyenv global pypy3.5-5.8.0;
50 fi
4851
4952 install:
50 - pip install tox flake8
53 - pip install tox
5154
5255 script:
53 - tox -e py-$TOXENV_SUFFIX
56 - tox
57
58 cache:
59 - pip
5460
5561 branches:
56 only:
57 - master
58 - auto
59 - /^.*-maintenance$/
62 only:
63 - master
64 - /^.*-maintenance$/
6065
6166 notifications:
6267 email: false
0 Werkzeug is written and maintained by the Werkzeug Team and various
1 contributors:
0 Werkzeug is developed and maintained by the Pallets team and community
1 contributors. It was created by Armin Ronacher. The core maintainers
2 are:
23
3 Project Leader / Developer:
4 - Armin Ronacher (mitsuhiko)
5 - Marcus Unterwaditzer (untitaker)
6 - Adrian Mönnich (ThiefMaster)
7 - David Lord (davidism)
48
5 - Armin Ronacher <armin.ronacher@active-4.com>
9 A full list of contributors is available from git with:
610
7 - Georg Brandl
8 - Leif K-Brooks <eurleif@gmail.com>
9 - Thomas Johansson
10 - Marian Sigler
11 - Ronny Pfannschmidt
12 - Noah Slater <nslater@tumbolia.org>
13 - Alec Thomas
14 - Shannon Behrens
15 - Christoph Rauch
16 - Clemens Hermann
17 - Jason Kirtland
18 - Ali Afshar
19 - Christopher Grebs <cg@webshox.org>
20 - Sean Cazzell <seancazzell@gmail.com>
21 - Florent Xicluna
22 - Kyle Dawkins
23 - Pedro Algarvio
24 - Zahari Petkov
25 - Ludvig Ericson
26 - Kenneth Reitz
27 - Daniel Neuhäuser
28 - Markus Unterwaditzer
29 - Joe Esposito <joe@joeyespo.com>
30 - Abhinav Upadhyay <er.abhinav.upadhyay@gmail.com>
31 - immerrr <immerrr@gmail.com>
32 - Cédric Krier
33 - Phil Jones
34 - Michael Hunsinger
35 - Lars Holm Nielsen
36 - Joël Charles
37 - Benjamin Dopplinger
38 - Nils Steinger
11 git shortlog -sne
3912
40 Contributors of code for werkzeug/examples are:
41
42 - Itay Neeman <itay@neeman.net>
43
44 The SSL related parts of the Werkzeug development server are partially
45 taken from Paste. Same thing is true for the range support which comes
46 from WebOb which is a Paste project. The original code is MIT licensed which
47 is largely compatible with the modfied BSD license. The following copyrights
13 The SSL parts of the Werkzeug development server are partially taken
14 from Paste. The same is true for the range support which comes from
15 WebOb, a Paste project. The original code is MIT licensed and largely
16 compatible with the BSD 3-clause license. The following copyrights
4817 apply:
4918
5019 - (c) 2005 Ian Bicking and contributors
5120 - (c) 2005 Clark C. Evans
5221
53 The rename() function from the posixemulation was taken almost unmodified
54 from the Trac project's utility module. The original code is BSD licensed
55 with the following copyrights from that module:
22 The rename() function from the posixemulation was taken almost
23 unmodified from the Trac project's utility module. The original code is
24 BSD licensed with the following copyrights from that module:
5625
5726 - (c) 2003-2009 Edgewall Software
5827 - (c) 2003-2006 Jonas Borgström <jonas@edgewall.com>
+0
-1182
CHANGES less more
0 Werkzeug Changelog
1 ==================
2
3 Version 0.12.2
4 --------------
5
6 Released on May 16 2017
7
8 - Fix regression: Pull request ``#892`` prevented Werkzeug from correctly
9 logging the IP of a remote client behind a reverse proxy, even when using
10 `ProxyFix`.
11 - Fix a bug in `safe_join` on Windows.
12
13 Version 0.12.1
14 --------------
15
16 Released on March 15th 2017
17
18 - Fix crash of reloader (used on debug mode) on Windows.
19 (`OSError: [WinError 10038]`). See pull request ``#1081``
20 - Partially revert change to class hierarchy of `Headers`. See ``#1084``.
21
22 Version 0.12
23 ------------
24
25 Released on March 10th 2017
26
27 - Spit out big deprecation warnings for werkzeug.script
28 - Use `inspect.getfullargspec` internally when available as
29 `inspect.getargspec` is gone in 3.6
30 - Added support for status code 451 and 423
31 - Improved the build error suggestions. In particular only if
32 someone stringifies the error will the suggestions be calculated.
33 - Added support for uWSGI's caching backend.
34 - Fix a bug where iterating over a `FileStorage` would result in an infinite
35 loop.
36 - Datastructures now inherit from the relevant baseclasses from the
37 `collections` module in the stdlib. See #794.
38 - Add support for recognizing NetBSD, OpenBSD, FreeBSD, DragonFlyBSD platforms
39 in the user agent string.
40 - Recognize SeaMonkey browser name and version correctly
41 - Recognize Baiduspider, and bingbot user agents
42 - If `LocalProxy`'s wrapped object is a function, refer to it with __wrapped__
43 attribute.
44 - The defaults of ``generate_password_hash`` have been changed to more secure
45 ones, see pull request ``#753``.
46 - Add support for encoding in options header parsing, see pull request
47 ``#933``.
48 - ``test.Client`` now properly handles Location headers with relative URLs, see
49 pull request ``#879``.
50 - When `HTTPException` is raised, it now prints the description, for easier
51 debugging.
52 - Werkzeug's dict-like datastructures now have ``view``-methods under Python 2,
53 see pull request ``#968``.
54 - Fix a bug in ``MultiPartParser`` when no ``stream_factory`` was provided
55 during initialization, see pull request ``#973``.
56 - Disable autocorrect and spellchecker in the debugger middleware's Python
57 prompt, see pull request ``#994``.
58 - Don't redirect to slash route when method doesn't match, see pull request
59 ``#907``.
60 - Fix a bug when using ``SharedDataMiddleware`` with frozen packages, see pull
61 request ``#959``.
62 - `Range` header parsing function fixed for invalid values ``#974``.
63 - Add support for byte Range Requests, see pull request ``#978``.
64 - Use modern cryptographic defaults in the dev servers ``#1004``.
65 - the post() method of the test client now accept file object through the data
66 parameter.
67 - Color run_simple's terminal output based on HTTP codes ``#1013``.
68 - Fix self-XSS in debugger console, see ``#1031``.
69 - Fix IPython 5.x shell support, see ``#1033``.
70
71 Version 0.11.16
72 ---------------
73
74 - werkzeug.serving: set CONTENT_TYPE / CONTENT_LENGTH if only they're provided by the client
75 - werkzeug.serving: Fix crash of reloader when using `python -m werkzeug.serving`.
76
77 Version 0.11.15
78 ---------------
79
80 Released on December 30th 2016.
81
82 - Bugfix for the bugfix in the previous release.
83
84 Version 0.11.14
85 ---------------
86
87 Released on December 30th 2016.
88
89 - Check if platform can fork before importing ``ForkingMixIn``, raise exception
90 when creating ``ForkingWSGIServer`` on such a platform, see PR ``#999``.
91
92 Version 0.11.13
93 ---------------
94
95 Released on December 26th 2016.
96
97 - Correct fix for the reloader issuer on certain Windows installations.
98
99 Version 0.11.12
100 ---------------
101
102 Released on December 26th 2016.
103
104 - Fix more bugs in multidicts regarding empty lists. See ``#1000``.
105 - Add some docstrings to some `EnvironBuilder` properties that were previously
106 unintentionally missing.
107 - Added a workaround for the reloader on windows.
108
109 Version 0.11.11
110 ---------------
111
112 Released on August 31st 2016.
113
114 - Fix JSONRequestMixin for Python3. See #731
115 - Fix broken string handling in test client when passing integers. See #852
116 - Fix a bug in ``parse_options_header`` where an invalid content type
117 starting with comma or semi-colon would result in an invalid return value,
118 see issue ``#995``.
119 - Fix a bug in multidicts when passing empty lists as values, see issue
120 ``#979``.
121 - Fix a security issue that allows XSS on the Werkzeug debugger. See ``#1001``.
122
123 Version 0.11.10
124 ---------------
125
126 Released on May 24th 2016.
127
128 - Fixed a bug that occurs when running on Python 2.6 and using a broken locale.
129 See pull request #912.
130 - Fixed a crash when running the debugger on Google App Engine. See issue #925.
131 - Fixed an issue with multipart parsing that could cause memory exhaustion.
132
133 Version 0.11.9
134 --------------
135
136 Released on April 24th 2016.
137
138 - Corrected an issue that caused the debugger not to use the
139 machine GUID on POSIX systems.
140 - Corrected a Unicode error on Python 3 for the debugger's
141 PIN usage.
142 - Corrected the timestamp verification in the pin debug code.
143 Without this fix the pin was remembered for too long.
144
145 Version 0.11.8
146 --------------
147
148 Released on April 15th 2016.
149
150 - fixed a problem with the machine GUID detection code on OS X
151 on Python 3.
152
153 Version 0.11.7
154 --------------
155
156 Released on April 14th 2016.
157
158 - fixed a regression on Python 3 for the debugger.
159
160 Version 0.11.6
161 --------------
162
163 Released on April 14th 2016.
164
165 - werkzeug.serving: Still show the client address on bad requests.
166 - improved the PIN based protection for the debugger to make it harder to
167 brute force via trying cookies. Please keep in mind that the debugger
168 *is not intended for running on production environments*
169 - increased the pin timeout to a week to make it less annoying for people
170 which should decrease the chance that users disable the pin check
171 entirely.
172 - werkzeug.serving: Fix broken HTTP_HOST when path starts with double slash.
173
174 Version 0.11.5
175 --------------
176
177 Released on March 22nd 2016.
178
179 - werkzeug.serving: Fix crash when attempting SSL connection to HTTP server.
180
181 Version 0.11.4
182 --------------
183
184 Released on February 14th 2016.
185
186 - Fixed werkzeug.serving not working from -m flag.
187 - Fixed incorrect weak etag handling.
188
189 Version 0.11.3
190 --------------
191
192 Released on December 20th 2015.
193
194 - Fixed an issue with copy operations not working against
195 proxies.
196 - Changed the logging operations of the development server to
197 correctly log where the server is running in all situations
198 again.
199 - Fixed another regression with SSL wrapping similar to the
200 fix in 0.11.2 but for a different code path.
201
202 Version 0.11.2
203 --------------
204
205 Released on November 12th 2015.
206
207 - Fix inheritable sockets on Windows on Python 3.
208 - Fixed an issue with the forking server not starting any longer.
209 - Fixed SSL wrapping on platforms that supported opening sockets
210 by file descriptor.
211 - No longer log from the watchdog reloader.
212 - Unicode errors in hosts are now better caught or converted into
213 bad request errors.
214
215 Version 0.11.1
216 --------------
217
218 Released on November 10th 2015.
219
220 - Fixed a regression on Python 3 in the debugger.
221
222 Version 0.11
223 ------------
224
225 Released on November 8th 2015, codename Gleisbaumaschine.
226
227 - Added ``reloader_paths`` option to ``run_simple`` and other functions in
228 ``werkzeug.serving``. This allows the user to completely override the Python
229 module watching of Werkzeug with custom paths.
230 - Many custom cached properties of Werkzeug's classes are now subclasses of
231 Python's ``property`` type (issue ``#616``).
232 - ``bind_to_environ`` now doesn't differentiate between implicit and explicit
233 default port numbers in ``HTTP_HOST`` (pull request ``#204``).
234 - ``BuildErrors`` are now more informative. They come with a complete sentence
235 as error message, and also provide suggestions (pull request ``#691``).
236 - Fix a bug in the user agent parser where Safari's build number instead of
237 version would be extracted (pull request ``#703``).
238 - Fixed issue where RedisCache set_many was broken for twemproxy, which doesn't
239 support the default MULTI command (pull request ``#702``).
240 - ``mimetype`` parameters on request and response classes are now always
241 converted to lowercase.
242 - Changed cache so that cache never expires if timeout is 0. This also fixes
243 an issue with redis setex (issue ``#550``)
244 - Werkzeug now assumes ``UTF-8`` as filesystem encoding on Unix if Python
245 detected it as ASCII.
246 - New optional `has` method on caches.
247 - Fixed various bugs in `parse_options_header` (pull request ``#643``).
248 - If the reloader is enabled the server will now open the socket in the parent
249 process if this is possible. This means that when the reloader kicks in
250 the connection from client will wait instead of tearing down. This does
251 not work on all Python versions.
252 - Implemented PIN based authentication for the debugger. This can optionally
253 be disabled but is discouraged. This change was necessary as it has been
254 discovered that too many people run the debugger in production.
255 - Devserver no longer requires SSL module to be installed.
256
257 Version 0.10.5
258 --------------
259
260 (bugfix release, release date yet to be decided)
261
262 - Reloader: Correctly detect file changes made by moving temporary files over
263 the original, which is e.g. the case with PyCharm (pull request ``#722``).
264 - Fix bool behavior of ``werkzeug.datastructures.ETags`` under Python 3 (issue
265 ``#744``).
266
267 Version 0.10.4
268 --------------
269
270 (bugfix release, released on March 26th 2015)
271
272 - Re-release of 0.10.3 with packaging artifacts manually removed.
273
274 Version 0.10.3
275 --------------
276
277 (bugfix release, released on March 26th 2015)
278
279 - Re-release of 0.10.2 without packaging artifacts.
280
281 Version 0.10.2
282 --------------
283
284 (bugfix release, released on March 26th 2015)
285
286 - Fixed issue where ``empty`` could break third-party libraries that relied on
287 keyword arguments (pull request ``#675``)
288 - Improved ``Rule.empty`` by providing a ```get_empty_kwargs`` to allow setting
289 custom kwargs without having to override entire ``empty`` method. (pull
290 request ``#675``)
291 - Fixed ```extra_files``` parameter for reloader to not cause startup
292 to crash when included in server params
293 - Using `MultiDict` when building URLs is now not supported again. The behavior
294 introduced several regressions.
295 - Fix performance problems with stat-reloader (pull request ``#715``).
296
297 Version 0.10.1
298 --------------
299
300 (bugfix release, released on February 3rd 2015)
301
302 - Fixed regression with multiple query values for URLs (pull request ``#667``).
303 - Fix issues with eventlet's monkeypatching and the builtin server (pull
304 request ``#663``).
305
306 Version 0.10
307 ------------
308
309 Released on January 30th 2015, codename Bagger.
310
311 - Changed the error handling of and improved testsuite for the caches in
312 ``contrib.cache``.
313 - Fixed a bug on Python 3 when creating adhoc ssl contexts, due to `sys.maxint`
314 not being defined.
315 - Fixed a bug on Python 3, that caused
316 :func:`~werkzeug.serving.make_ssl_devcert` to fail with an exception.
317 - Added exceptions for 504 and 505.
318 - Added support for ChromeOS detection.
319 - Added UUID converter to the routing system.
320 - Added message that explains how to quit the server.
321 - Fixed a bug on Python 2, that caused ``len`` for
322 :class:`werkzeug.datastructures.CombinedMultiDict` to crash.
323 - Added support for stdlib pbkdf2 hmac if a compatible digest
324 is found.
325 - Ported testsuite to use ``py.test``.
326 - Minor optimizations to various middlewares (pull requests ``#496`` and
327 ``#571``).
328 - Use stdlib ``ssl`` module instead of ``OpenSSL`` for the builtin server
329 (issue ``#434``). This means that OpenSSL contexts are not supported anymore,
330 but instead ``ssl.SSLContext`` from the stdlib.
331 - Allow protocol-relative URLs when building external URLs.
332 - Fixed Atom syndication to print time zone offset for tz-aware datetime
333 objects (pull request ``#254``).
334 - Improved reloader to track added files and to recover from broken
335 sys.modules setups with syntax errors in packages.
336 - ``cache.RedisCache`` now supports arbitrary ``**kwargs`` for the redis
337 object.
338 - ``werkzeug.test.Client`` now uses the original request method when resolving
339 307 redirects (pull request ``#556``).
340 - ``werkzeug.datastructures.MIMEAccept`` now properly deals with mimetype
341 parameters (pull request ``#205``).
342 - ``werkzeug.datastructures.Accept`` now handles a quality of ``0`` as
343 intolerable, as per RFC 2616 (pull request ``#536``).
344 - ``werkzeug.urls.url_fix`` now properly encodes hostnames with ``idna``
345 encoding (issue ``#559``). It also doesn't crash on malformed URLs anymore
346 (issue ``#582``).
347 - ``werkzeug.routing.MapAdapter.match`` now recognizes the difference between
348 the path ``/`` and an empty one (issue ``#360``).
349 - The interactive debugger now tries to decode non-ascii filenames (issue
350 ``#469``).
351 - Increased default key size of generated SSL certificates to 1024 bits (issue
352 ``#611``).
353 - Added support for specifying a ``Response`` subclass to use when calling
354 :func:`~werkzeug.utils.redirect`\ .
355 - ``werkzeug.test.EnvironBuilder`` now doesn't use the request method anymore
356 to guess the content type, and purely relies on the ``form``, ``files`` and
357 ``input_stream`` properties (issue ``#620``).
358 - Added Symbian to the user agent platform list.
359 - Fixed make_conditional to respect automatically_set_content_length
360 - Unset ``Content-Length`` when writing to response.stream (issue ``#451``)
361 - ``wrappers.Request.method`` is now always uppercase, eliminating
362 inconsistencies of the WSGI environment (issue ``647``).
363 - ``routing.Rule.empty`` now works correctly with subclasses of ``Rule`` (pull
364 request ``#645``).
365 - Made map updating safe in light of concurrent updates.
366 - Allow multiple values for the same field for url building (issue ``#658``).
367
368 Version 0.9.7
369 -------------
370
371 (bugfix release, release date to be decided)
372
373 - Fix unicode problems in ``werkzeug.debug.tbtools``.
374 - Fix Python 3-compatibility problems in ``werkzeug.posixemulation``.
375 - Backport fix of fatal typo for ``ImmutableList`` (issue ``#492``).
376 - Make creation of the cache dir for ``FileSystemCache`` atomic (issue
377 ``#468``).
378 - Use native strings for memcached keys to work with Python 3 client (issue
379 ``#539``).
380 - Fix charset detection for ``werkzeug.debug.tbtools.Frame`` objects (issues
381 ``#547`` and ``#532``).
382 - Fix ``AttributeError`` masking in ``werkzeug.utils.import_string`` (issue
383 ``#182``).
384 - Explicitly shut down server (issue ``#519``).
385 - Fix timeouts greater than 2592000 being misinterpreted as UNIX timestamps in
386 ``werkzeug.contrib.cache.MemcachedCache`` (issue ``#533``).
387 - Fix bug where ``werkzeug.exceptions.abort`` would raise an arbitrary subclass
388 of the expected class (issue ``#422``).
389 - Fix broken ``jsrouting`` (due to removal of ``werkzeug.templates``)
390 - ``werkzeug.urls.url_fix`` now doesn't crash on malformed URLs anymore, but
391 returns them unmodified. This is a cheap workaround for ``#582``, the proper
392 fix is included in version 0.10.
393 - The repr of ``werkzeug.wrappers.Request`` doesn't crash on non-ASCII-values
394 anymore (pull request ``#466``).
395 - Fix bug in ``cache.RedisCache`` when combined with ``redis.StrictRedis``
396 object (pull request ``#583``).
397 - The ``qop`` parameter for ``WWW-Authenticate`` headers is now always quoted,
398 as required by RFC 2617 (issue ``#633``).
399 - Fix bug in ``werkzeug.contrib.cache.SimpleCache`` with Python 3 where add/set
400 may throw an exception when pruning old entries from the cache (pull request
401 ``#651``).
402
403 Version 0.9.6
404 -------------
405
406 (bugfix release, released on June 7th 2014)
407
408 - Added a safe conversion for IRI to URI conversion and use that
409 internally to work around issues with spec violations for
410 protocols such as ``itms-service``.
411
412 Version 0.9.7
413 -------------
414
415 - Fixed uri_to_iri() not re-encoding hashes in query string parameters.
416
417 Version 0.9.5
418 -------------
419
420 (bugfix release, released on June 7th 2014)
421
422 - Forward charset argument from request objects to the environ
423 builder.
424 - Fixed error handling for missing boundaries in multipart data.
425 - Fixed session creation on systems without ``os.urandom()``.
426 - Fixed pluses in dictionary keys not being properly URL encoded.
427 - Fixed a problem with deepcopy not working for multi dicts.
428 - Fixed a double quoting issue on redirects.
429 - Fixed a problem with unicode keys appearing in headers on 2.x.
430 - Fixed a bug with unicode strings in the test builder.
431 - Fixed a unicode bug on Python 3 in the WSGI profiler.
432 - Fixed an issue with the safe string compare function on
433 Python 2.7.7 and Python 3.4.
434
435 Version 0.9.4
436 -------------
437
438 (bugfix release, released on August 26th 2013)
439
440 - Fixed an issue with Python 3.3 and an edge case in cookie parsing.
441 - Fixed decoding errors not handled properly through the WSGI
442 decoding dance.
443 - Fixed URI to IRI conversion incorrectly decoding percent signs.
444
445 Version 0.9.3
446 -------------
447
448 (bugfix release, released on July 25th 2013)
449
450 - Restored behavior of the ``data`` descriptor of the request class to pre 0.9
451 behavior. This now also means that ``.data`` and ``.get_data()`` have
452 different behavior. New code should use ``.get_data()`` always.
453
454 In addition to that there is now a flag for the ``.get_data()`` method that
455 controls what should happen with form data parsing and the form parser will
456 honor cached data. This makes dealing with custom form data more consistent.
457
458 Version 0.9.2
459 -------------
460
461 (bugfix release, released on July 18th 2013)
462
463 - Added `unsafe` parameter to :func:`~werkzeug.urls.url_quote`.
464 - Fixed an issue with :func:`~werkzeug.urls.url_quote_plus` not quoting
465 `'+'` correctly.
466 - Ported remaining parts of :class:`~werkzeug.contrib.RedisCache` to
467 Python 3.3.
468 - Ported remaining parts of :class:`~werkzeug.contrib.MemcachedCache` to
469 Python 3.3
470 - Fixed a deprecation warning in the contrib atom module.
471 - Fixed a regression with setting of content types through the
472 headers dictionary instead with the content type parameter.
473 - Use correct name for stdlib secure string comparison function.
474 - Fixed a wrong reference in the docstring of
475 :func:`~werkzeug.local.release_local`.
476 - Fixed an `AttributeError` that sometimes occurred when accessing the
477 :attr:`werkzeug.wrappers.BaseResponse.is_streamed` attribute.
478
479 Version 0.9.1
480 -------------
481
482 (bugfix release, released on June 14th 2013)
483
484 - Fixed an issue with integers no longer being accepted in certain
485 parts of the routing system or URL quoting functions.
486 - Fixed an issue with `url_quote` not producing the right escape
487 codes for single digit codepoints.
488 - Fixed an issue with :class:`~werkzeug.wsgi.SharedDataMiddleware` not
489 reading the path correctly and breaking on etag generation in some
490 cases.
491 - Properly handle `Expect: 100-continue` in the development server
492 to resolve issues with curl.
493 - Automatically exhaust the input stream on request close. This should
494 fix issues where not touching request files results in a timeout.
495 - Fixed exhausting of streams not doing anything if a non-limited
496 stream was passed into the multipart parser.
497 - Raised the buffer sizes for the multipart parser.
498
499 Version 0.9
500 -----------
501
502 Released on June 13nd 2013, codename Planierraupe.
503
504 - Added support for :meth:`~werkzeug.wsgi.LimitedStream.tell`
505 on the limited stream.
506 - :class:`~werkzeug.datastructures.ETags` now is nonzero if it
507 contains at least one etag of any kind, including weak ones.
508 - Added a workaround for a bug in the stdlib for SSL servers.
509 - Improved SSL interface of the devserver so that it can generate
510 certificates easily and load them from files.
511 - Refactored test client to invoke the open method on the class
512 for redirects. This makes subclassing more powerful.
513 - :func:`werkzeug.wsgi.make_chunk_iter` and
514 :func:`werkzeug.wsgi.make_line_iter` now support processing of
515 iterators and streams.
516 - URL generation by the routing system now no longer quotes
517 ``+``.
518 - URL fixing now no longer quotes certain reserved characters.
519 - The :func:`werkzeug.security.generate_password_hash` and
520 check functions now support any of the hashlib algorithms.
521 - `wsgi.get_current_url` is now ascii safe for browsers sending
522 non-ascii data in query strings.
523 - improved parsing behavior for :func:`werkzeug.http.parse_options_header`
524 - added more operators to local proxies.
525 - added a hook to override the default converter in the routing
526 system.
527 - The description field of HTTP exceptions is now always escaped.
528 Use markup objects to disable that.
529 - Added number of proxy argument to the proxy fix to make it more
530 secure out of the box on common proxy setups. It will by default
531 no longer trust the x-forwarded-for header as much as it did
532 before.
533 - Added support for fragment handling in URI/IRI functions.
534 - Added custom class support for :func:`werkzeug.http.parse_dict_header`.
535 - Renamed `LighttpdCGIRootFix` to `CGIRootFix`.
536 - Always treat `+` as safe when fixing URLs as people love misusing them.
537 - Added support to profiling into directories in the contrib profiler.
538 - The escape function now by default escapes quotes.
539 - Changed repr of exceptions to be less magical.
540 - Simplified exception interface to no longer require environments
541 to be passed to receive the response object.
542 - Added sentinel argument to IterIO objects.
543 - Added pbkdf2 support for the security module.
544 - Added a plain request type that disables all form parsing to only
545 leave the stream behind.
546 - Removed support for deprecated `fix_headers`.
547 - Removed support for deprecated `header_list`.
548 - Removed support for deprecated parameter for `iter_encoded`.
549 - Removed support for deprecated non-silent usage of the limited
550 stream object.
551 - Removed support for previous dummy `writable` parameter on
552 the cached property.
553 - Added support for explicitly closing request objects to close
554 associated resources.
555 - Conditional request handling or access to the data property on responses no
556 longer ignores direct passthrough mode.
557 - Removed werkzeug.templates and werkzeug.contrib.kickstart.
558 - Changed host lookup logic for forwarded hosts to allow lists of
559 hosts in which case only the first one is picked up.
560 - Added `wsgi.get_query_string`, `wsgi.get_path_info` and
561 `wsgi.get_script_name` and made the `wsgi.pop_path_info` and
562 `wsgi.peek_path_info` functions perform unicode decoding. This
563 was necessary to avoid having to expose the WSGI encoding dance
564 on Python 3.
565 - Added `content_encoding` and `content_md5` to the request object's
566 common request descriptor mixin.
567 - added `options` and `trace` to the test client.
568 - Overhauled the utilization of the input stream to be easier to use
569 and better to extend. The detection of content payload on the input
570 side is now more compliant with HTTP by detecting off the content
571 type header instead of the request method. This also now means that
572 the stream property on the request class is always available instead
573 of just when the parsing fails.
574 - Added support for using :class:`werkzeug.wrappers.BaseResponse` in a with
575 statement.
576 - Changed `get_app_iter` to fetch the response early so that it does not
577 fail when wrapping a response iterable. This makes filtering easier.
578 - Introduced `get_data` and `set_data` methods for responses.
579 - Introduced `get_data` for requests.
580 - Soft deprecated the `data` descriptors for request and response objects.
581 - Added `as_bytes` operations to some of the headers to simplify working
582 with things like cookies.
583 - Made the debugger paste tracebacks into github's gist service as
584 private pastes.
585
586 Version 0.8.4
587 -------------
588
589 (bugfix release, release date to be announced)
590
591 - Added a favicon to the debugger which fixes problem with
592 state changes being triggered through a request to
593 /favicon.ico in Google Chrome. This should fix some
594 problems with Flask and other frameworks that use
595 context local objects on a stack with context preservation
596 on errors.
597 - Fixed an issue with scrolling up in the debugger.
598 - Fixed an issue with debuggers running on a different URL
599 than the URL root.
600 - Fixed a problem with proxies not forwarding some rarely
601 used special methods properly.
602 - Added a workaround to prevent the XSS protection from Chrome
603 breaking the debugger.
604 - Skip redis tests if redis is not running.
605 - Fixed a typo in the multipart parser that caused content-type
606 to not be picked up properly.
607
608 Version 0.8.3
609 -------------
610
611 (bugfix release, released on February 5th 2012)
612
613 - Fixed another issue with :func:`werkzeug.wsgi.make_line_iter`
614 where lines longer than the buffer size were not handled
615 properly.
616 - Restore stdout after debug console finished executing so
617 that the debugger can be used on GAE better.
618 - Fixed a bug with the redis cache for int subclasses
619 (affects bool caching).
620 - Fixed an XSS problem with redirect targets coming from
621 untrusted sources.
622 - Redis cache backend now supports password authentication.
623
624 Version 0.8.2
625 -------------
626
627 (bugfix release, released on December 16th 2011)
628
629 - Fixed a problem with request handling of the builtin server
630 not responding to socket errors properly.
631 - The routing request redirect exception's code attribute is now
632 used properly.
633 - Fixed a bug with shutdowns on Windows.
634 - Fixed a few unicode issues with non-ascii characters being
635 hardcoded in URL rules.
636 - Fixed two property docstrings being assigned to fdel instead
637 of ``__doc__``.
638 - Fixed an issue where CRLF line endings could be split into two
639 by the line iter function, causing problems with multipart file
640 uploads.
641
642 Version 0.8.1
643 -------------
644
645 (bugfix release, released on September 30th 2011)
646
647 - Fixed an issue with the memcache not working properly.
648 - Fixed an issue for Python 2.7.1 and higher that broke
649 copying of multidicts with :func:`copy.copy`.
650 - Changed hashing methodology of immutable ordered multi dicts
651 for a potential problem with alternative Python implementations.
652
653 Version 0.8
654 -----------
655
656 Released on September 29th 2011, codename Lötkolben
657
658 - Removed data structure specific KeyErrors for a general
659 purpose :exc:`~werkzeug.exceptions.BadRequestKeyError`.
660 - Documented :meth:`werkzeug.wrappers.BaseRequest._load_form_data`.
661 - The routing system now also accepts strings instead of
662 dictionaries for the `query_args` parameter since we're only
663 passing them through for redirects.
664 - Werkzeug now automatically sets the content length immediately when
665 the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute is set
666 for efficiency and simplicity reasons.
667 - The routing system will now normalize server names to lowercase.
668 - The routing system will no longer raise ValueErrors in case the
669 configuration for the server name was incorrect. This should make
670 deployment much easier because you can ignore that factor now.
671 - Fixed a bug with parsing HTTP digest headers. It rejected headers
672 with missing nc and nonce params.
673 - Proxy fix now also updates wsgi.url_scheme based on X-Forwarded-Proto.
674 - Added support for key prefixes to the redis cache.
675 - Added the ability to suppress some auto corrections in the wrappers
676 that are now controlled via `autocorrect_location_header` and
677 `automatically_set_content_length` on the response objects.
678 - Werkzeug now uses a new method to check that the length of incoming
679 data is complete and will raise IO errors by itself if the server
680 fails to do so.
681 - :func:`~werkzeug.wsgi.make_line_iter` now requires a limit that is
682 not higher than the length the stream can provide.
683 - Refactored form parsing into a form parser class that makes it possible
684 to hook into individual parts of the parsing process for debugging and
685 extending.
686 - For conditional responses the content length is no longer set when it
687 is already there and added if missing.
688 - Immutable datastructures are hashable now.
689 - Headers datastructure no longer allows newlines in values to avoid
690 header injection attacks.
691 - Made it possible through subclassing to select a different remote
692 addr in the proxy fix.
693 - Added stream based URL decoding. This reduces memory usage on large
694 transmitted form data that is URL decoded since Werkzeug will no longer
695 load all the unparsed data into memory.
696 - Memcache client now no longer uses the buggy cmemcache module and
697 supports pylibmc. GAE is not tried automatically and the dedicated
698 class is no longer necessary.
699 - Redis cache now properly serializes data.
700 - Removed support for Python 2.4
701
702 Version 0.7.2
703 -------------
704
705 (bugfix release, released on September 30th 2011)
706
707 - Fixed a CSRF problem with the debugger.
708 - The debugger is now generating private pastes on lodgeit.
709 - If URL maps are now bound to environments the query arguments
710 are properly decoded from it for redirects.
711
712 Version 0.7.1
713 -------------
714
715 (bugfix release, released on July 26th 2011)
716
717 - Fixed a problem with newer versions of IPython.
718 - Disabled pyinotify based reloader which does not work reliably.
719
720 Version 0.7
721 -----------
722
723 Released on July 24th 2011, codename Schraubschlüssel
724
725 - Add support for python-libmemcached to the Werkzeug cache abstraction
726 layer.
727 - Improved :func:`url_decode` and :func:`url_encode` performance.
728 - Fixed an issue where the SharedDataMiddleware could cause an
729 internal server error on weird paths when loading via pkg_resources.
730 - Fixed an URL generation bug that caused URLs to be invalid if a
731 generated component contains a colon.
732 - :func:`werkzeug.import_string` now works with partially set up
733 packages properly.
734 - Disabled automatic socket switching for IPv6 on the development
735 server due to problems it caused.
736 - Werkzeug no longer overrides the Date header when creating a
737 conditional HTTP response.
738 - The routing system provides a method to retrieve the matching
739 methods for a given path.
740 - The routing system now accepts a parameter to change the encoding
741 error behaviour.
742 - The local manager can now accept custom ident functions in the
743 constructor that are forwarded to the wrapped local objects.
744 - url_unquote_plus now accepts unicode strings again.
745 - Fixed an issue with the filesystem session support's prune
746 function and concurrent usage.
747 - Fixed a problem with external URL generation discarding the port.
748 - Added support for pylibmc to the Werkzeug cache abstraction layer.
749 - Fixed an issue with the new multipart parser that happened when
750 a linebreak happened to be on the chunk limit.
751 - Cookies are now set properly if ports are in use. A runtime error
752 is raised if one tries to set a cookie for a domain without a dot.
753 - Fixed an issue with Template.from_file not working for file
754 descriptors.
755 - Reloader can now use inotify to track reloads. This requires the
756 pyinotify library to be installed.
757 - Werkzeug debugger can now submit to custom lodgeit installations.
758 - redirect function's status code assertion now allows 201 to be used
759 as redirection code. While it's not a real redirect, it shares
760 enough with redirects for the function to still be useful.
761 - Fixed securecookie for pypy.
762 - Fixed `ValueErrors` being raised on calls to `best_match` on
763 `MIMEAccept` objects when invalid user data was supplied.
764 - Deprecated `werkzeug.contrib.kickstart` and `werkzeug.contrib.testtools`
765 - URL routing now can be passed the URL arguments to keep them for
766 redirects. In the future matching on URL arguments might also be
767 possible.
768 - Header encoding changed from utf-8 to latin1 to support a port to
769 Python 3. Bytestrings passed to the object stay untouched which
770 makes it possible to have utf-8 cookies. This is a part where
771 the Python 3 version will later change in that it will always
772 operate on latin1 values.
773 - Fixed a bug in the form parser that caused the last character to
774 be dropped off if certain values in multipart data are used.
775 - Multipart parser now looks at the part-individual content type
776 header to override the global charset.
777 - Introduced mimetype and mimetype_params attribute for the file
778 storage object.
779 - Changed FileStorage filename fallback logic to skip special filenames
780 that Python uses for marking special files like stdin.
781 - Introduced more HTTP exception classes.
782 - `call_on_close` now can be used as a decorator.
783 - Support for redis as cache backend.
784 - Added `BaseRequest.scheme`.
785 - Support for the RFC 5789 PATCH method.
786 - New custom routing parser and better ordering.
787 - Removed support for `is_behind_proxy`. Use a WSGI middleware
788 instead that rewrites the `REMOTE_ADDR` according to your setup.
789 Also see the :class:`werkzeug.contrib.fixers.ProxyFix` for
790 a drop-in replacement.
791 - Added cookie forging support to the test client.
792 - Added support for host based matching in the routing system.
793 - Switched from the default 'ignore' to the better 'replace'
794 unicode error handling mode.
795 - The builtin server now adds a function named 'werkzeug.server.shutdown'
796 into the WSGI env to initiate a shutdown. This currently only works
797 in Python 2.6 and later.
798 - Headers are now assumed to be latin1 for better compatibility with
799 Python 3 once we have support.
800 - Added :func:`werkzeug.security.safe_join`.
801 - Added `accept_json` property analogous to `accept_html` on the
802 :class:`werkzeug.datastructures.MIMEAccept`.
803 - :func:`werkzeug.utils.import_string` now fails with much better
804 error messages that pinpoint to the problem.
805 - Added support for parsing of the `If-Range` header
806 (:func:`werkzeug.http.parse_if_range_header` and
807 :class:`werkzeug.datastructures.IfRange`).
808 - Added support for parsing of the `Range` header
809 (:func:`werkzeug.http.parse_range_header` and
810 :class:`werkzeug.datastructures.Range`).
811 - Added support for parsing of the `Content-Range` header of responses
812 and provided an accessor object for it
813 (:func:`werkzeug.http.parse_content_range_header` and
814 :class:`werkzeug.datastructures.ContentRange`).
815
816 Version 0.6.2
817 -------------
818
819 (bugfix release, released on April 23th 2010)
820
821 - renamed the attribute `implicit_seqence_conversion` attribute of the
822 request object to `implicit_sequence_conversion`.
823
824 Version 0.6.1
825 -------------
826
827 (bugfix release, released on April 13th 2010)
828
829 - heavily improved local objects. Should pick up standalone greenlet
830 builds now and support proxies to free callables as well. There is
831 also a stacked local now that makes it possible to invoke the same
832 application from within itself by pushing current request/response
833 on top of the stack.
834 - routing build method will also build non-default method rules properly
835 if no method is provided.
836 - added proper IPv6 support for the builtin server.
837 - windows specific filesystem session store fixes.
838 (should now be more stable under high concurrency)
839 - fixed a `NameError` in the session system.
840 - fixed a bug with empty arguments in the werkzeug.script system.
841 - fixed a bug where log lines will be duplicated if an application uses
842 :meth:`logging.basicConfig` (#499)
843 - added secure password hashing and checking functions.
844 - `HEAD` is now implicitly added as method in the routing system if
845 `GET` is present. Not doing that was considered a bug because often
846 code assumed that this is the case and in web servers that do not
847 normalize `HEAD` to `GET` this could break `HEAD` requests.
848 - the script support can start SSL servers now.
849
850 Version 0.6
851 -----------
852
853 Released on Feb 19th 2010, codename Hammer.
854
855 - removed pending deprecations
856 - sys.path is now printed from the testapp.
857 - fixed an RFC 2068 incompatibility with cookie value quoting.
858 - the :class:`FileStorage` now gives access to the multipart headers.
859 - `cached_property.writeable` has been deprecated.
860 - :meth:`MapAdapter.match` now accepts a `return_rule` keyword argument
861 that returns the matched `Rule` instead of just the `endpoint`
862 - :meth:`routing.Map.bind_to_environ` raises a more correct error message
863 now if the map was bound to an invalid WSGI environment.
864 - added support for SSL to the builtin development server.
865 - Response objects are no longer modified in place when they are evaluated
866 as WSGI applications. For backwards compatibility the `fix_headers`
867 function is still called in case it was overridden.
868 You should however change your application to use `get_wsgi_headers` if
869 you need header modifications before responses are sent as the backwards
870 compatibility support will go away in future versions.
871 - :func:`append_slash_redirect` no longer requires the QUERY_STRING to be
872 in the WSGI environment.
873 - added :class:`~werkzeug.contrib.wrappers.DynamicCharsetResponseMixin`
874 - added :class:`~werkzeug.contrib.wrappers.DynamicCharsetRequestMixin`
875 - added :attr:`BaseRequest.url_charset`
876 - request and response objects have a default `__repr__` now.
877 - builtin data structures can be pickled now.
878 - the form data parser will now look at the filename instead the
879 content type to figure out if it should treat the upload as regular
880 form data or file upload. This fixes a bug with Google Chrome.
881 - improved performance of `make_line_iter` and the multipart parser
882 for binary uploads.
883 - fixed :attr:`~werkzeug.BaseResponse.is_streamed`
884 - fixed a path quoting bug in `EnvironBuilder` that caused PATH_INFO and
885 SCRIPT_NAME to end up in the environ unquoted.
886 - :meth:`werkzeug.BaseResponse.freeze` now sets the content length.
887 - for unknown HTTP methods the request stream is now always limited
888 instead of being empty. This makes it easier to implement DAV
889 and other protocols on top of Werkzeug.
890 - added :meth:`werkzeug.MIMEAccept.best_match`
891 - multi-value test-client posts from a standard dictionary are now
892 supported. Previously you had to use a multi dict.
893 - rule templates properly work with submounts, subdomains and
894 other rule factories now.
895 - deprecated non-silent usage of the :class:`werkzeug.LimitedStream`.
896 - added support for IRI handling to many parts of Werkzeug.
897 - development server properly logs to the werkzeug logger now.
898 - added :func:`werkzeug.extract_path_info`
899 - fixed a querystring quoting bug in :func:`url_fix`
900 - added `fallback_mimetype` to :class:`werkzeug.SharedDataMiddleware`.
901 - deprecated :meth:`BaseResponse.iter_encoded`'s charset parameter.
902 - added :meth:`BaseResponse.make_sequence`,
903 :attr:`BaseResponse.is_sequence` and
904 :meth:`BaseResponse._ensure_sequence`.
905 - added better __repr__ of :class:`werkzeug.Map`
906 - `import_string` accepts unicode strings as well now.
907 - development server doesn't break on double slashes after the host name.
908 - better `__repr__` and `__str__` of
909 :exc:`werkzeug.exceptions.HTTPException`
910 - test client works correctly with multiple cookies now.
911 - the :class:`werkzeug.routing.Map` now has a class attribute with
912 the default converter mapping. This helps subclasses to override
913 the converters without passing them to the constructor.
914 - implemented :class:`OrderedMultiDict`
915 - improved the session support for more efficient session storing
916 on the filesystem. Also added support for listing of sessions
917 currently stored in the filesystem session store.
918 - werkzeug no longer utilizes the Python time module for parsing
919 which means that dates in a broader range can be parsed.
920 - the wrappers have no class attributes that make it possible to
921 swap out the dict and list types it uses.
922 - werkzeug debugger should work on the appengine dev server now.
923 - the URL builder supports dropping of unexpected arguments now.
924 Previously they were always appended to the URL as query string.
925 - profiler now writes to the correct stream.
926
927 Version 0.5.1
928 -------------
929 (bugfix release for 0.5, released on July 9th 2009)
930
931 - fixed boolean check of :class:`FileStorage`
932 - url routing system properly supports unicode URL rules now.
933 - file upload streams no longer have to provide a truncate()
934 method.
935 - implemented :meth:`BaseRequest._form_parsing_failed`.
936 - fixed #394
937 - :meth:`ImmutableDict.copy`, :meth:`ImmutableMultiDict.copy` and
938 :meth:`ImmutableTypeConversionDict.copy` return mutable shallow
939 copies.
940 - fixed a bug with the `make_runserver` script action.
941 - :meth:`MultiDict.items` and :meth:`MutiDict.iteritems` now accept an
942 argument to return a pair for each value of each key.
943 - the multipart parser works better with hand-crafted multipart
944 requests now that have extra newlines added. This fixes a bug
945 with setuptools uploads not handled properly (#390)
946 - fixed some minor bugs in the atom feed generator.
947 - fixed a bug with client cookie header parsing being case sensitive.
948 - fixed a not-working deprecation warning.
949 - fixed package loading for :class:`SharedDataMiddleware`.
950 - fixed a bug in the secure cookie that made server-side expiration
951 on servers with a local time that was not set to UTC impossible.
952 - fixed console of the interactive debugger.
953
954
955 Version 0.5
956 -----------
957
958 Released on April 24th, codename Schlagbohrer.
959
960 - requires Python 2.4 now
961 - fixed a bug in :class:`~contrib.IterIO`
962 - added :class:`MIMEAccept` and :class:`CharsetAccept` that work like the
963 regular :class:`Accept` but have extra special normalization for mimetypes
964 and charsets and extra convenience methods.
965 - switched the serving system from wsgiref to something homebrew.
966 - the :class:`Client` now supports cookies.
967 - added the :mod:`~werkzeug.contrib.fixers` module with various
968 fixes for webserver bugs and hosting setup side-effects.
969 - added :mod:`werkzeug.contrib.wrappers`
970 - added :func:`is_hop_by_hop_header`
971 - added :func:`is_entity_header`
972 - added :func:`remove_hop_by_hop_headers`
973 - added :func:`pop_path_info`
974 - added :func:`peek_path_info`
975 - added :func:`wrap_file` and :class:`FileWrapper`
976 - moved `LimitedStream` from the contrib package into the regular
977 werkzeug one and changed the default behavior to raise exceptions
978 rather than stopping without warning. The old class will stick in
979 the module until 0.6.
980 - implemented experimental multipart parser that replaces the old CGI hack.
981 - added :func:`dump_options_header` and :func:`parse_options_header`
982 - added :func:`quote_header_value` and :func:`unquote_header_value`
983 - :func:`url_encode` and :func:`url_decode` now accept a separator
984 argument to switch between `&` and `;` as pair separator. The magic
985 switch is no longer in place.
986 - all form data parsing functions as well as the :class:`BaseRequest`
987 object have parameters (or attributes) to limit the number of
988 incoming bytes (either totally or per field).
989 - added :class:`LanguageAccept`
990 - request objects are now enforced to be read only for all collections.
991 - added many new collection classes, refactored collections in general.
992 - test support was refactored, semi-undocumented `werkzeug.test.File`
993 was replaced by :class:`werkzeug.FileStorage`.
994 - :class:`EnvironBuilder` was added and unifies the previous distinct
995 :func:`create_environ`, :class:`Client` and
996 :meth:`BaseRequest.from_values`. They all work the same now which
997 is less confusing.
998 - officially documented imports from the internal modules as undefined
999 behavior. These modules were never exposed as public interfaces.
1000 - removed `FileStorage.__len__` which previously made the object
1001 falsy for browsers not sending the content length which all browsers
1002 do.
1003 - :class:`SharedDataMiddleware` uses `wrap_file` now and has a
1004 configurable cache timeout.
1005 - added :class:`CommonRequestDescriptorsMixin`
1006 - added :attr:`CommonResponseDescriptorsMixin.mimetype_params`
1007 - added :mod:`werkzeug.contrib.lint`
1008 - added `passthrough_errors` to `run_simple`.
1009 - added `secure_filename`
1010 - added :func:`make_line_iter`
1011 - :class:`MultiDict` copies now instead of revealing internal
1012 lists to the caller for `getlist` and iteration functions that
1013 return lists.
1014 - added :attr:`follow_redirect` to the :func:`open` of :class:`Client`.
1015 - added support for `extra_files` in
1016 :func:`~werkzeug.script.make_runserver`
1017
1018 Version 0.4.1
1019 -------------
1020
1021 (Bugfix release, released on January 11th 2009)
1022
1023 - `werkzeug.contrib.cache.Memcached` accepts now objects that
1024 implement the memcache.Client interface as alternative to a list of
1025 strings with server addresses.
1026 There is also now a `GAEMemcachedCache` that connects to the Google
1027 appengine cache.
1028 - explicitly convert secret keys to bytestrings now because Python
1029 2.6 no longer does that.
1030 - `url_encode` and all interfaces that call it, support ordering of
1031 options now which however is disabled by default.
1032 - the development server no longer resolves the addresses of clients.
1033 - Fixed a typo in `werkzeug.test` that broke `File`.
1034 - `Map.bind_to_environ` uses the `Host` header now if available.
1035 - Fixed `BaseCache.get_dict` (#345)
1036 - `werkzeug.test.Client` can now run the application buffered in which
1037 case the application is properly closed automatically.
1038 - Fixed `Headers.set` (#354). Caused header duplication before.
1039 - Fixed `Headers.pop` (#349). default parameter was not properly
1040 handled.
1041 - Fixed UnboundLocalError in `create_environ` (#351)
1042 - `Headers` is more compatible with wsgiref now.
1043 - `Template.render` accepts multidicts now.
1044 - dropped support for Python 2.3
1045
1046 Version 0.4
1047 -----------
1048
1049 Released on November 23rd 2008, codename Schraubenzieher.
1050
1051 - `Client` supports an empty `data` argument now.
1052 - fixed a bug in `Response.application` that made it impossible to use it
1053 as method decorator.
1054 - the session system should work on appengine now
1055 - the secure cookie works properly in load balanced environments with
1056 different cpu architectures now.
1057 - `CacheControl.no_cache` and `CacheControl.private` behavior changed to
1058 reflect the possibilities of the HTTP RFC. Setting these attributes to
1059 `None` or `True` now sets the value to "the empty value".
1060 More details in the documentation.
1061 - fixed `werkzeug.contrib.atom.AtomFeed.__call__`. (#338)
1062 - `BaseResponse.make_conditional` now always returns `self`. Previously
1063 it didn't for post requests and such.
1064 - fixed a bug in boolean attribute handling of `html` and `xhtml`.
1065 - added graceful error handling to the debugger pastebin feature.
1066 - added a more list like interface to `Headers` (slicing and indexing
1067 works now)
1068 - fixed a bug with the `__setitem__` method of `Headers` that didn't
1069 properly remove all keys on replacing.
1070 - added `remove_entity_headers` which removes all entity headers from
1071 a list of headers (or a `Headers` object)
1072 - the responses now automatically call `remove_entity_headers` if the
1073 status code is 304.
1074 - fixed a bug with `Href` query parameter handling. Previously the last
1075 item of a call to `Href` was not handled properly if it was a dict.
1076 - headers now support a `pop` operation to better work with environ
1077 properties.
1078
1079
1080 Version 0.3.1
1081 -------------
1082
1083 (bugfix release, released on June 24th 2008)
1084
1085 - fixed a security problem with `werkzeug.contrib.SecureCookie`.
1086 More details available in the `release announcement`_.
1087
1088 .. _release announcement: http://lucumr.pocoo.org/cogitations/2008/06/24/werkzeug-031-released/
1089
1090 Version 0.3
1091 -----------
1092
1093 Released on June 14th 2008, codename EUR325CAT6.
1094
1095 - added support for redirecting in url routing.
1096 - added `Authorization` and `AuthorizationMixin`
1097 - added `WWWAuthenticate` and `WWWAuthenticateMixin`
1098 - added `parse_list_header`
1099 - added `parse_dict_header`
1100 - added `parse_authorization_header`
1101 - added `parse_www_authenticate_header`
1102 - added `_get_current_object` method to `LocalProxy` objects
1103 - added `parse_form_data`
1104 - `MultiDict`, `CombinedMultiDict`, `Headers`, and `EnvironHeaders` raise
1105 special key errors now that are subclasses of `BadRequest` so if you
1106 don't catch them they give meaningful HTTP responses.
1107 - added support for alternative encoding error handling and the new
1108 `HTTPUnicodeError` which (if not caught) behaves like a `BadRequest`.
1109 - added `BadRequest.wrap`.
1110 - added ETag support to the SharedDataMiddleware and added an option
1111 to disable caching.
1112 - fixed `is_xhr` on the request objects.
1113 - fixed error handling of the url adapter's `dispatch` method. (#318)
1114 - fixed bug with `SharedDataMiddleware`.
1115 - fixed `Accept.values`.
1116 - `EnvironHeaders` contain content-type and content-length now
1117 - `url_encode` treats lists and tuples in dicts passed to it as multiple
1118 values for the same key so that one doesn't have to pass a `MultiDict`
1119 to the function.
1120 - added `validate_arguments`
1121 - added `BaseRequest.application`
1122 - improved Python 2.3 support
1123 - `run_simple` accepts `use_debugger` and `use_evalex` parameters now,
1124 like the `make_runserver` factory function from the script module.
1125 - the `environ_property` is now read-only by default
1126 - it's now possible to initialize requests as "shallow" requests which
1127 causes runtime errors if the request object tries to consume the
1128 input stream.
1129
1130
1131 Version 0.2
1132 -----------
1133
1134 Released Feb 14th 2008, codename Faustkeil.
1135
1136 - Added `AnyConverter` to the routing system.
1137 - Added `werkzeug.contrib.securecookie`
1138 - Exceptions have a ``get_response()`` method that return a response object
1139 - fixed the path ordering bug (#293), thanks Thomas Johansson
1140 - `BaseReporterStream` is now part of the werkzeug contrib module. From
1141 Werkzeug 0.3 onwards you will have to import it from there.
1142 - added `DispatcherMiddleware`.
1143 - `RequestRedirect` is now a subclass of `HTTPException` and uses a
1144 301 status code instead of 302.
1145 - `url_encode` and `url_decode` can optionally treat keys as unicode strings
1146 now, too.
1147 - `werkzeug.script` has a different caller format for boolean arguments now.
1148 - renamed `lazy_property` to `cached_property`.
1149 - added `import_string`.
1150 - added is_* properties to request objects.
1151 - added `empty()` method to routing rules.
1152 - added `werkzeug.contrib.profiler`.
1153 - added `extends` to `Headers`.
1154 - added `dump_cookie` and `parse_cookie`.
1155 - added `as_tuple` to the `Client`.
1156 - added `werkzeug.contrib.testtools`.
1157 - added `werkzeug.unescape`
1158 - added `BaseResponse.freeze`
1159 - added `werkzeug.contrib.atom`
1160 - the HTTPExceptions accept an argument `description` now which overrides the
1161 default description.
1162 - the `MapAdapter` has a default for path info now. If you use
1163 `bind_to_environ` you don't have to pass the path later.
1164 - the wsgiref subclass werkzeug uses for the dev server does not use direct
1165 sys.stderr logging any more but a logger called "werkzeug".
1166 - implemented `Href`.
1167 - implemented `find_modules`
1168 - refactored request and response objects into base objects, mixins and
1169 full featured subclasses that implement all mixins.
1170 - added simple user agent parser
1171 - werkzeug's routing raises `MethodNotAllowed` now if it matches a
1172 rule but for a different method.
1173 - many fixes and small improvements
1174
1175
1176 Version 0.1
1177 -----------
1178
1179 Released on Dec 9th 2007, codename Wictorinoxger.
1180
1181 - Initial release
0 Werkzeug Changelog
1 ==================
2
3
4 Version 0.13
5 ------------
6
7 Released on December 7th 2017
8
9 - **Deprecate support for Python 2.6 and 3.3.** CI tests will not run
10 for these versions, and support will be dropped completely in the next
11 version. (`pallets/meta#24`_)
12 - Raise ``TypeError`` when port is not an integer. (`#1088`_)
13 - Fully deprecate ``werkzeug.script``. Use `Click`_ instead. (`#1090`_)
14 - ``response.age`` is parsed as a ``timedelta``. Previously, it was
15 incorrectly treated as a ``datetime``. The header value is an integer
16 number of seconds, not a date string. (`#414`_)
17 - Fix a bug in ``TypeConversionDict`` where errors are not propagated
18 when using the converter. (`#1102`_)
19 - ``Authorization.qop`` is a string instead of a set, to comply with
20 RFC 2617. (`#984`_)
21 - An exception is raised when an encoded cookie is larger than, by
22 default, 4093 bytes. Browsers may silently ignore cookies larger than
23 this. ``BaseResponse`` has a new attribute ``max_cookie_size`` and
24 ``dump_cookie`` has a new argument ``max_size`` to configure this.
25 (`#780`_, `#1109`_)
26 - Fix a TypeError in ``werkzeug.contrib.lint.GuardedIterator.close``.
27 (`#1116`_)
28 - ``BaseResponse.calculate_content_length`` now correctly works for
29 Unicode responses on Python 3. It first encodes using
30 ``iter_encoded``. (`#705`_)
31 - Secure cookie contrib works with string secret key on Python 3.
32 (`#1205`_)
33 - Shared data middleware accepts a list instead of a dict of static
34 locations to preserve lookup order. (`#1197`_)
35 - HTTP header values without encoding can contain single quotes.
36 (`#1208`_)
37 - The built-in dev server supports receiving requests with chunked
38 transfer encoding. (`#1198`_)
39
40 .. _Click: https://www.palletsprojects.com/p/click/
41 .. _pallets/meta#24: https://github.com/pallets/meta/issues/24
42 .. _#414: https://github.com/pallets/werkzeug/pull/414
43 .. _#705: https://github.com/pallets/werkzeug/pull/705
44 .. _#780: https://github.com/pallets/werkzeug/pull/780
45 .. _#984: https://github.com/pallets/werkzeug/pull/984
46 .. _#1088: https://github.com/pallets/werkzeug/pull/1088
47 .. _#1090: https://github.com/pallets/werkzeug/pull/1090
48 .. _#1102: https://github.com/pallets/werkzeug/pull/1102
49 .. _#1109: https://github.com/pallets/werkzeug/pull/1109
50 .. _#1116: https://github.com/pallets/werkzeug/pull/1116
51 .. _#1197: https://github.com/pallets/werkzeug/pull/1197
52 .. _#1198: https://github.com/pallets/werkzeug/pull/1198
53 .. _#1205: https://github.com/pallets/werkzeug/pull/1205
54 .. _#1208: https://github.com/pallets/werkzeug/pull/1208
55
56
57 Version 0.12.2
58 --------------
59
60 Released on May 16 2017
61
62 - Fix regression: Pull request ``#892`` prevented Werkzeug from correctly
63 logging the IP of a remote client behind a reverse proxy, even when using
64 `ProxyFix`.
65 - Fix a bug in `safe_join` on Windows.
66
67 Version 0.12.1
68 --------------
69
70 Released on March 15th 2017
71
72 - Fix crash of reloader (used on debug mode) on Windows.
73 (`OSError: [WinError 10038]`). See pull request ``#1081``
74 - Partially revert change to class hierarchy of `Headers`. See ``#1084``.
75
76 Version 0.12
77 ------------
78
79 Released on March 10th 2017
80
81 - Spit out big deprecation warnings for werkzeug.script
82 - Use `inspect.getfullargspec` internally when available as
83 `inspect.getargspec` is gone in 3.6
84 - Added support for status code 451 and 423
85 - Improved the build error suggestions. In particular only if
86 someone stringifies the error will the suggestions be calculated.
87 - Added support for uWSGI's caching backend.
88 - Fix a bug where iterating over a `FileStorage` would result in an infinite
89 loop.
90 - Datastructures now inherit from the relevant baseclasses from the
91 `collections` module in the stdlib. See #794.
92 - Add support for recognizing NetBSD, OpenBSD, FreeBSD, DragonFlyBSD platforms
93 in the user agent string.
94 - Recognize SeaMonkey browser name and version correctly
95 - Recognize Baiduspider, and bingbot user agents
96 - If `LocalProxy`'s wrapped object is a function, refer to it with __wrapped__
97 attribute.
98 - The defaults of ``generate_password_hash`` have been changed to more secure
99 ones, see pull request ``#753``.
100 - Add support for encoding in options header parsing, see pull request
101 ``#933``.
102 - ``test.Client`` now properly handles Location headers with relative URLs, see
103 pull request ``#879``.
104 - When `HTTPException` is raised, it now prints the description, for easier
105 debugging.
106 - Werkzeug's dict-like datastructures now have ``view``-methods under Python 2,
107 see pull request ``#968``.
108 - Fix a bug in ``MultiPartParser`` when no ``stream_factory`` was provided
109 during initialization, see pull request ``#973``.
110 - Disable autocorrect and spellchecker in the debugger middleware's Python
111 prompt, see pull request ``#994``.
112 - Don't redirect to slash route when method doesn't match, see pull request
113 ``#907``.
114 - Fix a bug when using ``SharedDataMiddleware`` with frozen packages, see pull
115 request ``#959``.
116 - `Range` header parsing function fixed for invalid values ``#974``.
117 - Add support for byte Range Requests, see pull request ``#978``.
118 - Use modern cryptographic defaults in the dev servers ``#1004``.
119 - the post() method of the test client now accept file object through the data
120 parameter.
121 - Color run_simple's terminal output based on HTTP codes ``#1013``.
122 - Fix self-XSS in debugger console, see ``#1031``.
123 - Fix IPython 5.x shell support, see ``#1033``.
124
125 Version 0.11.16
126 ---------------
127
128 - werkzeug.serving: set CONTENT_TYPE / CONTENT_LENGTH if only they're provided by the client
129 - werkzeug.serving: Fix crash of reloader when using `python -m werkzeug.serving`.
130
131 Version 0.11.15
132 ---------------
133
134 Released on December 30th 2016.
135
136 - Bugfix for the bugfix in the previous release.
137
138 Version 0.11.14
139 ---------------
140
141 Released on December 30th 2016.
142
143 - Check if platform can fork before importing ``ForkingMixIn``, raise exception
144 when creating ``ForkingWSGIServer`` on such a platform, see PR ``#999``.
145
146 Version 0.11.13
147 ---------------
148
149 Released on December 26th 2016.
150
151 - Correct fix for the reloader issuer on certain Windows installations.
152
153 Version 0.11.12
154 ---------------
155
156 Released on December 26th 2016.
157
158 - Fix more bugs in multidicts regarding empty lists. See ``#1000``.
159 - Add some docstrings to some `EnvironBuilder` properties that were previously
160 unintentionally missing.
161 - Added a workaround for the reloader on windows.
162
163 Version 0.11.11
164 ---------------
165
166 Released on August 31st 2016.
167
168 - Fix JSONRequestMixin for Python3. See #731
169 - Fix broken string handling in test client when passing integers. See #852
170 - Fix a bug in ``parse_options_header`` where an invalid content type
171 starting with comma or semi-colon would result in an invalid return value,
172 see issue ``#995``.
173 - Fix a bug in multidicts when passing empty lists as values, see issue
174 ``#979``.
175 - Fix a security issue that allows XSS on the Werkzeug debugger. See ``#1001``.
176
177 Version 0.11.10
178 ---------------
179
180 Released on May 24th 2016.
181
182 - Fixed a bug that occurs when running on Python 2.6 and using a broken locale.
183 See pull request #912.
184 - Fixed a crash when running the debugger on Google App Engine. See issue #925.
185 - Fixed an issue with multipart parsing that could cause memory exhaustion.
186
187 Version 0.11.9
188 --------------
189
190 Released on April 24th 2016.
191
192 - Corrected an issue that caused the debugger not to use the
193 machine GUID on POSIX systems.
194 - Corrected a Unicode error on Python 3 for the debugger's
195 PIN usage.
196 - Corrected the timestamp verification in the pin debug code.
197 Without this fix the pin was remembered for too long.
198
199 Version 0.11.8
200 --------------
201
202 Released on April 15th 2016.
203
204 - fixed a problem with the machine GUID detection code on OS X
205 on Python 3.
206
207 Version 0.11.7
208 --------------
209
210 Released on April 14th 2016.
211
212 - fixed a regression on Python 3 for the debugger.
213
214 Version 0.11.6
215 --------------
216
217 Released on April 14th 2016.
218
219 - werkzeug.serving: Still show the client address on bad requests.
220 - improved the PIN based protection for the debugger to make it harder to
221 brute force via trying cookies. Please keep in mind that the debugger
222 *is not intended for running on production environments*
223 - increased the pin timeout to a week to make it less annoying for people
224 which should decrease the chance that users disable the pin check
225 entirely.
226 - werkzeug.serving: Fix broken HTTP_HOST when path starts with double slash.
227
228 Version 0.11.5
229 --------------
230
231 Released on March 22nd 2016.
232
233 - werkzeug.serving: Fix crash when attempting SSL connection to HTTP server.
234
235 Version 0.11.4
236 --------------
237
238 Released on February 14th 2016.
239
240 - Fixed werkzeug.serving not working from -m flag.
241 - Fixed incorrect weak etag handling.
242
243 Version 0.11.3
244 --------------
245
246 Released on December 20th 2015.
247
248 - Fixed an issue with copy operations not working against
249 proxies.
250 - Changed the logging operations of the development server to
251 correctly log where the server is running in all situations
252 again.
253 - Fixed another regression with SSL wrapping similar to the
254 fix in 0.11.2 but for a different code path.
255
256 Version 0.11.2
257 --------------
258
259 Released on November 12th 2015.
260
261 - Fix inheritable sockets on Windows on Python 3.
262 - Fixed an issue with the forking server not starting any longer.
263 - Fixed SSL wrapping on platforms that supported opening sockets
264 by file descriptor.
265 - No longer log from the watchdog reloader.
266 - Unicode errors in hosts are now better caught or converted into
267 bad request errors.
268
269 Version 0.11.1
270 --------------
271
272 Released on November 10th 2015.
273
274 - Fixed a regression on Python 3 in the debugger.
275
276 Version 0.11
277 ------------
278
279 Released on November 8th 2015, codename Gleisbaumaschine.
280
281 - Added ``reloader_paths`` option to ``run_simple`` and other functions in
282 ``werkzeug.serving``. This allows the user to completely override the Python
283 module watching of Werkzeug with custom paths.
284 - Many custom cached properties of Werkzeug's classes are now subclasses of
285 Python's ``property`` type (issue ``#616``).
286 - ``bind_to_environ`` now doesn't differentiate between implicit and explicit
287 default port numbers in ``HTTP_HOST`` (pull request ``#204``).
288 - ``BuildErrors`` are now more informative. They come with a complete sentence
289 as error message, and also provide suggestions (pull request ``#691``).
290 - Fix a bug in the user agent parser where Safari's build number instead of
291 version would be extracted (pull request ``#703``).
292 - Fixed issue where RedisCache set_many was broken for twemproxy, which doesn't
293 support the default MULTI command (pull request ``#702``).
294 - ``mimetype`` parameters on request and response classes are now always
295 converted to lowercase.
296 - Changed cache so that cache never expires if timeout is 0. This also fixes
297 an issue with redis setex (issue ``#550``)
298 - Werkzeug now assumes ``UTF-8`` as filesystem encoding on Unix if Python
299 detected it as ASCII.
300 - New optional `has` method on caches.
301 - Fixed various bugs in `parse_options_header` (pull request ``#643``).
302 - If the reloader is enabled the server will now open the socket in the parent
303 process if this is possible. This means that when the reloader kicks in
304 the connection from client will wait instead of tearing down. This does
305 not work on all Python versions.
306 - Implemented PIN based authentication for the debugger. This can optionally
307 be disabled but is discouraged. This change was necessary as it has been
308 discovered that too many people run the debugger in production.
309 - Devserver no longer requires SSL module to be installed.
310
311 Version 0.10.5
312 --------------
313
314 (bugfix release, release date yet to be decided)
315
316 - Reloader: Correctly detect file changes made by moving temporary files over
317 the original, which is e.g. the case with PyCharm (pull request ``#722``).
318 - Fix bool behavior of ``werkzeug.datastructures.ETags`` under Python 3 (issue
319 ``#744``).
320
321 Version 0.10.4
322 --------------
323
324 (bugfix release, released on March 26th 2015)
325
326 - Re-release of 0.10.3 with packaging artifacts manually removed.
327
328 Version 0.10.3
329 --------------
330
331 (bugfix release, released on March 26th 2015)
332
333 - Re-release of 0.10.2 without packaging artifacts.
334
335 Version 0.10.2
336 --------------
337
338 (bugfix release, released on March 26th 2015)
339
340 - Fixed issue where ``empty`` could break third-party libraries that relied on
341 keyword arguments (pull request ``#675``)
342 - Improved ``Rule.empty`` by providing a ```get_empty_kwargs`` to allow setting
343 custom kwargs without having to override entire ``empty`` method. (pull
344 request ``#675``)
345 - Fixed ```extra_files``` parameter for reloader to not cause startup
346 to crash when included in server params
347 - Using `MultiDict` when building URLs is now not supported again. The behavior
348 introduced several regressions.
349 - Fix performance problems with stat-reloader (pull request ``#715``).
350
351 Version 0.10.1
352 --------------
353
354 (bugfix release, released on February 3rd 2015)
355
356 - Fixed regression with multiple query values for URLs (pull request ``#667``).
357 - Fix issues with eventlet's monkeypatching and the builtin server (pull
358 request ``#663``).
359
360 Version 0.10
361 ------------
362
363 Released on January 30th 2015, codename Bagger.
364
365 - Changed the error handling of and improved testsuite for the caches in
366 ``contrib.cache``.
367 - Fixed a bug on Python 3 when creating adhoc ssl contexts, due to `sys.maxint`
368 not being defined.
369 - Fixed a bug on Python 3, that caused
370 :func:`~werkzeug.serving.make_ssl_devcert` to fail with an exception.
371 - Added exceptions for 504 and 505.
372 - Added support for ChromeOS detection.
373 - Added UUID converter to the routing system.
374 - Added message that explains how to quit the server.
375 - Fixed a bug on Python 2, that caused ``len`` for
376 :class:`werkzeug.datastructures.CombinedMultiDict` to crash.
377 - Added support for stdlib pbkdf2 hmac if a compatible digest
378 is found.
379 - Ported testsuite to use ``py.test``.
380 - Minor optimizations to various middlewares (pull requests ``#496`` and
381 ``#571``).
382 - Use stdlib ``ssl`` module instead of ``OpenSSL`` for the builtin server
383 (issue ``#434``). This means that OpenSSL contexts are not supported anymore,
384 but instead ``ssl.SSLContext`` from the stdlib.
385 - Allow protocol-relative URLs when building external URLs.
386 - Fixed Atom syndication to print time zone offset for tz-aware datetime
387 objects (pull request ``#254``).
388 - Improved reloader to track added files and to recover from broken
389 sys.modules setups with syntax errors in packages.
390 - ``cache.RedisCache`` now supports arbitrary ``**kwargs`` for the redis
391 object.
392 - ``werkzeug.test.Client`` now uses the original request method when resolving
393 307 redirects (pull request ``#556``).
394 - ``werkzeug.datastructures.MIMEAccept`` now properly deals with mimetype
395 parameters (pull request ``#205``).
396 - ``werkzeug.datastructures.Accept`` now handles a quality of ``0`` as
397 intolerable, as per RFC 2616 (pull request ``#536``).
398 - ``werkzeug.urls.url_fix`` now properly encodes hostnames with ``idna``
399 encoding (issue ``#559``). It also doesn't crash on malformed URLs anymore
400 (issue ``#582``).
401 - ``werkzeug.routing.MapAdapter.match`` now recognizes the difference between
402 the path ``/`` and an empty one (issue ``#360``).
403 - The interactive debugger now tries to decode non-ascii filenames (issue
404 ``#469``).
405 - Increased default key size of generated SSL certificates to 1024 bits (issue
406 ``#611``).
407 - Added support for specifying a ``Response`` subclass to use when calling
408 :func:`~werkzeug.utils.redirect`\ .
409 - ``werkzeug.test.EnvironBuilder`` now doesn't use the request method anymore
410 to guess the content type, and purely relies on the ``form``, ``files`` and
411 ``input_stream`` properties (issue ``#620``).
412 - Added Symbian to the user agent platform list.
413 - Fixed make_conditional to respect automatically_set_content_length
414 - Unset ``Content-Length`` when writing to response.stream (issue ``#451``)
415 - ``wrappers.Request.method`` is now always uppercase, eliminating
416 inconsistencies of the WSGI environment (issue ``647``).
417 - ``routing.Rule.empty`` now works correctly with subclasses of ``Rule`` (pull
418 request ``#645``).
419 - Made map updating safe in light of concurrent updates.
420 - Allow multiple values for the same field for url building (issue ``#658``).
421
422 Version 0.9.7
423 -------------
424
425 (bugfix release, release date to be decided)
426
427 - Fix unicode problems in ``werkzeug.debug.tbtools``.
428 - Fix Python 3-compatibility problems in ``werkzeug.posixemulation``.
429 - Backport fix of fatal typo for ``ImmutableList`` (issue ``#492``).
430 - Make creation of the cache dir for ``FileSystemCache`` atomic (issue
431 ``#468``).
432 - Use native strings for memcached keys to work with Python 3 client (issue
433 ``#539``).
434 - Fix charset detection for ``werkzeug.debug.tbtools.Frame`` objects (issues
435 ``#547`` and ``#532``).
436 - Fix ``AttributeError`` masking in ``werkzeug.utils.import_string`` (issue
437 ``#182``).
438 - Explicitly shut down server (issue ``#519``).
439 - Fix timeouts greater than 2592000 being misinterpreted as UNIX timestamps in
440 ``werkzeug.contrib.cache.MemcachedCache`` (issue ``#533``).
441 - Fix bug where ``werkzeug.exceptions.abort`` would raise an arbitrary subclass
442 of the expected class (issue ``#422``).
443 - Fix broken ``jsrouting`` (due to removal of ``werkzeug.templates``)
444 - ``werkzeug.urls.url_fix`` now doesn't crash on malformed URLs anymore, but
445 returns them unmodified. This is a cheap workaround for ``#582``, the proper
446 fix is included in version 0.10.
447 - The repr of ``werkzeug.wrappers.Request`` doesn't crash on non-ASCII-values
448 anymore (pull request ``#466``).
449 - Fix bug in ``cache.RedisCache`` when combined with ``redis.StrictRedis``
450 object (pull request ``#583``).
451 - The ``qop`` parameter for ``WWW-Authenticate`` headers is now always quoted,
452 as required by RFC 2617 (issue ``#633``).
453 - Fix bug in ``werkzeug.contrib.cache.SimpleCache`` with Python 3 where add/set
454 may throw an exception when pruning old entries from the cache (pull request
455 ``#651``).
456
457 Version 0.9.6
458 -------------
459
460 (bugfix release, released on June 7th 2014)
461
462 - Added a safe conversion for IRI to URI conversion and use that
463 internally to work around issues with spec violations for
464 protocols such as ``itms-service``.
465
466 Version 0.9.7
467 -------------
468
469 - Fixed uri_to_iri() not re-encoding hashes in query string parameters.
470
471 Version 0.9.5
472 -------------
473
474 (bugfix release, released on June 7th 2014)
475
476 - Forward charset argument from request objects to the environ
477 builder.
478 - Fixed error handling for missing boundaries in multipart data.
479 - Fixed session creation on systems without ``os.urandom()``.
480 - Fixed pluses in dictionary keys not being properly URL encoded.
481 - Fixed a problem with deepcopy not working for multi dicts.
482 - Fixed a double quoting issue on redirects.
483 - Fixed a problem with unicode keys appearing in headers on 2.x.
484 - Fixed a bug with unicode strings in the test builder.
485 - Fixed a unicode bug on Python 3 in the WSGI profiler.
486 - Fixed an issue with the safe string compare function on
487 Python 2.7.7 and Python 3.4.
488
489 Version 0.9.4
490 -------------
491
492 (bugfix release, released on August 26th 2013)
493
494 - Fixed an issue with Python 3.3 and an edge case in cookie parsing.
495 - Fixed decoding errors not handled properly through the WSGI
496 decoding dance.
497 - Fixed URI to IRI conversion incorrectly decoding percent signs.
498
499 Version 0.9.3
500 -------------
501
502 (bugfix release, released on July 25th 2013)
503
504 - Restored behavior of the ``data`` descriptor of the request class to pre 0.9
505 behavior. This now also means that ``.data`` and ``.get_data()`` have
506 different behavior. New code should use ``.get_data()`` always.
507
508 In addition to that there is now a flag for the ``.get_data()`` method that
509 controls what should happen with form data parsing and the form parser will
510 honor cached data. This makes dealing with custom form data more consistent.
511
512 Version 0.9.2
513 -------------
514
515 (bugfix release, released on July 18th 2013)
516
517 - Added `unsafe` parameter to :func:`~werkzeug.urls.url_quote`.
518 - Fixed an issue with :func:`~werkzeug.urls.url_quote_plus` not quoting
519 `'+'` correctly.
520 - Ported remaining parts of :class:`~werkzeug.contrib.RedisCache` to
521 Python 3.3.
522 - Ported remaining parts of :class:`~werkzeug.contrib.MemcachedCache` to
523 Python 3.3
524 - Fixed a deprecation warning in the contrib atom module.
525 - Fixed a regression with setting of content types through the
526 headers dictionary instead with the content type parameter.
527 - Use correct name for stdlib secure string comparison function.
528 - Fixed a wrong reference in the docstring of
529 :func:`~werkzeug.local.release_local`.
530 - Fixed an `AttributeError` that sometimes occurred when accessing the
531 :attr:`werkzeug.wrappers.BaseResponse.is_streamed` attribute.
532
533 Version 0.9.1
534 -------------
535
536 (bugfix release, released on June 14th 2013)
537
538 - Fixed an issue with integers no longer being accepted in certain
539 parts of the routing system or URL quoting functions.
540 - Fixed an issue with `url_quote` not producing the right escape
541 codes for single digit codepoints.
542 - Fixed an issue with :class:`~werkzeug.wsgi.SharedDataMiddleware` not
543 reading the path correctly and breaking on etag generation in some
544 cases.
545 - Properly handle `Expect: 100-continue` in the development server
546 to resolve issues with curl.
547 - Automatically exhaust the input stream on request close. This should
548 fix issues where not touching request files results in a timeout.
549 - Fixed exhausting of streams not doing anything if a non-limited
550 stream was passed into the multipart parser.
551 - Raised the buffer sizes for the multipart parser.
552
553 Version 0.9
554 -----------
555
556 Released on June 13nd 2013, codename Planierraupe.
557
558 - Added support for :meth:`~werkzeug.wsgi.LimitedStream.tell`
559 on the limited stream.
560 - :class:`~werkzeug.datastructures.ETags` now is nonzero if it
561 contains at least one etag of any kind, including weak ones.
562 - Added a workaround for a bug in the stdlib for SSL servers.
563 - Improved SSL interface of the devserver so that it can generate
564 certificates easily and load them from files.
565 - Refactored test client to invoke the open method on the class
566 for redirects. This makes subclassing more powerful.
567 - :func:`werkzeug.wsgi.make_chunk_iter` and
568 :func:`werkzeug.wsgi.make_line_iter` now support processing of
569 iterators and streams.
570 - URL generation by the routing system now no longer quotes
571 ``+``.
572 - URL fixing now no longer quotes certain reserved characters.
573 - The :func:`werkzeug.security.generate_password_hash` and
574 check functions now support any of the hashlib algorithms.
575 - `wsgi.get_current_url` is now ascii safe for browsers sending
576 non-ascii data in query strings.
577 - improved parsing behavior for :func:`werkzeug.http.parse_options_header`
578 - added more operators to local proxies.
579 - added a hook to override the default converter in the routing
580 system.
581 - The description field of HTTP exceptions is now always escaped.
582 Use markup objects to disable that.
583 - Added number of proxy argument to the proxy fix to make it more
584 secure out of the box on common proxy setups. It will by default
585 no longer trust the x-forwarded-for header as much as it did
586 before.
587 - Added support for fragment handling in URI/IRI functions.
588 - Added custom class support for :func:`werkzeug.http.parse_dict_header`.
589 - Renamed `LighttpdCGIRootFix` to `CGIRootFix`.
590 - Always treat `+` as safe when fixing URLs as people love misusing them.
591 - Added support to profiling into directories in the contrib profiler.
592 - The escape function now by default escapes quotes.
593 - Changed repr of exceptions to be less magical.
594 - Simplified exception interface to no longer require environments
595 to be passed to receive the response object.
596 - Added sentinel argument to IterIO objects.
597 - Added pbkdf2 support for the security module.
598 - Added a plain request type that disables all form parsing to only
599 leave the stream behind.
600 - Removed support for deprecated `fix_headers`.
601 - Removed support for deprecated `header_list`.
602 - Removed support for deprecated parameter for `iter_encoded`.
603 - Removed support for deprecated non-silent usage of the limited
604 stream object.
605 - Removed support for previous dummy `writable` parameter on
606 the cached property.
607 - Added support for explicitly closing request objects to close
608 associated resources.
609 - Conditional request handling or access to the data property on responses no
610 longer ignores direct passthrough mode.
611 - Removed werkzeug.templates and werkzeug.contrib.kickstart.
612 - Changed host lookup logic for forwarded hosts to allow lists of
613 hosts in which case only the first one is picked up.
614 - Added `wsgi.get_query_string`, `wsgi.get_path_info` and
615 `wsgi.get_script_name` and made the `wsgi.pop_path_info` and
616 `wsgi.peek_path_info` functions perform unicode decoding. This
617 was necessary to avoid having to expose the WSGI encoding dance
618 on Python 3.
619 - Added `content_encoding` and `content_md5` to the request object's
620 common request descriptor mixin.
621 - added `options` and `trace` to the test client.
622 - Overhauled the utilization of the input stream to be easier to use
623 and better to extend. The detection of content payload on the input
624 side is now more compliant with HTTP by detecting off the content
625 type header instead of the request method. This also now means that
626 the stream property on the request class is always available instead
627 of just when the parsing fails.
628 - Added support for using :class:`werkzeug.wrappers.BaseResponse` in a with
629 statement.
630 - Changed `get_app_iter` to fetch the response early so that it does not
631 fail when wrapping a response iterable. This makes filtering easier.
632 - Introduced `get_data` and `set_data` methods for responses.
633 - Introduced `get_data` for requests.
634 - Soft deprecated the `data` descriptors for request and response objects.
635 - Added `as_bytes` operations to some of the headers to simplify working
636 with things like cookies.
637 - Made the debugger paste tracebacks into github's gist service as
638 private pastes.
639
640 Version 0.8.4
641 -------------
642
643 (bugfix release, release date to be announced)
644
645 - Added a favicon to the debugger which fixes problem with
646 state changes being triggered through a request to
647 /favicon.ico in Google Chrome. This should fix some
648 problems with Flask and other frameworks that use
649 context local objects on a stack with context preservation
650 on errors.
651 - Fixed an issue with scrolling up in the debugger.
652 - Fixed an issue with debuggers running on a different URL
653 than the URL root.
654 - Fixed a problem with proxies not forwarding some rarely
655 used special methods properly.
656 - Added a workaround to prevent the XSS protection from Chrome
657 breaking the debugger.
658 - Skip redis tests if redis is not running.
659 - Fixed a typo in the multipart parser that caused content-type
660 to not be picked up properly.
661
662 Version 0.8.3
663 -------------
664
665 (bugfix release, released on February 5th 2012)
666
667 - Fixed another issue with :func:`werkzeug.wsgi.make_line_iter`
668 where lines longer than the buffer size were not handled
669 properly.
670 - Restore stdout after debug console finished executing so
671 that the debugger can be used on GAE better.
672 - Fixed a bug with the redis cache for int subclasses
673 (affects bool caching).
674 - Fixed an XSS problem with redirect targets coming from
675 untrusted sources.
676 - Redis cache backend now supports password authentication.
677
678 Version 0.8.2
679 -------------
680
681 (bugfix release, released on December 16th 2011)
682
683 - Fixed a problem with request handling of the builtin server
684 not responding to socket errors properly.
685 - The routing request redirect exception's code attribute is now
686 used properly.
687 - Fixed a bug with shutdowns on Windows.
688 - Fixed a few unicode issues with non-ascii characters being
689 hardcoded in URL rules.
690 - Fixed two property docstrings being assigned to fdel instead
691 of ``__doc__``.
692 - Fixed an issue where CRLF line endings could be split into two
693 by the line iter function, causing problems with multipart file
694 uploads.
695
696 Version 0.8.1
697 -------------
698
699 (bugfix release, released on September 30th 2011)
700
701 - Fixed an issue with the memcache not working properly.
702 - Fixed an issue for Python 2.7.1 and higher that broke
703 copying of multidicts with :func:`copy.copy`.
704 - Changed hashing methodology of immutable ordered multi dicts
705 for a potential problem with alternative Python implementations.
706
707 Version 0.8
708 -----------
709
710 Released on September 29th 2011, codename Lötkolben
711
712 - Removed data structure specific KeyErrors for a general
713 purpose :exc:`~werkzeug.exceptions.BadRequestKeyError`.
714 - Documented :meth:`werkzeug.wrappers.BaseRequest._load_form_data`.
715 - The routing system now also accepts strings instead of
716 dictionaries for the `query_args` parameter since we're only
717 passing them through for redirects.
718 - Werkzeug now automatically sets the content length immediately when
719 the :attr:`~werkzeug.wrappers.BaseResponse.data` attribute is set
720 for efficiency and simplicity reasons.
721 - The routing system will now normalize server names to lowercase.
722 - The routing system will no longer raise ValueErrors in case the
723 configuration for the server name was incorrect. This should make
724 deployment much easier because you can ignore that factor now.
725 - Fixed a bug with parsing HTTP digest headers. It rejected headers
726 with missing nc and nonce params.
727 - Proxy fix now also updates wsgi.url_scheme based on X-Forwarded-Proto.
728 - Added support for key prefixes to the redis cache.
729 - Added the ability to suppress some auto corrections in the wrappers
730 that are now controlled via `autocorrect_location_header` and
731 `automatically_set_content_length` on the response objects.
732 - Werkzeug now uses a new method to check that the length of incoming
733 data is complete and will raise IO errors by itself if the server
734 fails to do so.
735 - :func:`~werkzeug.wsgi.make_line_iter` now requires a limit that is
736 not higher than the length the stream can provide.
737 - Refactored form parsing into a form parser class that makes it possible
738 to hook into individual parts of the parsing process for debugging and
739 extending.
740 - For conditional responses the content length is no longer set when it
741 is already there and added if missing.
742 - Immutable datastructures are hashable now.
743 - Headers datastructure no longer allows newlines in values to avoid
744 header injection attacks.
745 - Made it possible through subclassing to select a different remote
746 addr in the proxy fix.
747 - Added stream based URL decoding. This reduces memory usage on large
748 transmitted form data that is URL decoded since Werkzeug will no longer
749 load all the unparsed data into memory.
750 - Memcache client now no longer uses the buggy cmemcache module and
751 supports pylibmc. GAE is not tried automatically and the dedicated
752 class is no longer necessary.
753 - Redis cache now properly serializes data.
754 - Removed support for Python 2.4
755
756 Version 0.7.2
757 -------------
758
759 (bugfix release, released on September 30th 2011)
760
761 - Fixed a CSRF problem with the debugger.
762 - The debugger is now generating private pastes on lodgeit.
763 - If URL maps are now bound to environments the query arguments
764 are properly decoded from it for redirects.
765
766 Version 0.7.1
767 -------------
768
769 (bugfix release, released on July 26th 2011)
770
771 - Fixed a problem with newer versions of IPython.
772 - Disabled pyinotify based reloader which does not work reliably.
773
774 Version 0.7
775 -----------
776
777 Released on July 24th 2011, codename Schraubschlüssel
778
779 - Add support for python-libmemcached to the Werkzeug cache abstraction
780 layer.
781 - Improved :func:`url_decode` and :func:`url_encode` performance.
782 - Fixed an issue where the SharedDataMiddleware could cause an
783 internal server error on weird paths when loading via pkg_resources.
784 - Fixed an URL generation bug that caused URLs to be invalid if a
785 generated component contains a colon.
786 - :func:`werkzeug.import_string` now works with partially set up
787 packages properly.
788 - Disabled automatic socket switching for IPv6 on the development
789 server due to problems it caused.
790 - Werkzeug no longer overrides the Date header when creating a
791 conditional HTTP response.
792 - The routing system provides a method to retrieve the matching
793 methods for a given path.
794 - The routing system now accepts a parameter to change the encoding
795 error behaviour.
796 - The local manager can now accept custom ident functions in the
797 constructor that are forwarded to the wrapped local objects.
798 - url_unquote_plus now accepts unicode strings again.
799 - Fixed an issue with the filesystem session support's prune
800 function and concurrent usage.
801 - Fixed a problem with external URL generation discarding the port.
802 - Added support for pylibmc to the Werkzeug cache abstraction layer.
803 - Fixed an issue with the new multipart parser that happened when
804 a linebreak happened to be on the chunk limit.
805 - Cookies are now set properly if ports are in use. A runtime error
806 is raised if one tries to set a cookie for a domain without a dot.
807 - Fixed an issue with Template.from_file not working for file
808 descriptors.
809 - Reloader can now use inotify to track reloads. This requires the
810 pyinotify library to be installed.
811 - Werkzeug debugger can now submit to custom lodgeit installations.
812 - redirect function's status code assertion now allows 201 to be used
813 as redirection code. While it's not a real redirect, it shares
814 enough with redirects for the function to still be useful.
815 - Fixed securecookie for pypy.
816 - Fixed `ValueErrors` being raised on calls to `best_match` on
817 `MIMEAccept` objects when invalid user data was supplied.
818 - Deprecated `werkzeug.contrib.kickstart` and `werkzeug.contrib.testtools`
819 - URL routing now can be passed the URL arguments to keep them for
820 redirects. In the future matching on URL arguments might also be
821 possible.
822 - Header encoding changed from utf-8 to latin1 to support a port to
823 Python 3. Bytestrings passed to the object stay untouched which
824 makes it possible to have utf-8 cookies. This is a part where
825 the Python 3 version will later change in that it will always
826 operate on latin1 values.
827 - Fixed a bug in the form parser that caused the last character to
828 be dropped off if certain values in multipart data are used.
829 - Multipart parser now looks at the part-individual content type
830 header to override the global charset.
831 - Introduced mimetype and mimetype_params attribute for the file
832 storage object.
833 - Changed FileStorage filename fallback logic to skip special filenames
834 that Python uses for marking special files like stdin.
835 - Introduced more HTTP exception classes.
836 - `call_on_close` now can be used as a decorator.
837 - Support for redis as cache backend.
838 - Added `BaseRequest.scheme`.
839 - Support for the RFC 5789 PATCH method.
840 - New custom routing parser and better ordering.
841 - Removed support for `is_behind_proxy`. Use a WSGI middleware
842 instead that rewrites the `REMOTE_ADDR` according to your setup.
843 Also see the :class:`werkzeug.contrib.fixers.ProxyFix` for
844 a drop-in replacement.
845 - Added cookie forging support to the test client.
846 - Added support for host based matching in the routing system.
847 - Switched from the default 'ignore' to the better 'replace'
848 unicode error handling mode.
849 - The builtin server now adds a function named 'werkzeug.server.shutdown'
850 into the WSGI env to initiate a shutdown. This currently only works
851 in Python 2.6 and later.
852 - Headers are now assumed to be latin1 for better compatibility with
853 Python 3 once we have support.
854 - Added :func:`werkzeug.security.safe_join`.
855 - Added `accept_json` property analogous to `accept_html` on the
856 :class:`werkzeug.datastructures.MIMEAccept`.
857 - :func:`werkzeug.utils.import_string` now fails with much better
858 error messages that pinpoint to the problem.
859 - Added support for parsing of the `If-Range` header
860 (:func:`werkzeug.http.parse_if_range_header` and
861 :class:`werkzeug.datastructures.IfRange`).
862 - Added support for parsing of the `Range` header
863 (:func:`werkzeug.http.parse_range_header` and
864 :class:`werkzeug.datastructures.Range`).
865 - Added support for parsing of the `Content-Range` header of responses
866 and provided an accessor object for it
867 (:func:`werkzeug.http.parse_content_range_header` and
868 :class:`werkzeug.datastructures.ContentRange`).
869
870 Version 0.6.2
871 -------------
872
873 (bugfix release, released on April 23th 2010)
874
875 - renamed the attribute `implicit_seqence_conversion` attribute of the
876 request object to `implicit_sequence_conversion`.
877
878 Version 0.6.1
879 -------------
880
881 (bugfix release, released on April 13th 2010)
882
883 - heavily improved local objects. Should pick up standalone greenlet
884 builds now and support proxies to free callables as well. There is
885 also a stacked local now that makes it possible to invoke the same
886 application from within itself by pushing current request/response
887 on top of the stack.
888 - routing build method will also build non-default method rules properly
889 if no method is provided.
890 - added proper IPv6 support for the builtin server.
891 - windows specific filesystem session store fixes.
892 (should now be more stable under high concurrency)
893 - fixed a `NameError` in the session system.
894 - fixed a bug with empty arguments in the werkzeug.script system.
895 - fixed a bug where log lines will be duplicated if an application uses
896 :meth:`logging.basicConfig` (#499)
897 - added secure password hashing and checking functions.
898 - `HEAD` is now implicitly added as method in the routing system if
899 `GET` is present. Not doing that was considered a bug because often
900 code assumed that this is the case and in web servers that do not
901 normalize `HEAD` to `GET` this could break `HEAD` requests.
902 - the script support can start SSL servers now.
903
904 Version 0.6
905 -----------
906
907 Released on Feb 19th 2010, codename Hammer.
908
909 - removed pending deprecations
910 - sys.path is now printed from the testapp.
911 - fixed an RFC 2068 incompatibility with cookie value quoting.
912 - the :class:`FileStorage` now gives access to the multipart headers.
913 - `cached_property.writeable` has been deprecated.
914 - :meth:`MapAdapter.match` now accepts a `return_rule` keyword argument
915 that returns the matched `Rule` instead of just the `endpoint`
916 - :meth:`routing.Map.bind_to_environ` raises a more correct error message
917 now if the map was bound to an invalid WSGI environment.
918 - added support for SSL to the builtin development server.
919 - Response objects are no longer modified in place when they are evaluated
920 as WSGI applications. For backwards compatibility the `fix_headers`
921 function is still called in case it was overridden.
922 You should however change your application to use `get_wsgi_headers` if
923 you need header modifications before responses are sent as the backwards
924 compatibility support will go away in future versions.
925 - :func:`append_slash_redirect` no longer requires the QUERY_STRING to be
926 in the WSGI environment.
927 - added :class:`~werkzeug.contrib.wrappers.DynamicCharsetResponseMixin`
928 - added :class:`~werkzeug.contrib.wrappers.DynamicCharsetRequestMixin`
929 - added :attr:`BaseRequest.url_charset`
930 - request and response objects have a default `__repr__` now.
931 - builtin data structures can be pickled now.
932 - the form data parser will now look at the filename instead the
933 content type to figure out if it should treat the upload as regular
934 form data or file upload. This fixes a bug with Google Chrome.
935 - improved performance of `make_line_iter` and the multipart parser
936 for binary uploads.
937 - fixed :attr:`~werkzeug.BaseResponse.is_streamed`
938 - fixed a path quoting bug in `EnvironBuilder` that caused PATH_INFO and
939 SCRIPT_NAME to end up in the environ unquoted.
940 - :meth:`werkzeug.BaseResponse.freeze` now sets the content length.
941 - for unknown HTTP methods the request stream is now always limited
942 instead of being empty. This makes it easier to implement DAV
943 and other protocols on top of Werkzeug.
944 - added :meth:`werkzeug.MIMEAccept.best_match`
945 - multi-value test-client posts from a standard dictionary are now
946 supported. Previously you had to use a multi dict.
947 - rule templates properly work with submounts, subdomains and
948 other rule factories now.
949 - deprecated non-silent usage of the :class:`werkzeug.LimitedStream`.
950 - added support for IRI handling to many parts of Werkzeug.
951 - development server properly logs to the werkzeug logger now.
952 - added :func:`werkzeug.extract_path_info`
953 - fixed a querystring quoting bug in :func:`url_fix`
954 - added `fallback_mimetype` to :class:`werkzeug.SharedDataMiddleware`.
955 - deprecated :meth:`BaseResponse.iter_encoded`'s charset parameter.
956 - added :meth:`BaseResponse.make_sequence`,
957 :attr:`BaseResponse.is_sequence` and
958 :meth:`BaseResponse._ensure_sequence`.
959 - added better __repr__ of :class:`werkzeug.Map`
960 - `import_string` accepts unicode strings as well now.
961 - development server doesn't break on double slashes after the host name.
962 - better `__repr__` and `__str__` of
963 :exc:`werkzeug.exceptions.HTTPException`
964 - test client works correctly with multiple cookies now.
965 - the :class:`werkzeug.routing.Map` now has a class attribute with
966 the default converter mapping. This helps subclasses to override
967 the converters without passing them to the constructor.
968 - implemented :class:`OrderedMultiDict`
969 - improved the session support for more efficient session storing
970 on the filesystem. Also added support for listing of sessions
971 currently stored in the filesystem session store.
972 - werkzeug no longer utilizes the Python time module for parsing
973 which means that dates in a broader range can be parsed.
974 - the wrappers have no class attributes that make it possible to
975 swap out the dict and list types it uses.
976 - werkzeug debugger should work on the appengine dev server now.
977 - the URL builder supports dropping of unexpected arguments now.
978 Previously they were always appended to the URL as query string.
979 - profiler now writes to the correct stream.
980
981 Version 0.5.1
982 -------------
983 (bugfix release for 0.5, released on July 9th 2009)
984
985 - fixed boolean check of :class:`FileStorage`
986 - url routing system properly supports unicode URL rules now.
987 - file upload streams no longer have to provide a truncate()
988 method.
989 - implemented :meth:`BaseRequest._form_parsing_failed`.
990 - fixed #394
991 - :meth:`ImmutableDict.copy`, :meth:`ImmutableMultiDict.copy` and
992 :meth:`ImmutableTypeConversionDict.copy` return mutable shallow
993 copies.
994 - fixed a bug with the `make_runserver` script action.
995 - :meth:`MultiDict.items` and :meth:`MutiDict.iteritems` now accept an
996 argument to return a pair for each value of each key.
997 - the multipart parser works better with hand-crafted multipart
998 requests now that have extra newlines added. This fixes a bug
999 with setuptools uploads not handled properly (#390)
1000 - fixed some minor bugs in the atom feed generator.
1001 - fixed a bug with client cookie header parsing being case sensitive.
1002 - fixed a not-working deprecation warning.
1003 - fixed package loading for :class:`SharedDataMiddleware`.
1004 - fixed a bug in the secure cookie that made server-side expiration
1005 on servers with a local time that was not set to UTC impossible.
1006 - fixed console of the interactive debugger.
1007
1008
1009 Version 0.5
1010 -----------
1011
1012 Released on April 24th, codename Schlagbohrer.
1013
1014 - requires Python 2.4 now
1015 - fixed a bug in :class:`~contrib.IterIO`
1016 - added :class:`MIMEAccept` and :class:`CharsetAccept` that work like the
1017 regular :class:`Accept` but have extra special normalization for mimetypes
1018 and charsets and extra convenience methods.
1019 - switched the serving system from wsgiref to something homebrew.
1020 - the :class:`Client` now supports cookies.
1021 - added the :mod:`~werkzeug.contrib.fixers` module with various
1022 fixes for webserver bugs and hosting setup side-effects.
1023 - added :mod:`werkzeug.contrib.wrappers`
1024 - added :func:`is_hop_by_hop_header`
1025 - added :func:`is_entity_header`
1026 - added :func:`remove_hop_by_hop_headers`
1027 - added :func:`pop_path_info`
1028 - added :func:`peek_path_info`
1029 - added :func:`wrap_file` and :class:`FileWrapper`
1030 - moved `LimitedStream` from the contrib package into the regular
1031 werkzeug one and changed the default behavior to raise exceptions
1032 rather than stopping without warning. The old class will stick in
1033 the module until 0.6.
1034 - implemented experimental multipart parser that replaces the old CGI hack.
1035 - added :func:`dump_options_header` and :func:`parse_options_header`
1036 - added :func:`quote_header_value` and :func:`unquote_header_value`
1037 - :func:`url_encode` and :func:`url_decode` now accept a separator
1038 argument to switch between `&` and `;` as pair separator. The magic
1039 switch is no longer in place.
1040 - all form data parsing functions as well as the :class:`BaseRequest`
1041 object have parameters (or attributes) to limit the number of
1042 incoming bytes (either totally or per field).
1043 - added :class:`LanguageAccept`
1044 - request objects are now enforced to be read only for all collections.
1045 - added many new collection classes, refactored collections in general.
1046 - test support was refactored, semi-undocumented `werkzeug.test.File`
1047 was replaced by :class:`werkzeug.FileStorage`.
1048 - :class:`EnvironBuilder` was added and unifies the previous distinct
1049 :func:`create_environ`, :class:`Client` and
1050 :meth:`BaseRequest.from_values`. They all work the same now which
1051 is less confusing.
1052 - officially documented imports from the internal modules as undefined
1053 behavior. These modules were never exposed as public interfaces.
1054 - removed `FileStorage.__len__` which previously made the object
1055 falsy for browsers not sending the content length which all browsers
1056 do.
1057 - :class:`SharedDataMiddleware` uses `wrap_file` now and has a
1058 configurable cache timeout.
1059 - added :class:`CommonRequestDescriptorsMixin`
1060 - added :attr:`CommonResponseDescriptorsMixin.mimetype_params`
1061 - added :mod:`werkzeug.contrib.lint`
1062 - added `passthrough_errors` to `run_simple`.
1063 - added `secure_filename`
1064 - added :func:`make_line_iter`
1065 - :class:`MultiDict` copies now instead of revealing internal
1066 lists to the caller for `getlist` and iteration functions that
1067 return lists.
1068 - added :attr:`follow_redirect` to the :func:`open` of :class:`Client`.
1069 - added support for `extra_files` in
1070 :func:`~werkzeug.script.make_runserver`
1071
1072 Version 0.4.1
1073 -------------
1074
1075 (Bugfix release, released on January 11th 2009)
1076
1077 - `werkzeug.contrib.cache.Memcached` accepts now objects that
1078 implement the memcache.Client interface as alternative to a list of
1079 strings with server addresses.
1080 There is also now a `GAEMemcachedCache` that connects to the Google
1081 appengine cache.
1082 - explicitly convert secret keys to bytestrings now because Python
1083 2.6 no longer does that.
1084 - `url_encode` and all interfaces that call it, support ordering of
1085 options now which however is disabled by default.
1086 - the development server no longer resolves the addresses of clients.
1087 - Fixed a typo in `werkzeug.test` that broke `File`.
1088 - `Map.bind_to_environ` uses the `Host` header now if available.
1089 - Fixed `BaseCache.get_dict` (#345)
1090 - `werkzeug.test.Client` can now run the application buffered in which
1091 case the application is properly closed automatically.
1092 - Fixed `Headers.set` (#354). Caused header duplication before.
1093 - Fixed `Headers.pop` (#349). default parameter was not properly
1094 handled.
1095 - Fixed UnboundLocalError in `create_environ` (#351)
1096 - `Headers` is more compatible with wsgiref now.
1097 - `Template.render` accepts multidicts now.
1098 - dropped support for Python 2.3
1099
1100 Version 0.4
1101 -----------
1102
1103 Released on November 23rd 2008, codename Schraubenzieher.
1104
1105 - `Client` supports an empty `data` argument now.
1106 - fixed a bug in `Response.application` that made it impossible to use it
1107 as method decorator.
1108 - the session system should work on appengine now
1109 - the secure cookie works properly in load balanced environments with
1110 different cpu architectures now.
1111 - `CacheControl.no_cache` and `CacheControl.private` behavior changed to
1112 reflect the possibilities of the HTTP RFC. Setting these attributes to
1113 `None` or `True` now sets the value to "the empty value".
1114 More details in the documentation.
1115 - fixed `werkzeug.contrib.atom.AtomFeed.__call__`. (#338)
1116 - `BaseResponse.make_conditional` now always returns `self`. Previously
1117 it didn't for post requests and such.
1118 - fixed a bug in boolean attribute handling of `html` and `xhtml`.
1119 - added graceful error handling to the debugger pastebin feature.
1120 - added a more list like interface to `Headers` (slicing and indexing
1121 works now)
1122 - fixed a bug with the `__setitem__` method of `Headers` that didn't
1123 properly remove all keys on replacing.
1124 - added `remove_entity_headers` which removes all entity headers from
1125 a list of headers (or a `Headers` object)
1126 - the responses now automatically call `remove_entity_headers` if the
1127 status code is 304.
1128 - fixed a bug with `Href` query parameter handling. Previously the last
1129 item of a call to `Href` was not handled properly if it was a dict.
1130 - headers now support a `pop` operation to better work with environ
1131 properties.
1132
1133
1134 Version 0.3.1
1135 -------------
1136
1137 (bugfix release, released on June 24th 2008)
1138
1139 - fixed a security problem with `werkzeug.contrib.SecureCookie`.
1140 More details available in the `release announcement`_.
1141
1142 .. _release announcement: http://lucumr.pocoo.org/cogitations/2008/06/24/werkzeug-031-released/
1143
1144 Version 0.3
1145 -----------
1146
1147 Released on June 14th 2008, codename EUR325CAT6.
1148
1149 - added support for redirecting in url routing.
1150 - added `Authorization` and `AuthorizationMixin`
1151 - added `WWWAuthenticate` and `WWWAuthenticateMixin`
1152 - added `parse_list_header`
1153 - added `parse_dict_header`
1154 - added `parse_authorization_header`
1155 - added `parse_www_authenticate_header`
1156 - added `_get_current_object` method to `LocalProxy` objects
1157 - added `parse_form_data`
1158 - `MultiDict`, `CombinedMultiDict`, `Headers`, and `EnvironHeaders` raise
1159 special key errors now that are subclasses of `BadRequest` so if you
1160 don't catch them they give meaningful HTTP responses.
1161 - added support for alternative encoding error handling and the new
1162 `HTTPUnicodeError` which (if not caught) behaves like a `BadRequest`.
1163 - added `BadRequest.wrap`.
1164 - added ETag support to the SharedDataMiddleware and added an option
1165 to disable caching.
1166 - fixed `is_xhr` on the request objects.
1167 - fixed error handling of the url adapter's `dispatch` method. (#318)
1168 - fixed bug with `SharedDataMiddleware`.
1169 - fixed `Accept.values`.
1170 - `EnvironHeaders` contain content-type and content-length now
1171 - `url_encode` treats lists and tuples in dicts passed to it as multiple
1172 values for the same key so that one doesn't have to pass a `MultiDict`
1173 to the function.
1174 - added `validate_arguments`
1175 - added `BaseRequest.application`
1176 - improved Python 2.3 support
1177 - `run_simple` accepts `use_debugger` and `use_evalex` parameters now,
1178 like the `make_runserver` factory function from the script module.
1179 - the `environ_property` is now read-only by default
1180 - it's now possible to initialize requests as "shallow" requests which
1181 causes runtime errors if the request object tries to consume the
1182 input stream.
1183
1184
1185 Version 0.2
1186 -----------
1187
1188 Released Feb 14th 2008, codename Faustkeil.
1189
1190 - Added `AnyConverter` to the routing system.
1191 - Added `werkzeug.contrib.securecookie`
1192 - Exceptions have a ``get_response()`` method that return a response object
1193 - fixed the path ordering bug (#293), thanks Thomas Johansson
1194 - `BaseReporterStream` is now part of the werkzeug contrib module. From
1195 Werkzeug 0.3 onwards you will have to import it from there.
1196 - added `DispatcherMiddleware`.
1197 - `RequestRedirect` is now a subclass of `HTTPException` and uses a
1198 301 status code instead of 302.
1199 - `url_encode` and `url_decode` can optionally treat keys as unicode strings
1200 now, too.
1201 - `werkzeug.script` has a different caller format for boolean arguments now.
1202 - renamed `lazy_property` to `cached_property`.
1203 - added `import_string`.
1204 - added is_* properties to request objects.
1205 - added `empty()` method to routing rules.
1206 - added `werkzeug.contrib.profiler`.
1207 - added `extends` to `Headers`.
1208 - added `dump_cookie` and `parse_cookie`.
1209 - added `as_tuple` to the `Client`.
1210 - added `werkzeug.contrib.testtools`.
1211 - added `werkzeug.unescape`
1212 - added `BaseResponse.freeze`
1213 - added `werkzeug.contrib.atom`
1214 - the HTTPExceptions accept an argument `description` now which overrides the
1215 default description.
1216 - the `MapAdapter` has a default for path info now. If you use
1217 `bind_to_environ` you don't have to pass the path later.
1218 - the wsgiref subclass werkzeug uses for the dev server does not use direct
1219 sys.stderr logging any more but a logger called "werkzeug".
1220 - implemented `Href`.
1221 - implemented `find_modules`
1222 - refactored request and response objects into base objects, mixins and
1223 full featured subclasses that implement all mixins.
1224 - added simple user agent parser
1225 - werkzeug's routing raises `MethodNotAllowed` now if it matches a
1226 rule but for a different method.
1227 - many fixes and small improvements
1228
1229
1230 Version 0.1
1231 -----------
1232
1233 Released on Dec 9th 2007, codename Wictorinoxger.
1234
1235 - Initial release
0 =============================
10 How to contribute to Werkzeug
21 =============================
32
4 Thanks for considering contributing to Werkzeug.
3 Thank you for considering contributing to Werkzeug!
4
55
66 Support questions
7 =================
7 -----------------
88
9 Please, don't use the issue tracker for this. Check whether the `Pocoo IRC
10 channel <http://www.pocoo.org/irc/>`_ can help with your issue. If your problem
11 is not strictly Werkzeug- or Flask-specific, ``#python`` on Freenode is
12 generally more active. `StackOverflow <https://stackoverflow.com/>`_ is also
13 worth considering.
9 Please, don't use the issue tracker for this. Use one of the following
10 resources for questions about your own code:
11
12 - The IRC channel ``#pocoo`` on FreeNode.
13 - The IRC channel ``#python`` on FreeNode for more general questions.
14 - The mailing list flask@python.org for long term discussion or larger
15 issues.
16 - Ask on `Stack Overflow`_. Search with Google first using:
17 ``site:stackoverflow.com werkzeug {search term, exception message, etc.}``.
18 Be sure to include a `minimal, complete, and verifiable example`_.
19
1420
1521 Reporting issues
16 ================
22 ----------------
1723
18 - Under which versions of Python does this happen? This is even more important
19 if your issue is encoding related.
24 - Describe what you expected to happen.
25 - If possible, include a `minimal, complete, and verifiable example`_ to
26 help us identify the issue. This also helps check that the issue is
27 not with your own code.
28 - Describe what actually happened. Include the full traceback if there
29 was an exception.
30 - List your Python and Werkzeug versions. If possible, check if
31 this issue is already fixed in the repository.
2032
21 - Under which versions of Werkzeug does this happen? Check if this issue is
22 fixed in the repository.
2333
2434 Submitting patches
25 ==================
26
27 - Please do not use pull requests as a way to suggest behavior changes. Open an
28 issue for discussion first. This helps keeping the discussions of concept and
29 implementation separate.
35 ------------------
3036
3137 - Include tests if your patch is supposed to solve a bug, and explain
32 clearly under which circumstances the bug happens. Make sure the test fails
33 without your patch.
34
35 - Try to follow `PEP8 <http://legacy.python.org/dev/peps/pep-0008/>`_, but you
36 may ignore the line-length-limit if following it would make the code uglier.
37
38 - Add an entry to ``CHANGES`` and your name to ``AUTHORS``.
38 clearly under which circumstances the bug happens. Make sure the test
39 fails without your patch.
40 - Follow the `PEP8`_ style guide.
3941
4042
41 Running the testsuite
42 ---------------------
43 First time setup
44 ~~~~~~~~~~~~~~~~
4345
44 You probably want to set up a `virtualenv
45 <https://virtualenv.readthedocs.io/en/latest/index.html>`_.
46 - Download and install the `latest version of git`_.
47 - Configure git with your `username`_ and `email`_::
4648
47 Werkzeug must be installed for all tests to pass::
49 git config --global user.name 'your name'
50 git config --global user.email 'your email'
4851
49 pip install -e .
52 - Make sure you have a `GitHub account`_.
53 - Fork Werkzeug to your GitHub account by clicking the `Fork`_ button.
54 - `Clone`_ your GitHub fork locally::
5055
51 The minimal requirement for running the testsuite is ``py.test``. You can
52 install it with::
56 git clone https://github.com/{username}/werkzeug
57 cd werkzeug
5358
54 pip install pytest
59 - Add the main repository as a remote to update later::
5560
56 Then you can run the testsuite with::
61 git remote add pallets https://github.com/pallets/werkzeug
62 git fetch pallets
5763
58 py.test
64 - Create a virtualenv::
5965
60 With only py.test installed, a large part of the testsuite will get skipped
61 though. Whether this is relevant depends on which part of Werkzeug you're
62 working on. Travis is set up to run the full testsuite when you submit your
63 pull request anyways.
66 python3 -m venv venv
67 . venv/bin/activate
68 # or "venv\Scripts\activate" on Windows
6469
65 If you really want to test everything, you will have to install ``tox`` instead
66 of ``pytest``. You can install it with::
70 - Install Werkzeug in editable mode with development dependencies::
6771
68 pip install tox
72 pip install -e ".[dev]"
6973
70 The ``tox`` command will then run all tests against multiple combinations
71 Python versions and dependency versions.
74
75 Start coding
76 ~~~~~~~~~~~~
77
78 - Create a branch to identify the issue you would like to work on (e.g.
79 ``2287-dry-test-suite``)
80 - Using your favorite editor, make your changes, `committing as you go`_.
81 - Follow the `PEP8`_ style guide.
82 - Include tests that cover any code changes you make. Make sure the test
83 fails without your patch. Run the tests as described below.
84 - Push your commits to GitHub and `create a pull request`_.
85 - Celebrate 🎉
86
87
88 Running the tests
89 ~~~~~~~~~~~~~~~~~
90
91 Run the basic test suite with::
92
93 pytest
94
95 This only runs the tests for the current environment. Whether this is
96 relevant depends on which part of Werkzeug you're working on. Travis-CI
97 will run the full suite when you submit your pull request.
98
99 The full test suite takes a long time to run because it tests multiple
100 combinations of Python and dependencies. You need to have Python 2.7,
101 3.4, 3.5, 3.6, and PyPy 2.7, as well as Redis and memcached installed to
102 run all of the environments. Then run::
103
104 tox
105
106
107 Running test coverage
108 ~~~~~~~~~~~~~~~~~~~~~
109
110 Generating a report of lines that do not have test coverage can indicate
111 where to start contributing. Run ``pytest`` using ``coverage`` and
112 generate a report on the terminal and as an interactive HTML document::
113
114 coverage run -m pytest
115 coverage report
116 coverage html
117 # then open htmlcov/index.html
118
119 Read more about `coverage`_.
120
121 Running the full test suite with ``tox`` will combine the coverage
122 reports from all runs.
123
124
125 .. _Stack Overflow: https://stackoverflow.com/questions/tagged/werkzeug?sort=linked
126 .. _minimal, complete, and verifiable example: https://stackoverflow.com/help/mcve
127 .. _GitHub account: https://github.com/join
128 .. _latest version of git: https://git-scm.com/downloads
129 .. _username: https://help.github.com/articles/setting-your-username-in-git/
130 .. _email: https://help.github.com/articles/setting-your-email-in-git/
131 .. _Fork: https://github.com/pallets/werkzeug/pull/2305#fork-destination-box
132 .. _Clone: https://help.github.com/articles/fork-a-repo/#step-2-create-a-local-clone-of-your-fork
133 .. _committing as you go: http://dont-be-afraid-to-commit.readthedocs.io/en/latest/git/commandlinegit.html#commit-your-changes
134 .. _PEP8: https://pep8.org/
135 .. _create a pull request: https://help.github.com/articles/creating-a-pull-request/
136 .. _coverage: https://coverage.readthedocs.io
0 Copyright (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
1
2 Redistribution and use in source and binary forms, with or without
3 modification, are permitted provided that the following conditions are
4 met:
5
6 * Redistributions of source code must retain the above copyright
7 notice, this list of conditions and the following disclaimer.
8
9 * Redistributions in binary form must reproduce the above
10 copyright notice, this list of conditions and the following
11 disclaimer in the documentation and/or other materials provided
12 with the distribution.
13
14 * The names of the contributors may not be used to endorse or
15 promote products derived from this software without specific
16 prior written permission.
17
18 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0 Copyright © 2007 by the Pallets team.
1
2 Some rights reserved.
3
4 Redistribution and use in source and binary forms, with or without
5 modification, are permitted provided that the following conditions are
6 met:
7
8 * Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
10
11 * Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14
15 * Neither the name of the copyright holder nor the names of its
16 contributors may be used to endorse or promote products derived from
17 this software without specific prior written permission.
18
19 THIS SOFTWARE AND DOCUMENTATION IS PROVIDED BY THE COPYRIGHT HOLDERS AND
20 CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING,
21 BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
25 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
26 USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
27 ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
29 THIS SOFTWARE AND DOCUMENTATION, EVEN IF ADVISED OF THE POSSIBILITY OF
30 SUCH DAMAGE.
0 include Makefile CHANGES LICENSE AUTHORS
1 recursive-include werkzeug/debug/shared *
2 recursive-include tests *
3 recursive-include docs *
4 recursive-include artwork *
5 recursive-include examples *
6
0 include CHANGES.rst LICENSE AUTHORS tox.ini
1 graft werkzeug/debug/shared
2 graft tests
3 graft docs
4 graft artwork
5 graft examples
76 prune docs/_build
87 prune docs/_themes
9 global-exclude *.py[cdo] __pycache__ *.so *.pyd
8 global-exclude *.py[cdo] __pycache__ *.so
0 #
1 # Werkzeug Makefile
2 # ~~~~~~~~~~~~~~~~~
3 #
4 # Shortcuts for various tasks.
5 #
6 # :copyright: (c) 2008 by the Werkzeug Team, see AUTHORS for more details.
7 # :license: BSD, see LICENSE for more details.
8 #
9
100 documentation:
111 @(cd docs; make html)
122
144 python scripts/make-release.py
155
166 test:
17 py.test --tb=native
7 pytest
188
199 tox-test:
2010 tox
2111
2212 coverage:
23 @(coverage run --source=werkzeug --module py.test $(TEST_OPTIONS) $(TESTS))
13 @(coverage run --module pytest $(TEST_OPTIONS) $(TESTS))
2414
2515 doctest:
2616 @(cd docs; sphinx-build -b doctest . _build/doctest)
00 Werkzeug
11 ========
22
3 Werkzeug started as simple collection of various utilities for WSGI
4 applications and has become one of the most advanced WSGI utility
5 modules. It includes a powerful debugger, full-featured request and
6 response objects, HTTP utilities to handle entity tags, cache control
7 headers, HTTP dates, cookie handling, file uploads, a powerful URL
8 routing system and a bunch of community-contributed addon modules.
3 Werkzeug is a comprehensive `WSGI`_ web application library. It began as
4 a simple collection of various utilities for WSGI applications and has
5 become one of the most advanced WSGI utility libraries.
96
10 Werkzeug is unicode aware and doesn't enforce a specific template
11 engine, database adapter or anything else. It doesn't even enforce
12 a specific way of handling requests and leaves all that up to the
13 developer. It's most useful for end user applications which should work
14 on as many server environments as possible (such as blogs, wikis,
15 bulletin boards, etc.).
7 It includes:
168
17 Details and example applications are available on the
18 `Werkzeug website <http://werkzeug.pocoo.org/>`_.
9 * An interactive debugger that allows inspecting stack traces and source
10 code in the browser with an interactive interpreter for any frame in
11 the stack.
12 * A full-featured request object with objects to interact with headers,
13 query args, form data, files, and cookies.
14 * A response object that can wrap other WSGI applications and handle
15 streaming data.
16 * A routing system for matching URLs to endpoints and generating URLs
17 for endpoints, with an extensible system for capturing variables from
18 URLs.
19 * HTTP utilities to handle entity tags, cache control, dates, user
20 agents, cookies, files, and more.
21 * A threaded WSGI server for use while developing applications locally.
22 * A test client for simulating HTTP requests during testing without
23 requiring running a server.
24
25 Werkzeug is Unicode aware and doesn't enforce any dependencies. It is up
26 to the developer to choose a template engine, database adapter, and even
27 how to handle requests. It can be used to build all sorts of end user
28 applications such as blogs, wikis, or bulletin boards.
29
30 `Flask`_ wraps Werkzeug, using it to handle the details of WSGI while
31 providing more structure and patterns for defining powerful
32 applications.
1933
2034
21 Branches
22 --------
35 Installing
36 ----------
2337
24 +----------------------+-------------------------------------------------------------------------------+
25 | ``master`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=master |
26 | | :target: https://travis-ci.org/pallets/werkzeug |
27 +----------------------+-------------------------------------------------------------------------------+
28 | ``0.12-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.12-maintenance |
29 | | :target: https://travis-ci.org/pallets/werkzeug |
30 +----------------------+-------------------------------------------------------------------------------+
31 | ``0.11-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.11-maintenance |
32 | | :target: https://travis-ci.org/pallets/werkzeug |
33 +----------------------+-------------------------------------------------------------------------------+
34 | ``0.10-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.10-maintenance |
35 | | :target: https://travis-ci.org/pallets/werkzeug |
36 +----------------------+-------------------------------------------------------------------------------+
37 | ``0.9-maintenance`` | .. image:: https://travis-ci.org/pallets/werkzeug.svg?branch=0.9-maintenance |
38 | | :target: https://travis-ci.org/pallets/werkzeug |
39 +----------------------+-------------------------------------------------------------------------------+
38 Install and update using `pip`_:
39
40 .. code-block:: text
41
42 pip install -U Werkzeug
43
44
45 A Simple Example
46 ----------------
47
48 .. code-block:: python
49
50 from werkzeug.wrappers import Request, Response
51
52 @Request.application
53 def application(request):
54 return Response('Hello, World!')
55
56 if __name__ == '__main__':
57 from werkzeug.serving import run_simple
58 run_simple('localhost', 4000, application)
59
60
61 Links
62 -----
63
64 * Website: https://www.palletsprojects.com/p/werkzeug/
65 * Releases: https://pypi.org/project/Werkzeug/
66 * Code: https://github.com/pallets/werkzeug
67 * Issue tracker: https://github.com/pallets/werkzeug/issues
68 * Test status:
69
70 * Linux, Mac: https://travis-ci.org/pallets/werkzeug
71 * Windows: https://ci.appveyor.com/project/davidism/werkzeug
72
73 * Test coverage: https://codecov.io/gh/pallets/werkzeug
74
75 .. _WSGI: https://wsgi.readthedocs.io/en/latest/
76 .. _Flask: https://www.palletsprojects.com/p/flask/
77 .. _pip: https://pip.pypa.io/en/stable/quickstart/
+0
-18
appveyor.yml less more
0 build: false # Not a C# project, build stuff at the test step instead.
1 environment:
2 matrix:
3 - PYTHON: "C:/Python27"
4 - PYTHON: "C:/Python33"
5 - PYTHON: "C:/Python34"
6
7 init:
8 - "ECHO %PYTHON%"
9 - ps: "ls C:/Python*"
10
11 install:
12 - ps: (new-object net.webclient).DownloadFile('https://bootstrap.pypa.io/get-pip.py', 'C:/get-pip.py')
13 - "%PYTHON%/python.exe C:/get-pip.py"
14 - "%PYTHON%/Scripts/pip.exe install tox"
15
16 test_script:
17 - "%PYTHON%/Scripts/tox.exe -e py-normal"
77 For API breaking changes have a look at :ref:`api-changes`, they
88 are listed there in detail.
99
10 .. include:: ../CHANGES
10 .. include:: ../CHANGES.rst
1111
1212 .. _api-changes:
1313
6262 Do not use:
6363
6464 - `werkzeug.script`, replace it with custom scripts written with
65 `argparse` or something similar.
65 `argparse`, `click` or something similar.
6666 - `werkzeug.template`, replace with a proper template engine.
6767 - `werkzeug.contrib.jsrouting`, stop using URL generation for
6868 JavaScript, it does not scale well with many public routing.
66
77 Beside the proof of concept applications and code snippets in the partial
88 folder they all have external depencencies for template engines or database
9 adapters (SQLAlchemy only so far).
9 adapters (SQLAlchemy only so far). Also, every application has click as
10 external dependency, used to create the command line interface.
1011
1112
1213 Full Example Applications
6666 app = CoolMagicApplication(config)
6767
6868 # static stuff
69 from werkzeug.utils import SharedDataMiddleware
69 from werkzeug.wsgi import SharedDataMiddleware
7070 app = SharedDataMiddleware(app, {
7171 '/public': path.join(path.dirname(__file__), 'public')
7272 })
00 from datetime import datetime
1 from couchdb.schema import Document, TextField, BooleanField, DateTimeField
1 from couchdb.mapping import Document, TextField, BooleanField, DateTimeField
22 from couchy.utils import url_for, get_random_uid
33
44
88 :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
99 :license: BSD, see LICENSE for more details.
1010 """
11 import os
11 import click
1212 from coolmagic import make_app
13 from werkzeug import script
13 from werkzeug.serving import run_simple
1414
15 action_runserver = script.make_runserver(make_app, use_reloader=True)
16 action_shell = script.make_shell(lambda: {})
15
16 @click.group()
17 def cli():
18 pass
19
20
21 @cli.command()
22 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
23 @click.option('-p', '--port', type=int, default=5000, help="5000")
24 @click.option('--no-reloader', is_flag=True, default=False)
25 @click.option('--debugger', is_flag=True)
26 @click.option('--no-evalex', is_flag=True, default=False)
27 @click.option('--threaded', is_flag=True)
28 @click.option('--processes', type=int, default=1, help="1")
29 def runserver(hostname, port, no_reloader, debugger, no_evalex, threaded, processes):
30 """Start a new development server."""
31 app = make_app()
32 reloader = not no_reloader
33 evalex = not no_evalex
34 run_simple(hostname, port, app,
35 use_reloader=reloader, use_debugger=debugger,
36 use_evalex=evalex, threaded=threaded, processes=processes)
37
38
39 @cli.command()
40 @click.option('--no-ipython', is_flag=True, default=False)
41 def shell(no_ipython):
42 """Start a new interactive python session."""
43 banner = 'Interactive Werkzeug Shell'
44 namespace = dict()
45 if not no_ipython:
46 try:
47 try:
48 from IPython.frontend.terminal.embed import InteractiveShellEmbed
49 sh = InteractiveShellEmbed.instance(banner1=banner)
50 except ImportError:
51 from IPython.Shell import IPShellEmbed
52 sh = IPShellEmbed(banner=banner)
53 except ImportError:
54 pass
55 else:
56 sh(local_ns=namespace)
57 return
58 from code import interact
59 interact(banner, local=namespace)
1760
1861 if __name__ == '__main__':
19 script.run()
62 cli()
00 #!/usr/bin/env python
1 from werkzeug import script
1 import click
2 from werkzeug.serving import run_simple
3
24
35 def make_app():
46 from couchy.application import Couchy
57 return Couchy('http://localhost:5984')
8
69
710 def make_shell():
811 from couchy import models, utils
912 application = make_app()
1013 return locals()
1114
12 action_runserver = script.make_runserver(make_app, use_reloader=True)
13 action_shell = script.make_shell(make_shell)
14 action_initdb = lambda: make_app().init_database()
1515
16 script.run()
16 @click.group()
17 def cli():
18 pass
19
20
21 @cli.command()
22 def initdb():
23 from couchy.application import Couchy
24 Couchy('http://localhost:5984').init_database()
25
26
27 @cli.command()
28 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
29 @click.option('-p', '--port', type=int, default=5000, help="5000")
30 @click.option('--no-reloader', is_flag=True, default=False)
31 @click.option('--debugger', is_flag=True)
32 @click.option('--no-evalex', is_flag=True, default=False)
33 @click.option('--threaded', is_flag=True)
34 @click.option('--processes', type=int, default=1, help="1")
35 def runserver(hostname, port, no_reloader, debugger, no_evalex, threaded, processes):
36 """Start a new development server."""
37 app = make_app()
38 reloader = not no_reloader
39 evalex = not no_evalex
40 run_simple(hostname, port, app,
41 use_reloader=reloader, use_debugger=debugger,
42 use_evalex=evalex, threaded=threaded, processes=processes)
43
44
45 @cli.command()
46 @click.option('--no-ipython', is_flag=True, default=False)
47 def shell(no_ipython):
48 """Start a new interactive python session."""
49 banner = 'Interactive Werkzeug Shell'
50 namespace = make_shell()
51 if not no_ipython:
52 try:
53 try:
54 from IPython.frontend.terminal.embed import InteractiveShellEmbed
55 sh = InteractiveShellEmbed.instance(banner1=banner)
56 except ImportError:
57 from IPython.Shell import IPShellEmbed
58 sh = IPShellEmbed(banner=banner)
59 except ImportError:
60 pass
61 else:
62 sh(local_ns=namespace)
63 return
64 from code import interact
65 interact(banner, local=namespace)
66
67 if __name__ == '__main__':
68 cli()
77 :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
88 :license: BSD, see LICENSE for more details.
99 """
10 from werkzeug import script
10 import click
11 from werkzeug.serving import run_simple
1112
1213
1314 def make_app():
1415 from cupoftee import make_app
1516 return make_app('/tmp/cupoftee.db')
16 action_runserver = script.make_runserver(make_app)
1717
18 script.run()
18
19 @click.group()
20 def cli():
21 pass
22
23
24 @cli.command()
25 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
26 @click.option('-p', '--port', type=int, default=5000, help="5000")
27 @click.option('--reloader', is_flag=True, default=False)
28 @click.option('--debugger', is_flag=True)
29 @click.option('--evalex', is_flag=True, default=False)
30 @click.option('--threaded', is_flag=True)
31 @click.option('--processes', type=int, default=1, help="1")
32 def runserver(hostname, port, reloader, debugger, evalex, threaded, processes):
33 """Start a new development server."""
34 app = make_app()
35 run_simple(hostname, port, app,
36 use_reloader=reloader, use_debugger=debugger,
37 use_evalex=evalex, threaded=threaded, processes=processes)
38
39
40 if __name__ == '__main__':
41 cli()
88 :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
99 :license: BSD, see LICENSE for more details.
1010 """
11 import os
11 import click
1212 from i18nurls import make_app
13 from werkzeug import script
13 from werkzeug.serving import run_simple
1414
15 action_runserver = script.make_runserver(make_app)
16 action_shell = script.make_shell(lambda: {})
15
16 @click.group()
17 def cli():
18 pass
19
20
21 @cli.command()
22 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
23 @click.option('-p', '--port', type=int, default=5000, help="5000")
24 @click.option('--no-reloader', is_flag=True, default=False)
25 @click.option('--debugger', is_flag=True)
26 @click.option('--no-evalex', is_flag=True, default=False)
27 @click.option('--threaded', is_flag=True)
28 @click.option('--processes', type=int, default=1, help="1")
29 def runserver(hostname, port, no_reloader, debugger, no_evalex, threaded, processes):
30 """Start a new development server."""
31 app = make_app()
32 reloader = not no_reloader
33 evalex = not no_evalex
34 run_simple(hostname, port, app,
35 use_reloader=reloader, use_debugger=debugger,
36 use_evalex=evalex, threaded=threaded, processes=processes)
37
38
39 @cli.command()
40 @click.option('--no-ipython', is_flag=True, default=False)
41 def shell(no_ipython):
42 """Start a new interactive python session."""
43 banner = 'Interactive Werkzeug Shell'
44 namespace = dict()
45 if not no_ipython:
46 try:
47 try:
48 from IPython.frontend.terminal.embed import InteractiveShellEmbed
49 sh = InteractiveShellEmbed.instance(banner1=banner)
50 except ImportError:
51 from IPython.Shell import IPShellEmbed
52 sh = IPShellEmbed(banner=banner)
53 except ImportError:
54 pass
55 else:
56 sh(local_ns=namespace)
57 return
58 from code import interact
59 interact(banner, local=namespace)
1760
1861 if __name__ == '__main__':
19 script.run()
62 cli()
88 :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
99 :license: BSD, see LICENSE for more details.
1010 """
11 import click
1112 import os
12 from werkzeug import script
13 from werkzeug.serving import run_simple
1314
1415
1516 def make_app():
2122 return app
2223
2324
24 action_runserver = script.make_runserver(make_app, use_reloader=True)
25 action_shell = script.make_shell(lambda: {'app': make_app()})
25 @click.group()
26 def cli():
27 pass
2628
2729
28 def action_initdb():
30 @cli.command()
31 def initdb():
2932 """Initialize the database"""
3033 from plnt.database import Blog, session
3134 make_app().init_database()
5053 for blog in blogs:
5154 session.add(blog)
5255 session.commit()
53 print 'Initialized database, now run manage-plnt.py sync to get the posts'
56 click.echo('Initialized database, now run manage-plnt.py sync to get the posts')
5457
5558
56 def action_sync():
59 @cli.command()
60 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
61 @click.option('-p', '--port', type=int, default=5000, help="5000")
62 @click.option('--no-reloader', is_flag=True, default=False)
63 @click.option('--debugger', is_flag=True)
64 @click.option('--no-evalex', is_flag=True, default=False)
65 @click.option('--threaded', is_flag=True)
66 @click.option('--processes', type=int, default=1, help="1")
67 def runserver(hostname, port, no_reloader, debugger, no_evalex, threaded, processes):
68 """Start a new development server."""
69 app = make_app()
70 reloader = not no_reloader
71 evalex = not no_evalex
72 run_simple(hostname, port, app,
73 use_reloader=reloader, use_debugger=debugger,
74 use_evalex=evalex, threaded=threaded, processes=processes)
75
76
77 @cli.command()
78 @click.option('--no-ipython', is_flag=True, default=False)
79 def shell(no_ipython):
80 """Start a new interactive python session."""
81 banner = 'Interactive Werkzeug Shell'
82 namespace = {'app': make_app()}
83 if not no_ipython:
84 try:
85 try:
86 from IPython.frontend.terminal.embed import InteractiveShellEmbed
87 sh = InteractiveShellEmbed.instance(banner1=banner)
88 except ImportError:
89 from IPython.Shell import IPShellEmbed
90 sh = IPShellEmbed(banner=banner)
91 except ImportError:
92 pass
93 else:
94 sh(local_ns=namespace)
95 return
96 from code import interact
97 interact(banner, local=namespace)
98
99
100 @cli.command()
101 def sync():
57102 """Sync the blogs in the planet. Call this from a cronjob."""
58103 from plnt.sync import sync
59104 make_app().bind_to_context()
60105 sync()
61106
62
63107 if __name__ == '__main__':
64 script.run()
108 cli()
00 #!/usr/bin/env python
1 import click
12 import os
23 import tempfile
3 from werkzeug import script
4 from werkzeug.serving import run_simple
5
46
57 def make_app():
68 from shorty.application import Shorty
79 filename = os.path.join(tempfile.gettempdir(), "shorty.db")
810 return Shorty('sqlite:///{0}'.format(filename))
911
12
1013 def make_shell():
1114 from shorty import models, utils
1215 application = make_app()
1316 return locals()
1417
15 action_runserver = script.make_runserver(make_app, use_reloader=True)
16 action_shell = script.make_shell(make_shell)
17 action_initdb = lambda: make_app().init_database()
1818
19 script.run()
19 @click.group()
20 def cli():
21 pass
22
23
24 @cli.command()
25 def initdb():
26 make_app().init_database()
27
28
29 @cli.command()
30 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
31 @click.option('-p', '--port', type=int, default=5000, help="5000")
32 @click.option('--no-reloader', is_flag=True, default=False)
33 @click.option('--debugger', is_flag=True)
34 @click.option('--no-evalex', is_flag=True, default=False)
35 @click.option('--threaded', is_flag=True)
36 @click.option('--processes', type=int, default=1, help="1")
37 def runserver(hostname, port, no_reloader, debugger, no_evalex, threaded, processes):
38 """Start a new development server."""
39 app = make_app()
40 reloader = not no_reloader
41 evalex = not no_evalex
42 run_simple(hostname, port, app,
43 use_reloader=reloader, use_debugger=debugger,
44 use_evalex=evalex, threaded=threaded, processes=processes)
45
46
47 @cli.command()
48 @click.option('--no-ipython', is_flag=True, default=False)
49 def shell(no_ipython):
50 """Start a new interactive python session."""
51 banner = 'Interactive Werkzeug Shell'
52 namespace = make_shell()
53 if not no_ipython:
54 try:
55 try:
56 from IPython.frontend.terminal.embed import InteractiveShellEmbed
57 sh = InteractiveShellEmbed.instance(banner1=banner)
58 except ImportError:
59 from IPython.Shell import IPShellEmbed
60 sh = IPShellEmbed(banner=banner)
61 except ImportError:
62 pass
63 else:
64 sh(local_ns=namespace)
65 return
66 from code import interact
67 interact(banner, local=namespace)
68
69 if __name__ == '__main__':
70 cli()
88 :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
99 :license: BSD, see LICENSE for more details.
1010 """
11 import click
1112 import os
12 from werkzeug import script
13 import tempfile
14 from werkzeug.serving import run_simple
1315
1416
1517 def make_wiki():
1921 return SimpleWiki(database_uri or 'sqlite:////tmp/simplewiki.db')
2022
2123
22 def shell_init_func():
23 """
24 Called on shell initialization. Adds useful stuff to the namespace.
25 """
24 def make_shell():
2625 from simplewiki import database
2726 wiki = make_wiki()
2827 wiki.bind_to_context()
3231 }
3332
3433
35 action_runserver = script.make_runserver(make_wiki, use_reloader=True)
36 action_shell = script.make_shell(shell_init_func)
34 @click.group()
35 def cli():
36 pass
3737
3838
39 def action_initdb():
40 """Initialize the database"""
39 @cli.command()
40 def initdb():
4141 make_wiki().init_database()
4242
4343
44 @cli.command()
45 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
46 @click.option('-p', '--port', type=int, default=5000, help="5000")
47 @click.option('--no-reloader', is_flag=True, default=False)
48 @click.option('--debugger', is_flag=True)
49 @click.option('--no-evalex', is_flag=True, default=False)
50 @click.option('--threaded', is_flag=True)
51 @click.option('--processes', type=int, default=1, help="1")
52 def runserver(hostname, port, no_reloader, debugger, no_evalex, threaded, processes):
53 """Start a new development server."""
54 app = make_wiki()
55 reloader = not no_reloader
56 evalex = not no_evalex
57 run_simple(hostname, port, app,
58 use_reloader=reloader, use_debugger=debugger,
59 use_evalex=evalex, threaded=threaded, processes=processes)
60
61
62 @cli.command()
63 @click.option('--no-ipython', is_flag=True, default=False)
64 def shell(no_ipython):
65 """Start a new interactive python session."""
66 banner = 'Interactive Werkzeug Shell'
67 namespace = make_shell()
68 if not no_ipython:
69 try:
70 try:
71 from IPython.frontend.terminal.embed import InteractiveShellEmbed
72 sh = InteractiveShellEmbed.instance(banner1=banner)
73 except ImportError:
74 from IPython.Shell import IPShellEmbed
75 sh = IPShellEmbed(banner=banner)
76 except ImportError:
77 pass
78 else:
79 sh(local_ns=namespace)
80 return
81 from code import interact
82 interact(banner, local=namespace)
83
4484 if __name__ == '__main__':
45 script.run()
85 cli()
1212 :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details.
1313 :license: BSD, see LICENSE for more details.
1414 """
15 import click
1516 import os
1617 import sys
1718 sys.path.append(os.path.join(os.path.dirname(__file__), 'webpylike'))
1819 from example import app
19 from werkzeug import script
20 from werkzeug.serving import run_simple
2021
21 action_runserver = script.make_runserver(lambda: app)
22 action_shell = script.make_shell(lambda: {})
22
23 @click.group()
24 def cli():
25 pass
26
27
28 @cli.command()
29 @click.option('-h', '--hostname', type=str, default='localhost', help="localhost")
30 @click.option('-p', '--port', type=int, default=5000, help="5000")
31 @click.option('--no-reloader', is_flag=True, default=False)
32 @click.option('--debugger', is_flag=True)
33 @click.option('--no-evalex', is_flag=True, default=False)
34 @click.option('--threaded', is_flag=True)
35 @click.option('--processes', type=int, default=1, help="1")
36 def runserver(hostname, port, no_reloader, debugger, no_evalex, threaded, processes):
37 """Start a new development server."""
38 reloader = not no_reloader
39 evalex = not no_evalex
40 run_simple(hostname, port, app,
41 use_reloader=reloader, use_debugger=debugger,
42 use_evalex=evalex, threaded=threaded, processes=processes)
43
44
45 @cli.command()
46 @click.option('--no-ipython', is_flag=True, default=False)
47 def shell(no_ipython):
48 """Start a new interactive python session."""
49 banner = 'Interactive Werkzeug Shell'
50 namespace = dict()
51 if not no_ipython:
52 try:
53 try:
54 from IPython.frontend.terminal.embed import InteractiveShellEmbed
55 sh = InteractiveShellEmbed.instance(banner1=banner)
56 except ImportError:
57 from IPython.Shell import IPShellEmbed
58 sh = IPShellEmbed(banner=banner)
59 except ImportError:
60 pass
61 else:
62 sh(local_ns=namespace)
63 return
64 from code import interact
65 interact(banner, local=namespace)
2366
2467 if __name__ == '__main__':
25 script.run()
68 cli()
00 #!/usr/bin/env python
11 # -*- coding: utf-8 -*-
2 """
3 make-release
4 ~~~~~~~~~~~~
2 from __future__ import print_function
53
6 Helper script that performs a release. Does pretty much everything
7 automatically for us.
8
9 :copyright: (c) 2014 by Armin Ronacher.
10 :license: BSD, see LICENSE for more details.
11 """
12 import sys
13 import shutil
144 import os
155 import re
16 from datetime import datetime, date
17 from subprocess import Popen, PIPE
6 import sys
7 from datetime import date, datetime
8 from subprocess import PIPE, Popen
189
19 _date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)')
10 _date_strip_re = re.compile(r'(?<=\d)(st|nd|rd|th)')
2011
2112
2213 def parse_changelog():
23 with open('CHANGES') as f:
14 with open('CHANGES.rst') as f:
2415 lineiter = iter(f)
2516 for line in lineiter:
2617 match = re.search('^Version\s+(.*)', line.strip())
18
2719 if match is None:
2820 continue
21
2922 version = match.group(1).strip()
30 if lineiter.next().count('-') != len(match.group(0)):
23
24 if next(lineiter).count('-') != len(match.group(0)):
3125 continue
26
3227 while 1:
33 change_info = lineiter.next().strip()
28 change_info = next(lineiter).strip()
29
3430 if change_info:
3531 break
3632
37 match = re.search(r'released on (\w+\s+\d+\w+\s+\d+)'
38 r'(?:, codename (.*))?', change_info,
39 flags=re.IGNORECASE)
33 match = re.search(
34 r'released on (\w+\s+\d+\w+\s+\d+)(?:, codename (.*))?',
35 change_info,
36 flags=re.IGNORECASE
37 )
38
4039 if match is None:
4140 continue
4241
4645
4746 def bump_version(version):
4847 try:
49 parts = map(int, version.split('.'))
48 parts = [int(i) for i in version.split('.')]
5049 except ValueError:
5150 fail('Current version is not numeric')
51
5252 parts[-1] += 1
5353 return '.'.join(map(str, parts))
5454
5555
5656 def parse_date(string):
57 string = _date_clean_re.sub(r'\1', string)
57 string = _date_strip_re.sub('', string)
5858 return datetime.strptime(string, '%B %d %Y')
5959
6060
6565 before, old, after = match.groups()
6666 changed.append(True)
6767 return before + version_number + after
68
6869 with open(filename) as f:
69 contents = re.sub(r"^(\s*%s\s*=\s*')(.+?)(')" % pattern,
70 inject_version, f.read(),
71 flags=re.DOTALL | re.MULTILINE)
70 contents = re.sub(
71 r"^(\s*%s\s*=\s*')(.+?)(')" % pattern,
72 inject_version, f.read(),
73 flags=re.DOTALL | re.MULTILINE
74 )
7275
7376 if not changed:
7477 fail('Could not find %s in %s', pattern, filename)
8285 set_filename_version('werkzeug/__init__.py', version, '__version__')
8386
8487
85 def build_and_upload():
86 # Work around setuptools packaging stray .pyc files
87 if os.path.exists('werkzeug/testsuite'):
88 shutil.rmtree('werkzeug/testsuite')
89
90 Popen([sys.executable, 'setup.py', 'release', 'sdist', 'bdist_wheel',
91 'upload']).wait()
88 def build():
89 cmd = [sys.executable, 'setup.py', 'sdist', 'bdist_wheel']
90 Popen(cmd).wait()
9291
9392
9493 def fail(message, *args):
95 print >> sys.stderr, 'Error:', message % args
94 print('Error:', message % args, file=sys.stderr)
9695 sys.exit(1)
9796
9897
9998 def info(message, *args):
100 print >> sys.stderr, message % args
99 print(message % args, file=sys.stderr)
101100
102101
103102 def get_git_tags():
104 return set(Popen(['git', 'tag'], stdout=PIPE)
105 .communicate()[0]
106 .splitlines())
103 return set(
104 Popen(['git', 'tag'], stdout=PIPE).communicate()[0].splitlines()
105 )
107106
108107
109108 def git_is_clean():
124123 os.chdir(os.path.join(os.path.dirname(__file__), '..'))
125124
126125 rv = parse_changelog()
126
127127 if rv is None:
128128 fail('Could not parse changelog')
129129
130130 version, release_date, codename = rv
131 dev_version = bump_version(version) + '-dev'
131 dev_version = bump_version(version) + '.dev'
132132
133 info('Releasing %s (codename %s, release date %s)',
134 version, codename, release_date.strftime('%d/%m/%Y'))
133 info(
134 'Releasing %s (codename %s, release date %s)',
135 version, codename, release_date.strftime('%d/%m/%Y')
136 )
135137 tags = get_git_tags()
136138
137139 if version in tags:
138140 fail('Version "%s" is already tagged', version)
141
139142 if release_date.date() != date.today():
140 fail('Release date is not today (%s != %s)',
141 release_date.date(), date.today())
143 fail(
144 'Release date is not today (%s != %s)',
145 release_date.date(), date.today()
146 )
142147
143148 if not git_is_clean():
144149 fail('You have uncommitted changes in git')
145150
151 try:
152 import wheel # noqa: F401
153 except ImportError:
154 fail('You need to install the wheel package.')
155
146156 set_init_version(version)
147157 make_git_commit('Bump version number to %s', version)
148158 make_git_tag(version)
149 build_and_upload()
159 build()
150160 set_init_version(dev_version)
151161
152162
0 [aliases]
1 release = egg_info -RDb ''
2
30 [tool:pytest]
4 norecursedirs = .* env* docs *.egg tests/hypothesis
1 minversion = 3.0
2 testpaths = tests
3 norecursedirs = tests/hypothesis
54
65 [bdist_wheel]
76 universal = 1
0 # -*- coding: utf-8 -*-
1 """
2 Werkzeug
3 ========
0 #!/usr/bin/env python
1 import io
2 import re
43
5 Werkzeug started as simple collection of various utilities for WSGI
6 applications and has become one of the most advanced WSGI utility
7 modules. It includes a powerful debugger, full featured request and
8 response objects, HTTP utilities to handle entity tags, cache control
9 headers, HTTP dates, cookie handling, file uploads, a powerful URL
10 routing system and a bunch of community contributed addon modules.
4 from setuptools import find_packages, setup
115
12 Werkzeug is unicode aware and doesn't enforce a specific template
13 engine, database adapter or anything else. It doesn't even enforce
14 a specific way of handling requests and leaves all that up to the
15 developer. It's most useful for end user applications which should work
16 on as many server environments as possible (such as blogs, wikis,
17 bulletin boards, etc.).
6 with io.open('README.rst', 'rt', encoding='utf8') as f:
7 readme = f.read()
188
19 Details and example applications are available on the
20 `Werkzeug website <http://werkzeug.pocoo.org/>`_.
21
22
23 Features
24 --------
25
26 - unicode awareness
27
28 - request and response objects
29
30 - various utility functions for dealing with HTTP headers such as
31 `Accept` and `Cache-Control` headers.
32
33 - thread local objects with proper cleanup at request end
34
35 - an interactive debugger
36
37 - A simple WSGI server with support for threading and forking
38 with an automatic reloader.
39
40 - a flexible URL routing system with REST support.
41
42 - fully WSGI compatible
43
44
45 Development Version
46 -------------------
47
48 The Werkzeug development version can be installed by cloning the git
49 repository from `github`_::
50
51 git clone git@github.com:pallets/werkzeug.git
52
53 .. _github: http://github.com/pallets/werkzeug
54 """
55 import ast
56 import re
57 try:
58 from setuptools import setup, Command
59 except ImportError:
60 from distutils.core import setup, Command
61
62
63 _version_re = re.compile(r'__version__\s+=\s+(.*)')
64
65 with open('werkzeug/__init__.py', 'rb') as f:
66 version = str(ast.literal_eval(_version_re.search(
67 f.read().decode('utf-8')).group(1)))
68
69
70 class TestCommand(Command):
71 user_options = []
72
73 def initialize_options(self):
74 pass
75
76 def finalize_options(self):
77 pass
78
79 def run(self):
80 import pytest
81 pytest.cmdline.main(args=[])
82
9 with io.open('werkzeug/__init__.py', 'rt', encoding='utf8') as f:
10 version = re.search(
11 r'__version__ = \'(.*?)\'', f.read(), re.M).group(1)
8312
8413 setup(
8514 name='Werkzeug',
8615 version=version,
87 url='http://werkzeug.pocoo.org/',
16 url='https://www.palletsprojects.org/p/werkzeug/',
8817 license='BSD',
8918 author='Armin Ronacher',
9019 author_email='armin.ronacher@active-4.com',
91 description='The Swiss Army knife of Python web development',
92 long_description=__doc__,
20 description='The comprehensive WSGI web application library.',
21 long_description=readme,
9322 classifiers=[
9423 'Development Status :: 5 - Production/Stable',
9524 'Environment :: Web Environment',
10433 'Programming Language :: Python :: 3.3',
10534 'Programming Language :: Python :: 3.4',
10635 'Programming Language :: Python :: 3.5',
36 'Programming Language :: Python :: 3.6',
10737 'Topic :: Internet :: WWW/HTTP :: Dynamic Content',
108 'Topic :: Software Development :: Libraries :: Python Modules'
38 'Topic :: Software Development :: Libraries :: Python Modules',
10939 ],
110 packages=['werkzeug', 'werkzeug.debug', 'werkzeug.contrib'],
40 packages=find_packages(exclude=('tests*',)),
11141 extras_require={
11242 'watchdog': ['watchdog'],
11343 'termcolor': ['termcolor'],
44 'dev': [
45 'pytest',
46 'coverage',
47 'tox',
48 'sphinx',
49 ],
11450 },
115 cmdclass=dict(test=TestCommand),
11651 include_package_data=True,
11752 zip_safe=False,
11853 platforms='any'
0 # -*- coding: utf-8 -*-
1 """
2 tests
3 ~~~~~
4
5 Contains all test Werkzeug tests.
6
7 :copyright: (c) 2014 by Armin Ronacher.
8 :license: BSD, see LICENSE for more details.
9 """
10 from __future__ import with_statement
11
12
130 def strict_eq(x, y):
14 '''Equality test bypassing the implicit string conversion in Python 2'''
1 """Equality test bypassing the implicit string conversion in
2 Python 2."""
153 __tracebackhide__ = True
164 assert x == y
175 assert issubclass(type(x), type(y)) or issubclass(type(y), type(x))
55 :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
66 :license: BSD, see LICENSE for more details.
77 """
8
9 from __future__ import with_statement
8 from __future__ import with_statement, print_function
109
1110 import os
1211 import signal
2524 try:
2625 __import__('pytest_xprocess')
2726 except ImportError:
28 @pytest.fixture
27 @pytest.fixture(scope='session')
2928 def subprocess():
3029 pytest.skip('pytest-xprocess not installed.')
3130 else:
32 @pytest.fixture
31 @pytest.fixture(scope='session')
3332 def subprocess(xprocess):
3433 return xprocess
3534
3736 def _patch_reloader_loop():
3837 def f(x):
3938 print('reloader loop finished')
39 # Need to flush for some reason even though xprocess opens the
40 # subprocess' stdout in unbuffered mode.
41 # flush=True makes the test fail on py2, so flush manually
42 sys.stdout.flush()
4043 return time.sleep(x)
4144
4245 import werkzeug._reloader
0 # -*- coding: utf-8 -*-
1 """
2 tests.contrib
3 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4
5 Tests the contrib modules.
6
7 :copyright: (c) 2014 by Armin Ronacher.
8 :license: BSD, see LICENSE for more details.
9 """
0 import os
1
2 import pytest
3
4 # build the path to the uwsgi marker file
5 # when running in tox, this will be relative to the tox env
6 filename = os.path.join(
7 os.environ.get('TOX_ENVTMPDIR', ''),
8 'test_uwsgi_failed'
9 )
10
11
12 @pytest.hookimpl(tryfirst=True, hookwrapper=True)
13 def pytest_runtest_makereport(item, call):
14 """``uwsgi --pyrun`` doesn't pass on the exit code when ``pytest`` fails,
15 so Tox thinks the tests passed. For UWSGI tests, create a file to mark what
16 tests fail. The uwsgi Tox env has a command to read this file and exit
17 appropriately.
18 """
19 outcome = yield
20 report = outcome.get_result()
21
22 if item.cls.__name__ != 'TestUWSGICache':
23 return
24
25 if report.failed:
26 with open(filename, 'a') as f:
27 f.write(item.name + '\n')
0 # -*- coding: utf-8 -*-
1 """
2 tests.cache
3 ~~~~~~~~~~~
4
5 Tests the cache system
6
7 :copyright: (c) 2014 by Armin Ronacher.
8 :license: BSD, see LICENSE for more details.
9 """
10 import os
11
12 import errno
13 import pytest
14
15 from werkzeug.contrib import cache
16
17 try:
18 import redis
19 except ImportError:
20 redis = None
21
22 try:
23 import pylibmc as memcache
24 except ImportError:
25 try:
26 from google.appengine.api import memcache
27 except ImportError:
28 try:
29 import memcache
30 except ImportError:
31 memcache = None
32
33
34 class CacheTests(object):
35 _can_use_fast_sleep = True
36
37 @pytest.fixture
38 def fast_sleep(self, monkeypatch):
39 if self._can_use_fast_sleep:
40 def sleep(delta):
41 orig_time = cache.time
42 monkeypatch.setattr(cache, 'time', lambda: orig_time() + delta)
43
44 return sleep
45 else:
46 import time
47 return time.sleep
48
49 @pytest.fixture
50 def make_cache(self):
51 """Return a cache class or factory."""
52 raise NotImplementedError()
53
54 @pytest.fixture
55 def c(self, make_cache):
56 """Return a cache instance."""
57 return make_cache()
58
59 def test_generic_get_dict(self, c):
60 assert c.set('a', 'a')
61 assert c.set('b', 'b')
62 d = c.get_dict('a', 'b')
63 assert 'a' in d
64 assert 'a' == d['a']
65 assert 'b' in d
66 assert 'b' == d['b']
67
68 def test_generic_set_get(self, c):
69 for i in range(3):
70 assert c.set(str(i), i * i)
71
72 for i in range(3):
73 result = c.get(str(i))
74 assert result == i * i, result
75
76 def test_generic_get_set(self, c):
77 assert c.set('foo', ['bar'])
78 assert c.get('foo') == ['bar']
79
80 def test_generic_get_many(self, c):
81 assert c.set('foo', ['bar'])
82 assert c.set('spam', 'eggs')
83 assert c.get_many('foo', 'spam') == [['bar'], 'eggs']
84
85 def test_generic_set_many(self, c):
86 assert c.set_many({'foo': 'bar', 'spam': ['eggs']})
87 assert c.get('foo') == 'bar'
88 assert c.get('spam') == ['eggs']
89
90 def test_generic_add(self, c):
91 # sanity check that add() works like set()
92 assert c.add('foo', 'bar')
93 assert c.get('foo') == 'bar'
94 assert not c.add('foo', 'qux')
95 assert c.get('foo') == 'bar'
96
97 def test_generic_delete(self, c):
98 assert c.add('foo', 'bar')
99 assert c.get('foo') == 'bar'
100 assert c.delete('foo')
101 assert c.get('foo') is None
102
103 def test_generic_delete_many(self, c):
104 assert c.add('foo', 'bar')
105 assert c.add('spam', 'eggs')
106 assert c.delete_many('foo', 'spam')
107 assert c.get('foo') is None
108 assert c.get('spam') is None
109
110 def test_generic_inc_dec(self, c):
111 assert c.set('foo', 1)
112 assert c.inc('foo') == c.get('foo') == 2
113 assert c.dec('foo') == c.get('foo') == 1
114 assert c.delete('foo')
115
116 def test_generic_true_false(self, c):
117 assert c.set('foo', True)
118 assert c.get('foo') in (True, 1)
119 assert c.set('bar', False)
120 assert c.get('bar') in (False, 0)
121
122 def test_generic_timeout(self, c, fast_sleep):
123 c.set('foo', 'bar', 0)
124 assert c.get('foo') == 'bar'
125 c.set('baz', 'qux', 1)
126 assert c.get('baz') == 'qux'
127 fast_sleep(3)
128 # timeout of zero means no timeout
129 assert c.get('foo') == 'bar'
130 assert c.get('baz') is None
131
132 def test_generic_has(self, c):
133 assert c.has('foo') in (False, 0)
134 assert c.has('spam') in (False, 0)
135 assert c.set('foo', 'bar')
136 assert c.has('foo') in (True, 1)
137 assert c.has('spam') in (False, 0)
138 c.delete('foo')
139 assert c.has('foo') in (False, 0)
140 assert c.has('spam') in (False, 0)
141
142
143 class TestSimpleCache(CacheTests):
144 @pytest.fixture
145 def make_cache(self):
146 return cache.SimpleCache
147
148 def test_purge(self):
149 c = cache.SimpleCache(threshold=2)
150 c.set('a', 'a')
151 c.set('b', 'b')
152 c.set('c', 'c')
153 c.set('d', 'd')
154 # Cache purges old items *before* it sets new ones.
155 assert len(c._cache) == 3
156
157
158 class TestFileSystemCache(CacheTests):
159 @pytest.fixture
160 def make_cache(self, tmpdir):
161 return lambda **kw: cache.FileSystemCache(cache_dir=str(tmpdir), **kw)
162
163 def test_filesystemcache_prune(self, make_cache):
164 THRESHOLD = 13
165 c = make_cache(threshold=THRESHOLD)
166
167 for i in range(2 * THRESHOLD):
168 assert c.set(str(i), i)
169
170 cache_files = os.listdir(c._path)
171 assert len(cache_files) <= THRESHOLD
172
173 def test_filesystemcache_clear(self, c):
174 assert c.set('foo', 'bar')
175 cache_files = os.listdir(c._path)
176 assert len(cache_files) == 1
177 assert c.clear()
178 cache_files = os.listdir(c._path)
179 assert len(cache_files) == 0
180
181
182 # don't use pytest.mark.skipif on subclasses
183 # https://bitbucket.org/hpk42/pytest/issue/568
184 # skip happens in requirements fixture instead
185 class TestRedisCache(CacheTests):
186 _can_use_fast_sleep = False
187
188 @pytest.fixture(scope='class', autouse=True)
189 def requirements(self, subprocess):
190 if redis is None:
191 pytest.skip('Python package "redis" is not installed.')
192
193 def prepare(cwd):
194 return '[Rr]eady to accept connections', ['redis-server']
195
196 try:
197 subprocess.ensure('redis_server', prepare)
198 except IOError as e:
199 # xprocess raises FileNotFoundError
200 if e.errno == errno.ENOENT:
201 pytest.skip('Redis is not installed.')
202 else:
203 raise
204
205 yield
206 subprocess.getinfo('redis_server').terminate()
207
208 @pytest.fixture(params=(None, False, True))
209 def make_cache(self, request):
210 if request.param is None:
211 host = 'localhost'
212 elif request.param:
213 host = redis.StrictRedis()
214 else:
215 host = redis.Redis()
216
217 c = cache.RedisCache(
218 host=host,
219 key_prefix='werkzeug-test-case:',
220 )
221 yield lambda: c
222 c.clear()
223
224 def test_compat(self, c):
225 assert c._client.set(c.key_prefix + 'foo', 'Awesome')
226 assert c.get('foo') == b'Awesome'
227 assert c._client.set(c.key_prefix + 'foo', '42')
228 assert c.get('foo') == 42
229
230
231 class TestMemcachedCache(CacheTests):
232 _can_use_fast_sleep = False
233
234 @pytest.fixture(scope='class', autouse=True)
235 def requirements(self, subprocess):
236 if memcache is None:
237 pytest.skip(
238 'Python package for memcache is not installed. Need one of '
239 '"pylibmc", "google.appengine", or "memcache".'
240 )
241
242 def prepare(cwd):
243 return '', ['memcached']
244
245 try:
246 subprocess.ensure('memcached', prepare)
247 except IOError as e:
248 # xprocess raises FileNotFoundError
249 if e.errno == errno.ENOENT:
250 pytest.skip('Memcached is not installed.')
251 else:
252 raise
253
254 yield
255 subprocess.getinfo('memcached').terminate()
256
257 @pytest.fixture
258 def make_cache(self):
259 c = cache.MemcachedCache(key_prefix='werkzeug-test-case:')
260 yield lambda: c
261 c.clear()
262
263 def test_compat(self, c):
264 assert c._client.set(c.key_prefix + 'foo', 'bar')
265 assert c.get('foo') == 'bar'
266
267 def test_huge_timeouts(self, c):
268 # Timeouts greater than epoch are interpreted as POSIX timestamps
269 # (i.e. not relative to now, but relative to epoch)
270 epoch = 2592000
271 c.set('foo', 'bar', epoch + 100)
272 assert c.get('foo') == 'bar'
273
274
275 class TestUWSGICache(CacheTests):
276 _can_use_fast_sleep = False
277
278 @pytest.fixture(scope='class', autouse=True)
279 def requirements(self):
280 try:
281 import uwsgi # NOQA
282 except ImportError:
283 pytest.skip(
284 'Python "uwsgi" package is only avaialable when running '
285 'inside uWSGI.'
286 )
287
288 @pytest.fixture
289 def make_cache(self):
290 c = cache.UWSGICache(cache='werkzeugtest')
291 yield lambda: c
292 c.clear()
+0
-269
tests/contrib/test_cache.py less more
0 # -*- coding: utf-8 -*-
1 """
2 tests.cache
3 ~~~~~~~~~~~
4
5 Tests the cache system
6
7 :copyright: (c) 2014 by Armin Ronacher.
8 :license: BSD, see LICENSE for more details.
9 """
10 import pytest
11 import os
12 import random
13
14 from werkzeug.contrib import cache
15
16 try:
17 import redis
18 except ImportError:
19 redis = None
20
21 try:
22 import pylibmc as memcache
23 except ImportError:
24 try:
25 from google.appengine.api import memcache
26 except ImportError:
27 try:
28 import memcache
29 except ImportError:
30 memcache = None
31
32
33 class CacheTests(object):
34 _can_use_fast_sleep = True
35
36 @pytest.fixture
37 def make_cache(self):
38 '''Return a cache class or factory.'''
39 raise NotImplementedError()
40
41 @pytest.fixture
42 def fast_sleep(self, monkeypatch):
43 if self._can_use_fast_sleep:
44 def sleep(delta):
45 orig_time = cache.time
46 monkeypatch.setattr(cache, 'time', lambda: orig_time() + delta)
47
48 return sleep
49 else:
50 import time
51 return time.sleep
52
53 @pytest.fixture
54 def c(self, make_cache):
55 '''Return a cache instance.'''
56 return make_cache()
57
58 def test_generic_get_dict(self, c):
59 assert c.set('a', 'a')
60 assert c.set('b', 'b')
61 d = c.get_dict('a', 'b')
62 assert 'a' in d
63 assert 'a' == d['a']
64 assert 'b' in d
65 assert 'b' == d['b']
66
67 def test_generic_set_get(self, c):
68 for i in range(3):
69 assert c.set(str(i), i * i)
70 for i in range(3):
71 result = c.get(str(i))
72 assert result == i * i, result
73
74 def test_generic_get_set(self, c):
75 assert c.set('foo', ['bar'])
76 assert c.get('foo') == ['bar']
77
78 def test_generic_get_many(self, c):
79 assert c.set('foo', ['bar'])
80 assert c.set('spam', 'eggs')
81 assert list(c.get_many('foo', 'spam')) == [['bar'], 'eggs']
82
83 def test_generic_set_many(self, c):
84 assert c.set_many({'foo': 'bar', 'spam': ['eggs']})
85 assert c.get('foo') == 'bar'
86 assert c.get('spam') == ['eggs']
87
88 def test_generic_expire(self, c, fast_sleep):
89 assert c.set('foo', 'bar', 1)
90 fast_sleep(5)
91 assert c.get('foo') is None
92
93 def test_generic_add(self, c):
94 # sanity check that add() works like set()
95 assert c.add('foo', 'bar')
96 assert c.get('foo') == 'bar'
97 assert not c.add('foo', 'qux')
98 assert c.get('foo') == 'bar'
99
100 def test_generic_delete(self, c):
101 assert c.add('foo', 'bar')
102 assert c.get('foo') == 'bar'
103 assert c.delete('foo')
104 assert c.get('foo') is None
105
106 def test_generic_delete_many(self, c):
107 assert c.add('foo', 'bar')
108 assert c.add('spam', 'eggs')
109 assert c.delete_many('foo', 'spam')
110 assert c.get('foo') is None
111 assert c.get('spam') is None
112
113 def test_generic_inc_dec(self, c):
114 assert c.set('foo', 1)
115 assert c.inc('foo') == c.get('foo') == 2
116 assert c.dec('foo') == c.get('foo') == 1
117 assert c.delete('foo')
118
119 def test_generic_true_false(self, c):
120 assert c.set('foo', True)
121 assert c.get('foo') in (True, 1)
122 assert c.set('bar', False)
123 assert c.get('bar') in (False, 0)
124
125 def test_generic_no_timeout(self, c, fast_sleep):
126 # Timeouts of zero should cause the cache to never expire
127 c.set('foo', 'bar', 0)
128 fast_sleep(random.randint(1, 5))
129 assert c.get('foo') == 'bar'
130
131 def test_generic_timeout(self, c, fast_sleep):
132 # Check that cache expires when the timeout is reached
133 timeout = random.randint(1, 5)
134 c.set('foo', 'bar', timeout)
135 assert c.get('foo') == 'bar'
136 # sleep a bit longer than timeout to ensure there are no
137 # race conditions
138 fast_sleep(timeout + 5)
139 assert c.get('foo') is None
140
141 def test_generic_has(self, c):
142 assert c.has('foo') in (False, 0)
143 assert c.has('spam') in (False, 0)
144 assert c.set('foo', 'bar')
145 assert c.has('foo') in (True, 1)
146 assert c.has('spam') in (False, 0)
147 c.delete('foo')
148 assert c.has('foo') in (False, 0)
149 assert c.has('spam') in (False, 0)
150
151
152 class TestSimpleCache(CacheTests):
153
154 @pytest.fixture
155 def make_cache(self):
156 return cache.SimpleCache
157
158 def test_purge(self):
159 c = cache.SimpleCache(threshold=2)
160 c.set('a', 'a')
161 c.set('b', 'b')
162 c.set('c', 'c')
163 c.set('d', 'd')
164 # Cache purges old items *before* it sets new ones.
165 assert len(c._cache) == 3
166
167
168 class TestFileSystemCache(CacheTests):
169
170 @pytest.fixture
171 def make_cache(self, tmpdir):
172 return lambda **kw: cache.FileSystemCache(cache_dir=str(tmpdir), **kw)
173
174 def test_filesystemcache_prune(self, make_cache):
175 THRESHOLD = 13
176 c = make_cache(threshold=THRESHOLD)
177 for i in range(2 * THRESHOLD):
178 assert c.set(str(i), i)
179 cache_files = os.listdir(c._path)
180 assert len(cache_files) <= THRESHOLD
181
182 def test_filesystemcache_clear(self, c):
183 assert c.set('foo', 'bar')
184 cache_files = os.listdir(c._path)
185 assert len(cache_files) == 1
186 assert c.clear()
187 cache_files = os.listdir(c._path)
188 assert len(cache_files) == 0
189
190
191 # Don't use pytest marker
192 # https://bitbucket.org/hpk42/pytest/issue/568
193 if redis is not None:
194 class TestRedisCache(CacheTests):
195 _can_use_fast_sleep = False
196
197 @pytest.fixture(params=[
198 ([], dict()),
199 ([redis.Redis()], dict()),
200 ([redis.StrictRedis()], dict())
201 ])
202 def make_cache(self, xprocess, request):
203 def preparefunc(cwd):
204 return 'server is now ready', ['redis-server']
205
206 xprocess.ensure('redis_server', preparefunc)
207 args, kwargs = request.param
208 c = cache.RedisCache(*args, key_prefix='werkzeug-test-case:',
209 **kwargs)
210 request.addfinalizer(c.clear)
211 return lambda: c
212
213 def test_compat(self, c):
214 assert c._client.set(c.key_prefix + 'foo', 'Awesome')
215 assert c.get('foo') == b'Awesome'
216 assert c._client.set(c.key_prefix + 'foo', '42')
217 assert c.get('foo') == 42
218
219
220 # Don't use pytest marker
221 # https://bitbucket.org/hpk42/pytest/issue/568
222 if memcache is not None:
223 class TestMemcachedCache(CacheTests):
224 _can_use_fast_sleep = False
225
226 @pytest.fixture
227 def make_cache(self, xprocess, request):
228 def preparefunc(cwd):
229 return '', ['memcached']
230
231 xprocess.ensure('memcached', preparefunc)
232 c = cache.MemcachedCache(key_prefix='werkzeug-test-case:')
233 request.addfinalizer(c.clear)
234 return lambda: c
235
236 def test_compat(self, c):
237 assert c._client.set(c.key_prefix + 'foo', 'bar')
238 assert c.get('foo') == 'bar'
239
240 def test_huge_timeouts(self, c):
241 # Timeouts greater than epoch are interpreted as POSIX timestamps
242 # (i.e. not relative to now, but relative to epoch)
243 import random
244 epoch = 2592000
245 timeout = epoch + random.random() * 100
246 c.set('foo', 'bar', timeout)
247 assert c.get('foo') == 'bar'
248
249
250 def _running_in_uwsgi():
251 try:
252 import uwsgi # NOQA
253 except ImportError:
254 return False
255 else:
256 return True
257
258
259 @pytest.mark.skipif(not _running_in_uwsgi(),
260 reason="uWSGI module can't be imported outside of uWSGI")
261 class TestUWSGICache(CacheTests):
262 _can_use_fast_sleep = False
263
264 @pytest.fixture
265 def make_cache(self, xprocess, request):
266 c = cache.UWSGICache(cache='werkzeugtest')
267 request.addfinalizer(c.clear)
268 return lambda: c
3535 assert not c3.new
3636 assert c3 == {}
3737
38 c4 = SecureCookie({'x': 42}, 'foo')
39 c4_serialized = c4.serialize()
40 assert SecureCookie.unserialize(c4_serialized, 'foo') == c4
41
3842
3943 def test_wrapper_support():
4044 req = Request.from_values()
(New empty file)
0 94
1 ----------------------------898239224156930639461866
2 Content-Disposition: form-data; name="file"; filename="test.txt"
3 Content-Type: text/plain
4
5
6 f
7 This is a test
8
9 2
10
11
12 65
13 ----------------------------898239224156930639461866
14 Content-Disposition: form-data; name="type"
15
16
17 a
18 text/plain
19 3a
20
21 ----------------------------898239224156930639461866--
22
23 0
24
542542 assert sorted(iterlists(ab)) == [('key_a', ['value_a']), ('key_b', ['value_b'])]
543543 assert sorted(iterlistvalues(ab)) == [['value_a'], ['value_b']]
544544 assert sorted(iterkeys(ab)) == ["key_a", "key_b"]
545
546
547 class TestTypeConversionDict(object):
548 storage_class = datastructures.TypeConversionDict
549
550 def test_value_conversion(self):
551 d = self.storage_class(foo='1')
552 assert d.get('foo', type=int) == 1
553
554 def test_return_default_when_conversion_is_not_possible(self):
555 d = self.storage_class(foo='bar')
556 assert d.get('foo', default=-1, type=int) == -1
557
558 def test_propagate_exceptions_in_conversion(self):
559 d = self.storage_class(foo='bar')
560 switch = {'a': 1}
561 with pytest.raises(KeyError):
562 d.get('foo', type=lambda x: switch[x])
545563
546564
547565 class TestCombinedMultiDict(object):
806824 storage_class = datastructures.ImmutableList
807825
808826 def test_list_hashable(self):
809 t = (1, 2, 3, 4)
810 l = self.storage_class(t)
811 assert hash(t) == hash(l)
812 assert t != l
827 data = (1, 2, 3, 4)
828 store = self.storage_class(data)
829 assert hash(data) == hash(store)
830 assert data != store
813831
814832
815833 def make_call_asserter(func=None):
127127 assert a.realm == 'testrealm@host.invalid'
128128 assert a.nonce == 'dcd98b7102dd2f0e8b11d0f600bfb0c093'
129129 assert a.uri == '/dir/index.html'
130 assert 'auth' in a.qop
130 assert a.qop == 'auth'
131131 assert a.nc == '00000001'
132132 assert a.cnonce == '0a4f113b'
133133 assert a.response == '6629fae49393a05397450978507c4ef1'
279279 ('form-data', {'name': u'\u016an\u012dc\u014dde\u033d',
280280 'filename': 'some_file.txt'})
281281
282 def test_parse_options_header_value_with_quotes(self):
283 assert http.parse_options_header(
284 'form-data; name="file"; filename="t\'es\'t.txt"'
285 ) == ('form-data', {'name': 'file', 'filename': "t'es't.txt"})
286 assert http.parse_options_header(
287 'form-data; name="file"; filename*=UTF-8\'\'"\'🐍\'.txt"'
288 ) == ('form-data', {'name': 'file', 'filename': u"'🐍'.txt"})
289
282290 def test_parse_options_header_broken_values(self):
283291 # Issue #995
284292 assert http.parse_options_header(' ') == ('', {})
416424 val = http.dump_cookie('foo', 'bar', domain=u'.foo.com')
417425 strict_eq(val, 'foo=bar; Domain=.foo.com; Path=/')
418426
427 def test_cookie_maxsize(self, recwarn):
428 val = http.dump_cookie('foo', 'bar' * 1360 + 'b')
429 assert len(recwarn) == 0
430 assert len(val) == 4093
431
432 http.dump_cookie('foo', 'bar' * 1360 + 'ba')
433 assert len(recwarn) == 1
434 w = recwarn.pop()
435 assert 'cookie is too large' in str(w.message)
436
437 http.dump_cookie('foo', b'w' * 502, max_size=512)
438 assert len(recwarn) == 1
439 w = recwarn.pop()
440 assert 'the limit is 512 bytes' in str(w.message)
441
419442
420443 class TestRange(object):
421444
1818
1919
2020 def test_basic_local():
21 l = local.Local()
22 l.foo = 0
21 ns = local.Local()
22 ns.foo = 0
2323 values = []
2424
2525 def value_setter(idx):
2626 time.sleep(0.01 * idx)
27 l.foo = idx
27 ns.foo = idx
2828 time.sleep(0.02)
29 values.append(l.foo)
29 values.append(ns.foo)
3030 threads = [Thread(target=value_setter, args=(x,))
3131 for x in [1, 2, 3]]
3232 for thread in threads:
3535 assert sorted(values) == [1, 2, 3]
3636
3737 def delfoo():
38 del l.foo
38 del ns.foo
3939 delfoo()
40 pytest.raises(AttributeError, lambda: l.foo)
40 pytest.raises(AttributeError, lambda: ns.foo)
4141 pytest.raises(AttributeError, delfoo)
4242
43 local.release_local(l)
43 local.release_local(ns)
4444
4545
4646 def test_local_release():
47 l = local.Local()
48 l.foo = 42
49 local.release_local(l)
50 assert not hasattr(l, 'foo')
47 ns = local.Local()
48 ns.foo = 42
49 local.release_local(ns)
50 assert not hasattr(ns, 'foo')
5151
5252 ls = local.LocalStack()
5353 ls.push(42)
138138
139139 def test_custom_idents():
140140 ident = 0
141 l = local.Local()
141 ns = local.Local()
142142 stack = local.LocalStack()
143 local.LocalManager([l, stack], ident_func=lambda: ident)
144
145 l.foo = 42
143 local.LocalManager([ns, stack], ident_func=lambda: ident)
144
145 ns.foo = 42
146146 stack.push({'foo': 42})
147147 ident = 1
148 l.foo = 23
148 ns.foo = 23
149149 stack.push({'foo': 23})
150150 ident = 0
151 assert l.foo == 42
151 assert ns.foo == 42
152152 assert stack.top['foo'] == 42
153153 stack.pop()
154154 assert stack.top is None
155155 ident = 1
156 assert l.foo == 23
156 assert ns.foo == 23
157157 assert stack.top['foo'] == 23
158158 stack.pop()
159159 assert stack.top is None
198198 partial_proxy = local.LocalProxy(partial_lookup_func)
199199 assert partial_proxy.__wrapped__ == partial_lookup_func
200200
201 l = local.Local()
202 l.foo = SomeClassWithWrapped()
203 l.bar = 42
204
205 assert l('foo').__wrapped__ == 'wrapped'
206 pytest.raises(AttributeError, lambda: l('bar').__wrapped__)
201 ns = local.Local()
202 ns.foo = SomeClassWithWrapped()
203 ns.bar = 42
204
205 assert ns('foo').__wrapped__ == 'wrapped'
206 pytest.raises(AttributeError, lambda: ns('bar').__wrapped__)
99 """
1010 import os
1111 import ssl
12 import sys
1213 import subprocess
1314 import textwrap
1415
277278 'content_type': 'application/json'
278279 })
279280 assert r.content == b'YES'
281
282
283 def test_port_must_be_integer(dev_server):
284 def app(environ, start_response):
285 start_response('200 OK', [('Content-Type', 'text/html')])
286 return [b'hello']
287
288 with pytest.raises(TypeError) as excinfo:
289 serving.run_simple(hostname='localhost', port='5001',
290 application=app, use_reloader=True)
291 assert 'port must be an integer' in str(excinfo.value)
292
293 with pytest.raises(TypeError) as excinfo:
294 serving.run_simple(hostname='localhost', port='5001',
295 application=app, use_reloader=False)
296 assert 'port must be an integer' in str(excinfo.value)
297
298
299 def test_chunked_encoding(dev_server):
300 server = dev_server(r'''
301 from werkzeug.wrappers import Request
302 def app(environ, start_response):
303 assert environ['HTTP_TRANSFER_ENCODING'] == 'chunked'
304 assert environ.get('wsgi.input_terminated', False)
305 request = Request(environ)
306 assert request.mimetype == 'multipart/form-data'
307 assert request.files['file'].read() == b'This is a test\n'
308 assert request.form['type'] == 'text/plain'
309 start_response('200 OK', [('Content-Type', 'text/plain')])
310 return [b'YES']
311 ''')
312
313 testfile = os.path.join(os.path.dirname(__file__), 'res', 'chunked.txt')
314
315 if sys.version_info[0] == 2:
316 from httplib import HTTPConnection
317 else:
318 from http.client import HTTPConnection
319
320 conn = HTTPConnection('127.0.0.1', server.port)
321 conn.connect()
322 conn.putrequest('POST', '/', skip_host=1, skip_accept_encoding=1)
323 conn.putheader('Accept', 'text/plain')
324 conn.putheader('Transfer-Encoding', 'chunked')
325 conn.putheader(
326 'Content-Type',
327 'multipart/form-data; boundary='
328 '--------------------------898239224156930639461866')
329 conn.endheaders()
330
331 with open(testfile, 'rb') as f:
332 conn.send(f.read())
333
334 res = conn.getresponse()
335 assert res.status == 200
336 assert res.read() == b'YES'
337
338 conn.close()
339
340
341 def test_chunked_encoding_with_content_length(dev_server):
342 server = dev_server(r'''
343 from werkzeug.wrappers import Request
344 def app(environ, start_response):
345 assert environ['HTTP_TRANSFER_ENCODING'] == 'chunked'
346 assert environ.get('wsgi.input_terminated', False)
347 request = Request(environ)
348 assert request.mimetype == 'multipart/form-data'
349 assert request.files['file'].read() == b'This is a test\n'
350 assert request.form['type'] == 'text/plain'
351 start_response('200 OK', [('Content-Type', 'text/plain')])
352 return [b'YES']
353 ''')
354
355 testfile = os.path.join(os.path.dirname(__file__), 'res', 'chunked.txt')
356
357 if sys.version_info[0] == 2:
358 from httplib import HTTPConnection
359 else:
360 from http.client import HTTPConnection
361
362 conn = HTTPConnection('127.0.0.1', server.port)
363 conn.connect()
364 conn.putrequest('POST', '/', skip_host=1, skip_accept_encoding=1)
365 conn.putheader('Accept', 'text/plain')
366 conn.putheader('Transfer-Encoding', 'chunked')
367 # Content-Length is invalid for chunked, but some libraries might send it
368 conn.putheader('Content-Length', '372')
369 conn.putheader(
370 'Content-Type',
371 'multipart/form-data; boundary='
372 '--------------------------898239224156930639461866')
373 conn.endheaders()
374
375 with open(testfile, 'rb') as f:
376 conn.send(f.read())
377
378 res = conn.getresponse()
379 assert res.status == 200
380 assert res.read() == b'YES'
381
382 conn.close()
168168 assert b.form.getlist('foo') == ['bar1', 'bar2']
169169
170170 def check_list_content(b, length):
171 l = b.files.getlist('foo')
172 assert len(l) == length
173 for obj in l:
171 foo = b.files.getlist('foo')
172 assert len(foo) == length
173 for obj in foo:
174174 assert isinstance(obj, FileStorage)
175175
176176 b = EnvironBuilder(data={'foo': BytesIO()})
294294 assert utils.secure_filename('../../../etc/passwd') == 'etc_passwd'
295295 assert utils.secure_filename(u'i contain cool \xfcml\xe4uts.txt') == \
296296 'i_contain_cool_umlauts.txt'
297 assert utils.secure_filename('__filename__') == 'filename'
298 assert utils.secure_filename('foo$&^*)bar') == 'foobar'
77 :copyright: (c) 2014 by Armin Ronacher.
88 :license: BSD, see LICENSE for more details.
99 """
10 import contextlib
1011 import os
1112
1213 import pytest
1314
1415 import pickle
1516 from io import BytesIO
16 from datetime import datetime
17 from datetime import datetime, timedelta
1718 from werkzeug._compat import iteritems
1819
1920 from tests import strict_eq
291292 response.status = 'wtf'
292293 strict_eq(response.status_code, 0)
293294 strict_eq(response.status, '0 wtf')
295
296 # invalid status codes
297 with pytest.raises(ValueError) as empty_string_error:
298 wrappers.BaseResponse(None, '')
299 assert 'Empty status argument' in str(empty_string_error)
300
301 with pytest.raises(TypeError) as invalid_type_error:
302 wrappers.BaseResponse(None, tuple())
303 assert 'Invalid status argument' in str(invalid_type_error)
294304
295305
296306 def test_type_forcing():
686696 response.content_length = '42'
687697 assert response.content_length == 42
688698
689 for attr in 'date', 'age', 'expires':
699 for attr in 'date', 'expires':
690700 assert getattr(response, attr) is None
691701 setattr(response, attr, now)
692702 assert getattr(response, attr) == now
703
704 assert response.age is None
705 age_td = timedelta(days=1, minutes=3, seconds=5)
706 response.age = age_td
707 assert response.age == age_td
708 response.age = 42
709 assert response.age == timedelta(seconds=42)
693710
694711 assert response.retry_after is None
695712 response.retry_after = now
858875 assert resp.headers['content-length'] == '6'
859876
860877
878 def test_response_content_length_uses_encode():
879 r = wrappers.Response(u'你好')
880 assert r.calculate_content_length() == 6
881
882
861883 def test_other_method_payload():
862884 data = b'Hello World'
863885 req = wrappers.Request.from_values(input_stream=BytesIO(data),
10871109 assert req.method == 'GET'
10881110
10891111
1112 def test_is_xhr_warning():
1113 req = wrappers.Request.from_values()
1114
1115 with pytest.warns(DeprecationWarning) as record:
1116 req.is_xhr
1117
1118 assert len(record) == 1
1119 assert 'Request.is_xhr is deprecated' in str(record[0].message)
1120
1121
1122 def test_write_length():
1123 response = wrappers.Response()
1124 length = response.stream.write(b'bar')
1125 assert length == 3
1126
1127
1128 def test_stream_zip():
1129 import zipfile
1130
1131 response = wrappers.Response()
1132 with contextlib.closing(zipfile.ZipFile(response.stream, mode='w')) as z:
1133 z.writestr("foo", b"bar")
1134
1135 buffer = BytesIO(response.get_data())
1136 with contextlib.closing(zipfile.ZipFile(buffer, mode='r')) as z:
1137 assert z.namelist() == ['foo']
1138 assert z.read('foo') == b'bar'
1139
1140
10901141 class TestSetCookie(object):
10911142 """Tests for :meth:`werkzeug.wrappers.BaseResponse.set_cookie`."""
10921143
77 :copyright: (c) 2014 by Armin Ronacher.
88 :license: BSD, see LICENSE for more details.
99 """
10 import io
11 import json
1012 import os
13 from contextlib import closing
14 from os import path
1115
1216 import pytest
1317
14 from os import path
15 from contextlib import closing
16
1718 from tests import strict_eq
18
19 from werkzeug.wrappers import BaseResponse
19 from werkzeug import wsgi
20 from werkzeug._compat import BytesIO, NativeStringIO, StringIO, to_bytes, \
21 to_native
2022 from werkzeug.exceptions import BadRequest, ClientDisconnected
2123 from werkzeug.test import Client, create_environ, run_wsgi_app
22 from werkzeug import wsgi
23 from werkzeug._compat import StringIO, BytesIO, NativeStringIO, to_native, \
24 to_bytes
24 from werkzeug.wrappers import BaseResponse
2525 from werkzeug.wsgi import _RangeWrapper, wrap_file
2626
2727
3939 with open(path.join(test_dir, to_native(u'äöü', 'utf-8')), 'w') as test_file:
4040 test_file.write(u'FOUND')
4141
42 app = wsgi.SharedDataMiddleware(null_application, {
43 '/': path.join(path.dirname(__file__), 'res'),
44 '/sources': path.join(path.dirname(__file__), 'res'),
45 '/pkg': ('werkzeug.debug', 'shared'),
46 '/foo': test_dir
47 })
48
49 for p in '/test.txt', '/sources/test.txt', '/foo/äöü':
50 app_iter, status, headers = run_wsgi_app(app, create_environ(p))
51 assert status == '200 OK'
42 for t in [list, dict]:
43 app = wsgi.SharedDataMiddleware(null_application, t([
44 ('/', path.join(path.dirname(__file__), 'res')),
45 ('/sources', path.join(path.dirname(__file__), 'res')),
46 ('/pkg', ('werkzeug.debug', 'shared')),
47 ('/foo', test_dir)
48 ]))
49
50 for p in '/test.txt', '/sources/test.txt', '/foo/äöü':
51 app_iter, status, headers = run_wsgi_app(app, create_environ(p))
52 assert status == '200 OK'
53 with closing(app_iter) as app_iter:
54 data = b''.join(app_iter).strip()
55 assert data == b'FOUND'
56
57 app_iter, status, headers = run_wsgi_app(
58 app, create_environ('/pkg/debugger.js'))
5259 with closing(app_iter) as app_iter:
53 data = b''.join(app_iter).strip()
54 assert data == b'FOUND'
55
56 app_iter, status, headers = run_wsgi_app(
57 app, create_environ('/pkg/debugger.js'))
58 with closing(app_iter) as app_iter:
59 contents = b''.join(app_iter)
60 assert b'$(function() {' in contents
61
62 app_iter, status, headers = run_wsgi_app(
63 app, create_environ('/missing'))
64 assert status == '404 NOT FOUND'
65 assert b''.join(app_iter).strip() == b'NOT FOUND'
60 contents = b''.join(app_iter)
61 assert b'$(function() {' in contents
62
63 app_iter, status, headers = run_wsgi_app(
64 app, create_environ('/missing'))
65 assert status == '404 NOT FOUND'
66 assert b''.join(app_iter).strip() == b'NOT FOUND'
6667
6768
6869 def test_dispatchermiddleware():
241242 io = StringIO(u'123\n456\n')
242243 stream = wsgi.LimitedStream(io, 8)
243244 strict_eq(list(stream), [u'123\n', u'456\n'])
245
246
247 def test_limited_stream_json_load():
248 stream = wsgi.LimitedStream(BytesIO(b'{"hello": "test"}'), 17)
249 # flask.json adapts bytes to text with TextIOWrapper
250 # this expects stream.readable() to exist and return true
251 stream = io.TextIOWrapper(io.BufferedReader(stream), 'UTF-8')
252 data = json.load(stream)
253 assert data == {'hello': 'test'}
244254
245255
246256 def test_limited_stream_disconnection():
00 [tox]
1 envlist = py{26,27,py,33,34,35,36}-{normal,uwsgi,stylecheck}
1 envlist =
2 py{36,27}-hypothesis-uwsgi
3 py{35,34,py}
4 # run py33, py26 manually
5 stylecheck
6 docs-html
7 coverage-report
28
39 [testenv]
410 passenv = LANG
5 deps=
6 normal: pyopenssl
7 normal: greenlet
8 normal: pytest
9 normal: pytest-xprocess
10 normal: redis
11 normal: python-memcached
12 normal: requests
13 normal: watchdog
14 py{27,36}-normal: hypothesis
11 setenv =
12 TOX_ENVTMPDIR={envtmpdir}
13
14 usedevelop = true
15 deps =
16 # remove once we drop support for 2.6, 3.3
17 py26,py33: py<1.5
18 py26,py33: pytest<3.3
19
20 pytest-xprocess
21 coverage
22 requests
23 pyopenssl
24 greenlet
25 redis
26 python-memcached
27 watchdog
28
29 hypothesis: hypothesis
30
1531 uwsgi: uwsgi
16 stylecheck: flake8
1732
18 whitelist_externals=
33 whitelist_externals =
1934 redis-server
2035 memcached
2136 uwsgi
2237
23 commands=
24 normal: py.test []
25 py{27,36}-normal: py.test [] tests/hypothesis/
26 stylecheck: flake8 []
27 uwsgi: uwsgi --pyrun {envbindir}/py.test --pyargv -kUWSGI --cache2=name=werkzeugtest,items=20 --master
38 commands =
39 coverage run -p -m pytest []
40 hypothesis: coverage run -p -m pytest [] tests/hypothesis
41
42 uwsgi: uwsgi --pyrun {envbindir}/coverage --pyargv 'run -p -m pytest -kUWSGI' --cache2=name=werkzeugtest,items=20 --master
43 # --pyrun doesn't pass pytest exit code up, so check for a marker
44 uwsgi: python -c 'import os, sys; sys.exit(os.path.exists("{envtmpdir}/test_uwsgi_failed"))'
45
46 [testenv:stylecheck]
47 deps = flake8
48 commands = flake8 []
49
50 [testenv:docs-html]
51 deps = sphinx
52 commands = sphinx-build -W -b html -d {envtmpdir}/doctrees docs docs/_build/html
53
54 [testenv:docs-linkcheck]
55 deps = sphinx
56 commands = sphinx-build -W -b linkcheck -d {envtmpdir}/doctrees docs docs/_build/linkcheck
57
58 [testenv:coverage-report]
59 deps = coverage
60 skip_install = true
61 commands =
62 coverage combine
63 coverage report
64 coverage html
65
66 [testenv:codecov]
67 passenv = CI TRAVIS TRAVIS_*
68 deps = codecov
69 skip_install = true
70 commands =
71 coverage combine
72 coverage report
73 codecov
1818
1919 from werkzeug._compat import iteritems
2020
21 __version__ = '0.12.2-dev'
21 __version__ = '0.13'
2222
2323
2424 # This import magic raises concerns quite often which is why the implementation
9797 }
9898
9999 # modules that should be imported when accessed as attributes of werkzeug
100 attribute_modules = frozenset(['exceptions', 'routing', 'script'])
100 attribute_modules = frozenset(['exceptions', 'routing'])
101101
102102
103103 object_origins = {}
123123 def __dir__(self):
124124 """Just show what we want to show."""
125125 result = list(new_module.__all__)
126 result.extend(('__file__', '__path__', '__doc__', '__all__',
126 result.extend(('__file__', '__doc__', '__all__',
127127 '__docformat__', '__name__', '__path__',
128128 '__package__', '__version__'))
129129 return result
125125
126126 def get_many(self, *keys):
127127 """Returns a list of values for the given keys.
128 For each key a item in the list is created::
128 For each key an item in the list is created::
129129
130130 foo, bar = cache.get_many("foo", "bar")
131131
134134 :param keys: The function accepts multiple keys as positional
135135 arguments.
136136 """
137 return map(self.get, keys)
137 return [self.get(k) for k in keys]
138138
139139 def get_dict(self, *keys):
140140 """Like :meth:`get_many` but return a dict::
354354 - ``pylibmc``
355355 - ``google.appengine.api.memcached``
356356 - ``memcached``
357 - ``libmc``
357358
358359 Implementation notes: This cache backend works around some limitations in
359360 memcached to simplify the interface. For example unicode keys are encoded
506507 else:
507508 return memcache.Client(servers)
508509
510 try:
511 import libmc
512 except ImportError:
513 pass
514 else:
515 return libmc.Client(servers)
516
509517
510518 # backwards compatibility
511519 GAEMemcachedCache = MemcachedCache
171171 elif 100 <= status_code < 200 or status_code == 204:
172172 if content_length != 0:
173173 warn(HTTPWarning('%r responses must have an empty '
174 'content length') % status_code)
174 'content length' % status_code))
175175 if bytes_sent:
176176 warn(HTTPWarning('%r responses must not have a body' %
177177 status_code))
9393 from time import time
9494 from hashlib import sha1 as _default_hash
9595
96 from werkzeug._compat import iteritems, text_type
96 from werkzeug._compat import iteritems, text_type, to_bytes
9797 from werkzeug.urls import url_quote_plus, url_unquote_plus
9898 from werkzeug._internal import _date_to_unix
9999 from werkzeug.contrib.sessions import ModificationTrackingDict
151151 # explicitly convert it into a bytestring because python 2.6
152152 # no longer performs an implicit string conversion on hmac
153153 if secret_key is not None:
154 secret_key = bytes(secret_key)
154 secret_key = to_bytes(secret_key, 'utf-8')
155155 self.secret_key = secret_key
156156 self.new = new
157157
298298 """
299299 try:
300300 rv = self[key]
301 if type is not None:
301 except KeyError:
302 return default
303 if type is not None:
304 try:
302305 rv = type(rv)
303 except (KeyError, ValueError):
304 rv = default
306 except ValueError:
307 rv = default
305308 return rv
306309
307310
24532456 The opaque header from the server returned unchanged by the client.
24542457 It is recommended that this string be base64 or hexadecimal data.
24552458 Digest auth only.''')
2456
2457 @property
2458 def qop(self):
2459 """Indicates what "quality of protection" the client has applied to
2460 the message for HTTP digest auth."""
2461 def on_update(header_set):
2462 if not header_set and 'qop' in self:
2463 del self['qop']
2464 elif header_set:
2465 self['qop'] = header_set.to_header()
2466 return parse_set_header(self.get('qop'), on_update)
2459 qop = property(lambda x: x.get('qop'), doc='''
2460 Indicates what "quality of protection" the client has applied to
2461 the message for HTTP digest auth. Note that this is a single token,
2462 not a quoted list of alternatives as in WWW-Authenticate.''')
24672463
24682464
24692465 class WWWAuthenticate(UpdateDictMixin, dict):
1010 """
1111 import re
1212 import codecs
13 from io import BytesIO
14 from tempfile import TemporaryFile
13 from tempfile import SpooledTemporaryFile
1514 from itertools import chain, repeat, tee
1615 from functools import update_wrapper
1716
3736 def default_stream_factory(total_content_length, filename, content_type,
3837 content_length=None):
3938 """The stream factory that is used per default."""
40 if total_content_length > 1024 * 500:
41 return TemporaryFile('wb+')
42 return BytesIO()
39 return SpooledTemporaryFile(max_size=1024 * 500, mode='wb+')
4340
4441
4542 def parse_form_data(environ, stream_factory=None, charset='utf-8',
1616 :license: BSD, see LICENSE for more details.
1717 """
1818 import re
19 import warnings
1920 from time import time, gmtime
2021 try:
2122 from email.utils import parsedate_tz
6061 '^_`abcdefghijklmnopqrstuvwxyz|~')
6162 _etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)')
6263 _unsafe_header_chars = set('()<>@,;:\"/[]?={} \t')
63 _quoted_string_re = r'"[^"\\]*(?:\\.[^"\\]*)*"'
64 _option_header_piece_re = re.compile(
65 r';\s*(%s|[^\s;,=\*]+)\s*'
66 r'(?:\*?=\s*(?:([^\s]+?)\'([^\s]*?)\')?(%s|[^;,]+)?)?\s*' %
67 (_quoted_string_re, _quoted_string_re)
68 )
64 _option_header_piece_re = re.compile(r'''
65 ;\s*
66 (?P<key>
67 "[^"\\]*(?:\\.[^"\\]*)*" # quoted string
68 |
69 [^\s;,=*]+ # token
70 )
71 \s*
72 (?: # optionally followed by =value
73 (?: # equals sign, possibly with encoding
74 \*\s*=\s* # * indicates extended notation
75 (?P<encoding>[^\s]+?)
76 '(?P<language>[^\s]*?)'
77 |
78 =\s* # basic notation
79 )
80 (?P<value>
81 "[^"\\]*(?:\\.[^"\\]*)*" # quoted string
82 |
83 [^;,]+ # token
84 )?
85 )?
86 \s*
87 ''', flags=re.VERBOSE)
6988 _option_header_start_mime_type = re.compile(r',\s*([^;,\s]+)([;,]\s*.+)?')
7089
7190 _entity_headers = frozenset([
780799 return _dump_date(timestamp, ' ')
781800
782801
802 def parse_age(value=None):
803 """Parses a base-10 integer count of seconds into a timedelta.
804
805 If parsing fails, the return value is `None`.
806
807 :param value: a string consisting of an integer represented in base-10
808 :return: a :class:`datetime.timedelta` object or `None`.
809 """
810 if not value:
811 return None
812 try:
813 seconds = int(value)
814 except ValueError:
815 return None
816 if seconds < 0:
817 return None
818 try:
819 return timedelta(seconds=seconds)
820 except OverflowError:
821 return None
822
823
824 def dump_age(age=None):
825 """Formats the duration as a base-10 integer.
826
827 :param age: should be an integer number of seconds,
828 a :class:`datetime.timedelta` object, or,
829 if the age is unknown, `None` (default).
830 """
831 if age is None:
832 return
833 if isinstance(age, timedelta):
834 # do the equivalent of Python 2.7's timedelta.total_seconds(),
835 # but disregarding fractional seconds
836 age = age.seconds + (age.days * 24 * 3600)
837
838 age = int(age)
839 if age < 0:
840 raise ValueError('age cannot be negative')
841
842 return str(age)
843
844
783845 def is_resource_modified(environ, etag=None, data=None, last_modified=None,
784846 ignore_if_range=True):
785847 """Convenience method for conditional requests.
936998
937999 def dump_cookie(key, value='', max_age=None, expires=None, path='/',
9381000 domain=None, secure=False, httponly=False,
939 charset='utf-8', sync_expires=True):
1001 charset='utf-8', sync_expires=True, max_size=4093):
9401002 """Creates a new Set-Cookie header without the ``Set-Cookie`` prefix
9411003 The parameters are the same as in the cookie Morsel object in the
9421004 Python standard library but it accepts unicode data, too.
9721034 :param charset: the encoding for unicode values.
9731035 :param sync_expires: automatically set expires if max_age is defined
9741036 but expires not.
1037 :param max_size: Warn if the final header value exceeds this size. The
1038 default, 4093, should be safely `supported by most browsers
1039 <cookie_>`_. Set to 0 to disable this check.
1040
1041 .. _`cookie`: http://browsercookielimits.squawky.net/
9751042 """
9761043 key = to_bytes(key, charset)
9771044 value = to_bytes(value, charset)
10201087 rv = b'; '.join(buf)
10211088 if not PY2:
10221089 rv = rv.decode('latin1')
1090
1091 # Warn if the final value of the cookie is less than the limit. If the
1092 # cookie is too large, then it may be silently ignored, which can be quite
1093 # hard to debug.
1094 cookie_size = len(rv)
1095
1096 if max_size and cookie_size > max_size:
1097 value_size = len(value)
1098 warnings.warn(
1099 'The "{key}" cookie is too large: the value was {value_size} bytes'
1100 ' but the header required {extra_size} extra bytes. The final size'
1101 ' was {cookie_size} bytes but the limit is {max_size} bytes.'
1102 ' Browsers may silently ignore cookies larger than this.'.format(
1103 key=key,
1104 value_size=value_size,
1105 extra_size=cookie_size - value_size,
1106 cookie_size=cookie_size,
1107 max_size=max_size
1108 ),
1109 stacklevel=2
1110 )
1111
10231112 return rv
10241113
10251114
+0
-332
werkzeug/script.py less more
0 # -*- coding: utf-8 -*-
1 r'''
2 werkzeug.script
3 ~~~~~~~~~~~~~~~
4
5 .. admonition:: Deprecated Functionality
6
7 ``werkzeug.script`` is deprecated without replacement functionality.
8 Python's command line support improved greatly with :mod:`argparse`
9 and a bunch of alternative modules.
10
11 Most of the time you have recurring tasks while writing an application
12 such as starting up an interactive python interpreter with some prefilled
13 imports, starting the development server, initializing the database or
14 something similar.
15
16 For that purpose werkzeug provides the `werkzeug.script` module which
17 helps you writing such scripts.
18
19
20 Basic Usage
21 -----------
22
23 The following snippet is roughly the same in every werkzeug script::
24
25 #!/usr/bin/env python
26 # -*- coding: utf-8 -*-
27 from werkzeug import script
28
29 # actions go here
30
31 if __name__ == '__main__':
32 script.run()
33
34 Starting this script now does nothing because no actions are defined.
35 An action is a function in the same module starting with ``"action_"``
36 which takes a number of arguments where every argument has a default. The
37 type of the default value specifies the type of the argument.
38
39 Arguments can then be passed by position or using ``--name=value`` from
40 the shell.
41
42 Because a runserver and shell command is pretty common there are two
43 factory functions that create such commands::
44
45 def make_app():
46 from yourapplication import YourApplication
47 return YourApplication(...)
48
49 action_runserver = script.make_runserver(make_app, use_reloader=True)
50 action_shell = script.make_shell(lambda: {'app': make_app()})
51
52
53 Using The Scripts
54 -----------------
55
56 The script from above can be used like this from the shell now:
57
58 .. sourcecode:: text
59
60 $ ./manage.py --help
61 $ ./manage.py runserver localhost 8080 --debugger --no-reloader
62 $ ./manage.py runserver -p 4000
63 $ ./manage.py shell
64
65 As you can see it's possible to pass parameters as positional arguments
66 or as named parameters, pretty much like Python function calls.
67
68
69 :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
70 :license: BSD, see LICENSE for more details.
71 '''
72 from __future__ import print_function
73
74 import sys
75 import inspect
76 import getopt
77 from warnings import warn
78 from os.path import basename
79 from werkzeug._compat import iteritems
80
81
82 argument_types = {
83 bool: 'boolean',
84 str: 'string',
85 int: 'integer',
86 float: 'float'
87 }
88
89
90 converters = {
91 'boolean': lambda x: x.lower() in ('1', 'true', 'yes', 'on'),
92 'string': str,
93 'integer': int,
94 'float': float
95 }
96
97
98 def _deprecated():
99 warn(DeprecationWarning('werkzeug.script is deprecated and '
100 'will be removed soon'), stacklevel=2)
101
102
103 def run(namespace=None, action_prefix='action_', args=None):
104 """Run the script. Participating actions are looked up in the caller's
105 namespace if no namespace is given, otherwise in the dict provided.
106 Only items that start with action_prefix are processed as actions. If
107 you want to use all items in the namespace provided as actions set
108 action_prefix to an empty string.
109
110 :param namespace: An optional dict where the functions are looked up in.
111 By default the local namespace of the caller is used.
112 :param action_prefix: The prefix for the functions. Everything else
113 is ignored.
114 :param args: the arguments for the function. If not specified
115 :data:`sys.argv` without the first argument is used.
116 """
117 _deprecated()
118 if namespace is None:
119 namespace = sys._getframe(1).f_locals
120 actions = find_actions(namespace, action_prefix)
121
122 if args is None:
123 args = sys.argv[1:]
124 if not args or args[0] in ('-h', '--help'):
125 return print_usage(actions)
126 elif args[0] not in actions:
127 fail('Unknown action \'%s\'' % args[0])
128
129 arguments = {}
130 types = {}
131 key_to_arg = {}
132 long_options = []
133 formatstring = ''
134 func, doc, arg_def = actions[args.pop(0)]
135 for idx, (arg, shortcut, default, option_type) in enumerate(arg_def):
136 real_arg = arg.replace('-', '_')
137 if shortcut:
138 formatstring += shortcut
139 if not isinstance(default, bool):
140 formatstring += ':'
141 key_to_arg['-' + shortcut] = real_arg
142 long_options.append(isinstance(default, bool) and arg or arg + '=')
143 key_to_arg['--' + arg] = real_arg
144 key_to_arg[idx] = real_arg
145 types[real_arg] = option_type
146 arguments[real_arg] = default
147
148 try:
149 optlist, posargs = getopt.gnu_getopt(args, formatstring, long_options)
150 except getopt.GetoptError as e:
151 fail(str(e))
152
153 specified_arguments = set()
154 for key, value in enumerate(posargs):
155 try:
156 arg = key_to_arg[key]
157 except IndexError:
158 fail('Too many parameters')
159 specified_arguments.add(arg)
160 try:
161 arguments[arg] = converters[types[arg]](value)
162 except ValueError:
163 fail('Invalid value for argument %s (%s): %s' % (key, arg, value))
164
165 for key, value in optlist:
166 arg = key_to_arg[key]
167 if arg in specified_arguments:
168 fail('Argument \'%s\' is specified twice' % arg)
169 if types[arg] == 'boolean':
170 if arg.startswith('no_'):
171 value = 'no'
172 else:
173 value = 'yes'
174 try:
175 arguments[arg] = converters[types[arg]](value)
176 except ValueError:
177 fail('Invalid value for \'%s\': %s' % (key, value))
178
179 newargs = {}
180 for k, v in iteritems(arguments):
181 newargs[k.startswith('no_') and k[3:] or k] = v
182 arguments = newargs
183 return func(**arguments)
184
185
186 def fail(message, code=-1):
187 """Fail with an error."""
188 _deprecated()
189 print('Error: %s' % message, file=sys.stderr)
190 sys.exit(code)
191
192
193 def find_actions(namespace, action_prefix):
194 """Find all the actions in the namespace."""
195 _deprecated()
196 actions = {}
197 for key, value in iteritems(namespace):
198 if key.startswith(action_prefix):
199 actions[key[len(action_prefix):]] = analyse_action(value)
200 return actions
201
202
203 def print_usage(actions):
204 """Print the usage information. (Help screen)"""
205 _deprecated()
206 actions = sorted(iteritems(actions))
207 print('usage: %s <action> [<options>]' % basename(sys.argv[0]))
208 print(' %s --help' % basename(sys.argv[0]))
209 print()
210 print('actions:')
211 for name, (func, doc, arguments) in actions:
212 print(' %s:' % name)
213 for line in doc.splitlines():
214 print(' %s' % line)
215 if arguments:
216 print()
217 for arg, shortcut, default, argtype in arguments:
218 if isinstance(default, bool):
219 print(' %s' % (
220 (shortcut and '-%s, ' % shortcut or '') + '--' + arg
221 ))
222 else:
223 print(' %-30s%-10s%s' % (
224 (shortcut and '-%s, ' % shortcut or '') + '--' + arg,
225 argtype, default
226 ))
227 print()
228
229
230 def analyse_action(func):
231 """Analyse a function."""
232 _deprecated()
233 description = inspect.getdoc(func) or 'undocumented action'
234 arguments = []
235 args, varargs, kwargs, defaults = inspect.getargspec(func)
236 if varargs or kwargs:
237 raise TypeError('variable length arguments for action not allowed.')
238 if len(args) != len(defaults or ()):
239 raise TypeError('not all arguments have proper definitions')
240
241 for idx, (arg, definition) in enumerate(zip(args, defaults or ())):
242 if arg.startswith('_'):
243 raise TypeError('arguments may not start with an underscore')
244 if not isinstance(definition, tuple):
245 shortcut = None
246 default = definition
247 else:
248 shortcut, default = definition
249 argument_type = argument_types[type(default)]
250 if isinstance(default, bool) and default is True:
251 arg = 'no-' + arg
252 arguments.append((arg.replace('_', '-'), shortcut,
253 default, argument_type))
254 return func, description, arguments
255
256
257 def make_shell(init_func=None, banner=None, use_ipython=True):
258 """Returns an action callback that spawns a new interactive
259 python shell.
260
261 :param init_func: an optional initialization function that is
262 called before the shell is started. The return
263 value of this function is the initial namespace.
264 :param banner: the banner that is displayed before the shell. If
265 not specified a generic banner is used instead.
266 :param use_ipython: if set to `True` ipython is used if available.
267 """
268 _deprecated()
269 if banner is None:
270 banner = 'Interactive Werkzeug Shell'
271 if init_func is None:
272 init_func = dict
273
274 def action(ipython=use_ipython):
275 """Start a new interactive python session."""
276 namespace = init_func()
277 if ipython:
278 try:
279 try:
280 from IPython.frontend.terminal.embed import InteractiveShellEmbed
281 sh = InteractiveShellEmbed.instance(banner1=banner)
282 except ImportError:
283 from IPython.Shell import IPShellEmbed
284 sh = IPShellEmbed(banner=banner)
285 except ImportError:
286 pass
287 else:
288 sh(local_ns=namespace)
289 return
290 from code import interact
291 interact(banner, local=namespace)
292 return action
293
294
295 def make_runserver(app_factory, hostname='localhost', port=5000,
296 use_reloader=False, use_debugger=False, use_evalex=True,
297 threaded=False, processes=1, static_files=None,
298 extra_files=None, ssl_context=None):
299 """Returns an action callback that spawns a new development server.
300
301 .. versionadded:: 0.5
302 `static_files` and `extra_files` was added.
303
304 ..versionadded:: 0.6.1
305 `ssl_context` was added.
306
307 :param app_factory: a function that returns a new WSGI application.
308 :param hostname: the default hostname the server should listen on.
309 :param port: the default port of the server.
310 :param use_reloader: the default setting for the reloader.
311 :param use_evalex: the default setting for the evalex flag of the debugger.
312 :param threaded: the default threading setting.
313 :param processes: the default number of processes to start.
314 :param static_files: optional dict of static files.
315 :param extra_files: optional list of extra files to track for reloading.
316 :param ssl_context: optional SSL context for running server in HTTPS mode.
317 """
318 _deprecated()
319
320 def action(hostname=('h', hostname), port=('p', port),
321 reloader=use_reloader, debugger=use_debugger,
322 evalex=use_evalex, threaded=threaded, processes=processes):
323 """Start a new development server."""
324 from werkzeug.serving import run_simple
325 app = app_factory()
326 run_simple(hostname, port, app,
327 use_reloader=reloader, use_debugger=debugger,
328 use_evalex=evalex, extra_files=extra_files,
329 reloader_interval=1, threaded=threaded, processes=processes,
330 static_files=static_files, ssl_context=ssl_context)
331 return action
252252 cannot be done, this function returns ``None``.
253253
254254 :param directory: the base directory.
255 :param filename: the untrusted filename relative to that directory.
255 :param pathnames: the untrusted pathnames relative to that directory.
256256 """
257257 parts = [directory]
258258 for filename in pathnames:
2727 You can also pass it a `extra_files` keyword argument with a list of
2828 additional files (like configuration files) you want to observe.
2929
30 For bigger applications you should consider using `werkzeug.script`
31 instead of a simple start file.
30 For bigger applications you should consider using `click`
31 (http://click.pocoo.org) instead of a simple start file.
3232
3333
3434 :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
3636 """
3737 from __future__ import with_statement
3838
39 import io
3940 import os
4041 import socket
4142 import sys
9495
9596 LISTEN_QUEUE = 128
9697 can_open_by_fd = not WIN and hasattr(socket, 'fromfd')
98
99
100 class DechunkedInput(io.RawIOBase):
101 """An input stream that handles Transfer-Encoding 'chunked'"""
102
103 def __init__(self, rfile):
104 self._rfile = rfile
105 self._done = False
106 self._len = 0
107
108 def readable(self):
109 return True
110
111 def read_chunk_len(self):
112 try:
113 line = self._rfile.readline().decode('latin1')
114 _len = int(line.strip(), 16)
115 except ValueError:
116 raise IOError('Invalid chunk header')
117 if _len < 0:
118 raise IOError('Negative chunk length not allowed')
119 return _len
120
121 def readinto(self, buf):
122 read = 0
123 while not self._done and read < len(buf):
124 if self._len == 0:
125 # This is the first chunk or we fully consumed the previous
126 # one. Read the next length of the next chunk
127 self._len = self.read_chunk_len()
128
129 if self._len == 0:
130 # Found the final chunk of size 0. The stream is now exhausted,
131 # but there is still a final newline that should be consumed
132 self._done = True
133
134 if self._len > 0:
135 # There is data (left) in this chunk, so append it to the
136 # buffer. If this operation fully consumes the chunk, this will
137 # reset self._len to 0.
138 n = min(len(buf), self._len)
139 buf[read:read + n] = self._rfile.read(n)
140 self._len -= n
141 read += n
142
143 if self._len == 0:
144 # Skip the terminating newline of a chunk that has been fully
145 # consumed. This also applies to the 0-sized final chunk
146 terminator = self._rfile.readline()
147 if terminator not in (b'\n', b'\r\n', b'\r'):
148 raise IOError('Missing chunk terminating newline')
149
150 return read
97151
98152
99153 class WSGIRequestHandler(BaseHTTPRequestHandler, object):
139193 if key not in ('CONTENT_TYPE', 'CONTENT_LENGTH'):
140194 key = 'HTTP_' + key
141195 environ[key] = value
196
197 if environ.get('HTTP_TRANSFER_ENCODING', '').strip().lower() == 'chunked':
198 environ['wsgi.input_terminated'] = True
199 environ['wsgi.input'] = DechunkedInput(environ['wsgi.input'])
142200
143201 if request_url.scheme and request_url.netloc:
144202 environ['HTTP_HOST'] = request_url.netloc
295353
296354 if code[0] == '1': # 1xx - Informational
297355 msg = color(msg, attrs=['bold'])
298 if code[0] == '2': # 2xx - Success
356 elif code[0] == '2': # 2xx - Success
299357 msg = color(msg, color='white')
300358 elif code == '304': # 304 - Resource Not Modified
301359 msg = color(msg, color='cyan')
341399
342400 subject = cert.get_subject()
343401 subject.CN = cn
344 subject.O = 'Dummy Certificate'
402 subject.O = 'Dummy Certificate' # noqa: E741
345403
346404 issuer = cert.get_issuer()
347405 issuer.CN = 'Untrusted Authority'
348 issuer.O = 'Self-Signed'
406 issuer.O = 'Self-Signed' # noqa: E741
349407
350408 pkey = crypto.PKey()
351409 pkey.generate_key(crypto.TYPE_RSA, 2048)
657715 with a different
658716 :class:`~BaseHTTPServer.BaseHTTPRequestHandler`
659717 subclass.
660 :param static_files: a dict of paths for static files. This works exactly
661 like :class:`SharedDataMiddleware`, it's actually
718 :param static_files: a list or dict of paths for static files. This works
719 exactly like :class:`SharedDataMiddleware`, it's actually
662720 just wrapping the application in that middleware before
663721 serving.
664722 :param passthrough_errors: set this to `True` to disable the error catching.
670728 the server should automatically create one, or ``None``
671729 to disable SSL (which is the default).
672730 """
731 if not isinstance(port, int):
732 raise TypeError('port must be an integer')
673733 if use_debugger:
674734 from werkzeug.debug import DebuggedApplication
675735 application = DebuggedApplication(application, use_evalex)
3737 ((a + b).encode(), int(a + b, 16))
3838 for a in _hexdigits for b in _hexdigits
3939 )
40 _bytetohex = [
41 ('%%%02X' % char).encode('ascii') for char in range(256)
42 ]
4043
4144
4245 _URLTuple = fix_tuple_repr(namedtuple(
470473 if char in safe:
471474 rv.append(char)
472475 else:
473 rv.extend(('%%%02X' % char).encode('ascii'))
476 rv.extend(_bytetohex[char])
474477 return to_native(bytes(rv))
475478
476479
116116 - `aix`
117117 - `amiga`
118118 - `android`
119 - `blackberry`
119120 - `bsd`
120121 - `chromeos`
122 - `dragonflybsd`
123 - `freebsd`
121124 - `hpux`
125 - `ipad`
122126 - `iphone`
123 - `ipad`
124127 - `irix`
125128 - `linux`
126129 - `macos`
130 - `netbsd`
131 - `openbsd`
127132 - `sco`
128133 - `solaris`
134 - `symbian`
129135 - `wii`
130136 - `windows`
131137
136142
137143 - `aol` *
138144 - `ask` *
145 - `baidu` *
146 - `bing` *
139147 - `camino`
140148 - `chrome`
141149 - `firefox`
145153 - `konqueror`
146154 - `links`
147155 - `lynx`
156 - `mozilla`
148157 - `msie`
149158 - `msn`
150159 - `netscape`
452452 also recursively list modules but in that case it will import all the
453453 packages to get the correct load path of that module.
454454
455 :param import_name: the dotted name for the package to find child modules.
455 :param import_path: the dotted name for the package to find child modules.
456456 :param include_packages: set to `True` if packages should be returned, too.
457457 :param recursive: set to `True` if recursion should happen.
458458 :return: generator
2121 """
2222 from functools import update_wrapper
2323 from datetime import datetime, timedelta
24 from warnings import warn
2425
2526 from werkzeug.http import HTTP_STATUS_CODES, \
2627 parse_accept_header, parse_cache_control_header, parse_etags, \
2930 parse_www_authenticate_header, remove_entity_headers, \
3031 parse_options_header, dump_options_header, http_date, \
3132 parse_if_range_header, parse_cookie, dump_cookie, \
32 parse_range_header, parse_content_range_header, dump_header
33 parse_range_header, parse_content_range_header, dump_header, \
34 parse_age, dump_age
3335 from werkzeug.urls import url_decode, iri_to_uri, url_join
3436 from werkzeug.formparser import FormDataParser, default_stream_factory
3537 from werkzeug.utils import cached_property, environ_property, \
6163 to the WSGI server is not a string.
6264 """
6365 if isinstance(iterable, string_types):
64 from warnings import warn
6566 warn(Warning('response iterable was set to a string. This appears '
6667 'to work but means that the server will send the '
6768 'data to the client char, by char. This is almost '
321322 not provided because webbrowsers do not provide
322323 this value.
323324 """
324 return default_stream_factory(total_content_length, content_type,
325 filename, content_length)
325 return default_stream_factory(
326 total_content_length=total_content_length,
327 content_type=content_type,
328 filename=filename,
329 content_length=content_length)
326330
327331 @property
328332 def want_form_data_parsed(self):
334338 return bool(self.environ.get('CONTENT_TYPE'))
335339
336340 def make_form_data_parser(self):
337 """Creates the form data parser. Instanciates the
341 """Creates the form data parser. Instantiates the
338342 :attr:`form_data_parser_class` with some parameters.
339343
340344 .. versionadded:: 0.8
668672
669673 .. versionadded:: 0.7''')
670674
671 is_xhr = property(lambda x: x.environ.get('HTTP_X_REQUESTED_WITH', '')
672 .lower() == 'xmlhttprequest', doc='''
673 True if the request was triggered via a JavaScript XMLHttpRequest.
674 This only works with libraries that support the `X-Requested-With`
675 @property
676 def is_xhr(self):
677 """True if the request was triggered via a JavaScript XMLHttpRequest.
678 This only works with libraries that support the ``X-Requested-With``
675679 header and set it to "XMLHttpRequest". Libraries that do that are
676 prototype, jQuery and Mochikit and probably some more.''')
680 prototype, jQuery and Mochikit and probably some more.
681
682 .. deprecated:: 0.13
683 ``X-Requested-With`` is not standard and is unreliable.
684 """
685 warn(DeprecationWarning(
686 'Request.is_xhr is deprecated. Given that the X-Requested-With '
687 'header is not a part of any spec, it is not reliable'
688 ), stacklevel=2)
689 return self.environ.get(
690 'HTTP_X_REQUESTED_WITH', ''
691 ).lower() == 'xmlhttprequest'
692
677693 is_secure = property(lambda x: x.environ['wsgi.url_scheme'] == 'https',
678694 doc='`True` if the request is secure.')
679695 is_multithread = environ_property('wsgi.multithread', doc='''
789805 #:
790806 #: .. versionadded:: 0.8
791807 automatically_set_content_length = True
808
809 #: Warn if a cookie header exceeds this size. The default, 4093, should be
810 #: safely `supported by most browsers <cookie_>`_. A cookie larger than
811 #: this size will still be sent, but it may be ignored or handled
812 #: incorrectly by some browsers. Set to 0 to disable this check.
813 #:
814 #: .. versionadded:: 0.13
815 #:
816 #: .. _`cookie`: http://browsercookielimits.squawky.net/
817 max_cookie_size = 4093
792818
793819 def __init__(self, response=None, status=None, headers=None,
794820 mimetype=None, content_type=None, direct_passthrough=False):
918944 return self._status
919945
920946 def _set_status(self, value):
921 self._status = to_native(value)
947 try:
948 self._status = to_native(value)
949 except AttributeError:
950 raise TypeError('Invalid status argument')
951
922952 try:
923953 self._status_code = int(self._status.split(None, 1)[0])
924954 except ValueError:
925955 self._status_code = 0
926956 self._status = '0 %s' % self._status
957 except IndexError:
958 raise ValueError('Empty status argument')
927959 status = property(_get_status, _set_status, doc='The HTTP Status code')
928960 del _get_status, _set_status
929961
9741006 self._ensure_sequence()
9751007 except RuntimeError:
9761008 return None
977 return sum(len(x) for x in self.response)
1009 return sum(len(x) for x in self.iter_encoded())
9781010
9791011 def _ensure_sequence(self, mutable=False):
9801012 """This method can be called by methods that need a sequence. If
10331065 path='/', domain=None, secure=False, httponly=False):
10341066 """Sets a cookie. The parameters are the same as in the cookie `Morsel`
10351067 object in the Python standard library but it accepts unicode data, too.
1068
1069 A warning is raised if the size of the cookie header exceeds
1070 :attr:`max_cookie_size`, but the header will still be set.
10361071
10371072 :param key: the key (name) of the cookie to be set.
10381073 :param value: the value of the cookie.
10521087 extension to the cookie standard and probably not
10531088 supported by all browsers.
10541089 """
1055 self.headers.add('Set-Cookie', dump_cookie(key,
1056 value=value,
1057 max_age=max_age,
1058 expires=expires,
1059 path=path,
1060 domain=domain,
1061 secure=secure,
1062 httponly=httponly,
1063 charset=self.charset))
1090 self.headers.add('Set-Cookie', dump_cookie(
1091 key,
1092 value=value,
1093 max_age=max_age,
1094 expires=expires,
1095 path=path,
1096 domain=domain,
1097 secure=secure,
1098 httponly=httponly,
1099 charset=self.charset,
1100 max_size=self.max_cookie_size
1101 ))
10641102
10651103 def delete_cookie(self, key, path='/', domain=None):
10661104 """Delete a cookie. Fails silently if key doesn't exist.
16461684 self.response._ensure_sequence(mutable=True)
16471685 self.response.response.append(value)
16481686 self.response.headers.pop('Content-Length', None)
1687 return len(value)
16491688
16501689 def writelines(self, seq):
16511690 for item in seq:
16621701 if self.closed:
16631702 raise ValueError('I/O operation on closed file')
16641703 return False
1704
1705 def tell(self):
1706 self.response._ensure_sequence()
1707 return sum(map(len, self.response.response))
16651708
16661709 @property
16671710 def encoding(self):
18051848 The Location response-header field is used to redirect the recipient
18061849 to a location other than the Request-URI for completion of the request
18071850 or identification of a new resource.''')
1808 age = header_property('Age', None, parse_date, http_date, doc='''
1851 age = header_property('Age', None, parse_age, dump_age, doc='''
18091852 The Age response-header field conveys the sender's estimate of the
18101853 amount of time since the response (or its revalidation) was
18111854 generated at the origin server.
77 :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details.
88 :license: BSD, see LICENSE for more details.
99 """
10 import re
10 import io
11 import mimetypes
1112 import os
1213 import posixpath
13 import mimetypes
14 from itertools import chain
15 from zlib import adler32
16 from time import time, mktime
14 import re
1715 from datetime import datetime
1816 from functools import partial, update_wrapper
19
20 from werkzeug._compat import iteritems, text_type, string_types, \
21 implements_iterator, make_literal_wrapper, to_unicode, to_bytes, \
22 wsgi_get_bytes, try_coerce_native, PY2, BytesIO
17 from itertools import chain
18 from time import mktime, time
19 from zlib import adler32
20
21 from werkzeug._compat import BytesIO, PY2, implements_iterator, iteritems, \
22 make_literal_wrapper, string_types, text_type, to_bytes, to_unicode, \
23 try_coerce_native, wsgi_get_bytes
2324 from werkzeug._internal import _empty_stream, _encode_idna
24 from werkzeug.http import is_resource_modified, http_date
25 from werkzeug.urls import uri_to_iri, url_quote, url_parse, url_join
2625 from werkzeug.filesystem import get_filesystem_encoding
26 from werkzeug.http import http_date, is_resource_modified
27 from werkzeug.urls import uri_to_iri, url_join, url_parse, url_quote
2728
2829
2930 def responder(f):
164165
165166 def get_content_length(environ):
166167 """Returns the content length from the WSGI environment as
167 integer. If it's not available `None` is returned.
168 integer. If it's not available or chunked transfer encoding is used,
169 ``None`` is returned.
168170
169171 .. versionadded:: 0.9
170172
171173 :param environ: the WSGI environ to fetch the content length from.
172174 """
175 if environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked':
176 return None
177
173178 content_length = environ.get('CONTENT_LENGTH')
174179 if content_length is not None:
175180 try:
180185
181186 def get_input_stream(environ, safe_fallback=True):
182187 """Returns the input stream from the WSGI environment and wraps it
183 in the most sensible way possible. The stream returned is not the
188 in the most sensible way possible. The stream returned is not the
184189 raw WSGI stream in most cases but one that is safe to read from
185190 without taking into account the content length.
186191
192 If content length is not set, the stream will be empty for safety reasons.
193 If the WSGI server supports chunked or infinite streams, it should set
194 the ``wsgi.input_terminated`` value in the WSGI environ to indicate that.
195
187196 .. versionadded:: 0.9
188197
189198 :param environ: the WSGI environ to fetch the stream from.
190 :param safe: indicates whether the function should use an empty
191 stream as safe fallback or just return the original
192 WSGI input stream if it can't wrap it safely. The
193 default is to return an empty string in those cases.
199 :param safe_fallback: use an empty stream as a safe fallback when the
200 content length is not set. Disabling this allows infinite streams,
201 which can be a denial-of-service risk.
194202 """
195203 stream = environ['wsgi.input']
196204 content_length = get_content_length(environ)
201209 if environ.get('wsgi.input_terminated'):
202210 return stream
203211
204 # If we don't have a content length we fall back to an empty stream
205 # in case of a safe fallback, otherwise we return the stream unchanged.
206 # The non-safe fallback is not recommended but might be useful in
207 # some situations.
212 # If the request doesn't specify a content length, returning the stream is
213 # potentially dangerous because it could be infinite, malicious or not. If
214 # safe_fallback is true, return an empty stream instead for safety.
208215 if content_length is None:
209216 return safe_fallback and _empty_stream or stream
210217
480487
481488 :param app: the application to wrap. If you don't want to wrap an
482489 application you can pass it :exc:`NotFound`.
483 :param exports: a dict of exported files and folders.
490 :param exports: a list or dict of exported files and folders.
484491 :param disallow: a list of :func:`~fnmatch.fnmatch` rules.
485492 :param fallback_mimetype: the fallback mimetype for unknown files.
486493 :param cache: enable or disable caching headers.
490497 def __init__(self, app, exports, disallow=None, cache=True,
491498 cache_timeout=60 * 60 * 12, fallback_mimetype='text/plain'):
492499 self.app = app
493 self.exports = {}
500 self.exports = []
494501 self.cache = cache
495502 self.cache_timeout = cache_timeout
496 for key, value in iteritems(exports):
503 if hasattr(exports, 'items'):
504 exports = iteritems(exports)
505 for key, value in exports:
497506 if isinstance(value, tuple):
498507 loader = self.get_package_loader(*value)
499508 elif isinstance(value, string_types):
503512 loader = self.get_directory_loader(value)
504513 else:
505514 raise TypeError('unknown def %r' % value)
506 self.exports[key] = loader
515 self.exports.append((key, loader))
507516 if disallow is not None:
508517 from fnmatch import fnmatch
509518 self.is_allowed = lambda x: not fnmatch(x, disallow)
584593 path = '/' + '/'.join(x for x in cleaned_path.split('/')
585594 if x and x != '..')
586595 file_loader = None
587 for search_path, loader in iteritems(self.exports):
596 for search_path, loader in self.exports:
588597 if search_path == path:
589598 real_filename, file_loader = loader(None)
590599 if file_loader is not None:
10401049
10411050
10421051 @implements_iterator
1043 class LimitedStream(object):
1052 class LimitedStream(io.IOBase):
10441053
10451054 """Wraps a stream so that it doesn't read more than n bytes. If the
10461055 stream is exhausted and the caller tries to get more bytes from it
11921201 if not line:
11931202 raise StopIteration()
11941203 return line
1204
1205 def readable(self):
1206 return True