Import python-werkzeug_0.13+dfsg1.orig.tar.gz
Ondřej Nový
6 years ago
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 |
5 | 5 | *.pyo |
6 | 6 | env |
7 | 7 | .DS_Store |
8 | .tox | |
9 | 8 | docs/_build |
10 | 9 | bench/a |
11 | 10 | bench/b |
11 | .tox | |
12 | 12 | .coverage |
13 | .coverage.* | |
13 | 14 | coverage_out |
15 | htmlcov | |
14 | 16 | .cache |
15 | 17 | .xprocess |
16 | htmlcov | |
17 | 18 | .hypothesis |
19 | test_uwsgi_failed | |
20 | .idea |
0 | os: linux | |
0 | 1 | sudo: false |
1 | 2 | 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 | |
15 | 3 | |
16 | 4 | 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 | |
28 | 26 | |
29 | 27 | before_install: |
30 | 28 | - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then |
31 | 29 | brew update; |
32 | 30 | brew install python3 redis memcached; |
33 | virtualenv ~/py-env -p python3; | |
31 | virtualenv -p python3 ~/py-env; | |
34 | 32 | . ~/py-env/bin/activate; |
35 | 33 | 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; | |
41 | 37 | PYENV_ROOT="$HOME/.pyenv"; |
42 | 38 | PATH="$PYENV_ROOT/bin:$PATH"; |
43 | 39 | 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; | |
46 | 42 | 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 | |
48 | 51 | |
49 | 52 | install: |
50 | - pip install tox flake8 | |
53 | - pip install tox | |
51 | 54 | |
52 | 55 | script: |
53 | - tox -e py-$TOXENV_SUFFIX | |
56 | - tox | |
57 | ||
58 | cache: | |
59 | - pip | |
54 | 60 | |
55 | 61 | branches: |
56 | only: | |
57 | - master | |
58 | - auto | |
59 | - /^.*-maintenance$/ | |
62 | only: | |
63 | - master | |
64 | - /^.*-maintenance$/ | |
60 | 65 | |
61 | 66 | notifications: |
62 | 67 | 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: | |
2 | 3 | |
3 | Project Leader / Developer: | |
4 | - Armin Ronacher (mitsuhiko) | |
5 | - Marcus Unterwaditzer (untitaker) | |
6 | - Adrian Mönnich (ThiefMaster) | |
7 | - David Lord (davidism) | |
4 | 8 | |
5 | - Armin Ronacher <armin.ronacher@active-4.com> | |
9 | A full list of contributors is available from git with: | |
6 | 10 | |
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 | |
39 | 12 | |
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 | |
48 | 17 | apply: |
49 | 18 | |
50 | 19 | - (c) 2005 Ian Bicking and contributors |
51 | 20 | - (c) 2005 Clark C. Evans |
52 | 21 | |
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: | |
56 | 25 | |
57 | 26 | - (c) 2003-2009 Edgewall Software |
58 | 27 | - (c) 2003-2006 Jonas Borgström <jonas@edgewall.com> |
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 | ============================= | |
1 | 0 | How to contribute to Werkzeug |
2 | 1 | ============================= |
3 | 2 | |
4 | Thanks for considering contributing to Werkzeug. | |
3 | Thank you for considering contributing to Werkzeug! | |
4 | ||
5 | 5 | |
6 | 6 | Support questions |
7 | ================= | |
7 | ----------------- | |
8 | 8 | |
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 | ||
14 | 20 | |
15 | 21 | Reporting issues |
16 | ================ | |
22 | ---------------- | |
17 | 23 | |
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. | |
20 | 32 | |
21 | - Under which versions of Werkzeug does this happen? Check if this issue is | |
22 | fixed in the repository. | |
23 | 33 | |
24 | 34 | 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 | ------------------ | |
30 | 36 | |
31 | 37 | - 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. | |
39 | 41 | |
40 | 42 | |
41 | Running the testsuite | |
42 | --------------------- | |
43 | First time setup | |
44 | ~~~~~~~~~~~~~~~~ | |
43 | 45 | |
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`_:: | |
46 | 48 | |
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' | |
48 | 51 | |
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:: | |
50 | 55 | |
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 | |
53 | 58 | |
54 | pip install pytest | |
59 | - Add the main repository as a remote to update later:: | |
55 | 60 | |
56 | Then you can run the testsuite with:: | |
61 | git remote add pallets https://github.com/pallets/werkzeug | |
62 | git fetch pallets | |
57 | 63 | |
58 | py.test | |
64 | - Create a virtualenv:: | |
59 | 65 | |
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 | |
64 | 69 | |
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:: | |
67 | 71 | |
68 | pip install tox | |
72 | pip install -e ".[dev]" | |
69 | 73 | |
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 | |
7 | 6 | prune docs/_build |
8 | 7 | 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 | ||
10 | 0 | documentation: |
11 | 1 | @(cd docs; make html) |
12 | 2 | |
14 | 4 | python scripts/make-release.py |
15 | 5 | |
16 | 6 | test: |
17 | py.test --tb=native | |
7 | pytest | |
18 | 8 | |
19 | 9 | tox-test: |
20 | 10 | tox |
21 | 11 | |
22 | 12 | coverage: |
23 | @(coverage run --source=werkzeug --module py.test $(TEST_OPTIONS) $(TESTS)) | |
13 | @(coverage run --module pytest $(TEST_OPTIONS) $(TESTS)) | |
24 | 14 | |
25 | 15 | doctest: |
26 | 16 | @(cd docs; sphinx-build -b doctest . _build/doctest) |
0 | 0 | Werkzeug |
1 | 1 | ======== |
2 | 2 | |
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. | |
9 | 6 | |
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: | |
16 | 8 | |
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. | |
19 | 33 | |
20 | 34 | |
21 | Branches | |
22 | -------- | |
35 | Installing | |
36 | ---------- | |
23 | 37 | |
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 | 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" |
7 | 7 | For API breaking changes have a look at :ref:`api-changes`, they |
8 | 8 | are listed there in detail. |
9 | 9 | |
10 | .. include:: ../CHANGES | |
10 | .. include:: ../CHANGES.rst | |
11 | 11 | |
12 | 12 | .. _api-changes: |
13 | 13 |
62 | 62 | Do not use: |
63 | 63 | |
64 | 64 | - `werkzeug.script`, replace it with custom scripts written with |
65 | `argparse` or something similar. | |
65 | `argparse`, `click` or something similar. | |
66 | 66 | - `werkzeug.template`, replace with a proper template engine. |
67 | 67 | - `werkzeug.contrib.jsrouting`, stop using URL generation for |
68 | 68 | JavaScript, it does not scale well with many public routing. |
6 | 6 | |
7 | 7 | Beside the proof of concept applications and code snippets in the partial |
8 | 8 | 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. | |
10 | 11 | |
11 | 12 | |
12 | 13 | Full Example Applications |
66 | 66 | app = CoolMagicApplication(config) |
67 | 67 | |
68 | 68 | # static stuff |
69 | from werkzeug.utils import SharedDataMiddleware | |
69 | from werkzeug.wsgi import SharedDataMiddleware | |
70 | 70 | app = SharedDataMiddleware(app, { |
71 | 71 | '/public': path.join(path.dirname(__file__), 'public') |
72 | 72 | }) |
0 | 0 | from datetime import datetime |
1 | from couchdb.schema import Document, TextField, BooleanField, DateTimeField | |
1 | from couchdb.mapping import Document, TextField, BooleanField, DateTimeField | |
2 | 2 | from couchy.utils import url_for, get_random_uid |
3 | 3 | |
4 | 4 |
8 | 8 | :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details. |
9 | 9 | :license: BSD, see LICENSE for more details. |
10 | 10 | """ |
11 | import os | |
11 | import click | |
12 | 12 | from coolmagic import make_app |
13 | from werkzeug import script | |
13 | from werkzeug.serving import run_simple | |
14 | 14 | |
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) | |
17 | 60 | |
18 | 61 | if __name__ == '__main__': |
19 | script.run() | |
62 | cli() |
0 | 0 | #!/usr/bin/env python |
1 | from werkzeug import script | |
1 | import click | |
2 | from werkzeug.serving import run_simple | |
3 | ||
2 | 4 | |
3 | 5 | def make_app(): |
4 | 6 | from couchy.application import Couchy |
5 | 7 | return Couchy('http://localhost:5984') |
8 | ||
6 | 9 | |
7 | 10 | def make_shell(): |
8 | 11 | from couchy import models, utils |
9 | 12 | application = make_app() |
10 | 13 | return locals() |
11 | 14 | |
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() | |
15 | 15 | |
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() |
7 | 7 | :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details. |
8 | 8 | :license: BSD, see LICENSE for more details. |
9 | 9 | """ |
10 | from werkzeug import script | |
10 | import click | |
11 | from werkzeug.serving import run_simple | |
11 | 12 | |
12 | 13 | |
13 | 14 | def make_app(): |
14 | 15 | from cupoftee import make_app |
15 | 16 | return make_app('/tmp/cupoftee.db') |
16 | action_runserver = script.make_runserver(make_app) | |
17 | 17 | |
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() |
8 | 8 | :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details. |
9 | 9 | :license: BSD, see LICENSE for more details. |
10 | 10 | """ |
11 | import os | |
11 | import click | |
12 | 12 | from i18nurls import make_app |
13 | from werkzeug import script | |
13 | from werkzeug.serving import run_simple | |
14 | 14 | |
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) | |
17 | 60 | |
18 | 61 | if __name__ == '__main__': |
19 | script.run() | |
62 | cli() |
8 | 8 | :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details. |
9 | 9 | :license: BSD, see LICENSE for more details. |
10 | 10 | """ |
11 | import click | |
11 | 12 | import os |
12 | from werkzeug import script | |
13 | from werkzeug.serving import run_simple | |
13 | 14 | |
14 | 15 | |
15 | 16 | def make_app(): |
21 | 22 | return app |
22 | 23 | |
23 | 24 | |
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 | |
26 | 28 | |
27 | 29 | |
28 | def action_initdb(): | |
30 | @cli.command() | |
31 | def initdb(): | |
29 | 32 | """Initialize the database""" |
30 | 33 | from plnt.database import Blog, session |
31 | 34 | make_app().init_database() |
50 | 53 | for blog in blogs: |
51 | 54 | session.add(blog) |
52 | 55 | 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') | |
54 | 57 | |
55 | 58 | |
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(): | |
57 | 102 | """Sync the blogs in the planet. Call this from a cronjob.""" |
58 | 103 | from plnt.sync import sync |
59 | 104 | make_app().bind_to_context() |
60 | 105 | sync() |
61 | 106 | |
62 | ||
63 | 107 | if __name__ == '__main__': |
64 | script.run() | |
108 | cli() |
0 | 0 | #!/usr/bin/env python |
1 | import click | |
1 | 2 | import os |
2 | 3 | import tempfile |
3 | from werkzeug import script | |
4 | from werkzeug.serving import run_simple | |
5 | ||
4 | 6 | |
5 | 7 | def make_app(): |
6 | 8 | from shorty.application import Shorty |
7 | 9 | filename = os.path.join(tempfile.gettempdir(), "shorty.db") |
8 | 10 | return Shorty('sqlite:///{0}'.format(filename)) |
9 | 11 | |
12 | ||
10 | 13 | def make_shell(): |
11 | 14 | from shorty import models, utils |
12 | 15 | application = make_app() |
13 | 16 | return locals() |
14 | 17 | |
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() | |
18 | 18 | |
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() |
8 | 8 | :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details. |
9 | 9 | :license: BSD, see LICENSE for more details. |
10 | 10 | """ |
11 | import click | |
11 | 12 | import os |
12 | from werkzeug import script | |
13 | import tempfile | |
14 | from werkzeug.serving import run_simple | |
13 | 15 | |
14 | 16 | |
15 | 17 | def make_wiki(): |
19 | 21 | return SimpleWiki(database_uri or 'sqlite:////tmp/simplewiki.db') |
20 | 22 | |
21 | 23 | |
22 | def shell_init_func(): | |
23 | """ | |
24 | Called on shell initialization. Adds useful stuff to the namespace. | |
25 | """ | |
24 | def make_shell(): | |
26 | 25 | from simplewiki import database |
27 | 26 | wiki = make_wiki() |
28 | 27 | wiki.bind_to_context() |
32 | 31 | } |
33 | 32 | |
34 | 33 | |
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 | |
37 | 37 | |
38 | 38 | |
39 | def action_initdb(): | |
40 | """Initialize the database""" | |
39 | @cli.command() | |
40 | def initdb(): | |
41 | 41 | make_wiki().init_database() |
42 | 42 | |
43 | 43 | |
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 | ||
44 | 84 | if __name__ == '__main__': |
45 | script.run() | |
85 | cli() |
12 | 12 | :copyright: (c) 2009 by the Werkzeug Team, see AUTHORS for more details. |
13 | 13 | :license: BSD, see LICENSE for more details. |
14 | 14 | """ |
15 | import click | |
15 | 16 | import os |
16 | 17 | import sys |
17 | 18 | sys.path.append(os.path.join(os.path.dirname(__file__), 'webpylike')) |
18 | 19 | from example import app |
19 | from werkzeug import script | |
20 | from werkzeug.serving import run_simple | |
20 | 21 | |
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) | |
23 | 66 | |
24 | 67 | if __name__ == '__main__': |
25 | script.run() | |
68 | cli() |
0 | 0 | #!/usr/bin/env python |
1 | 1 | # -*- coding: utf-8 -*- |
2 | """ | |
3 | make-release | |
4 | ~~~~~~~~~~~~ | |
2 | from __future__ import print_function | |
5 | 3 | |
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 | |
14 | 4 | import os |
15 | 5 | 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 | |
18 | 9 | |
19 | _date_clean_re = re.compile(r'(\d+)(st|nd|rd|th)') | |
10 | _date_strip_re = re.compile(r'(?<=\d)(st|nd|rd|th)') | |
20 | 11 | |
21 | 12 | |
22 | 13 | def parse_changelog(): |
23 | with open('CHANGES') as f: | |
14 | with open('CHANGES.rst') as f: | |
24 | 15 | lineiter = iter(f) |
25 | 16 | for line in lineiter: |
26 | 17 | match = re.search('^Version\s+(.*)', line.strip()) |
18 | ||
27 | 19 | if match is None: |
28 | 20 | continue |
21 | ||
29 | 22 | version = match.group(1).strip() |
30 | if lineiter.next().count('-') != len(match.group(0)): | |
23 | ||
24 | if next(lineiter).count('-') != len(match.group(0)): | |
31 | 25 | continue |
26 | ||
32 | 27 | while 1: |
33 | change_info = lineiter.next().strip() | |
28 | change_info = next(lineiter).strip() | |
29 | ||
34 | 30 | if change_info: |
35 | 31 | break |
36 | 32 | |
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 | ||
40 | 39 | if match is None: |
41 | 40 | continue |
42 | 41 | |
46 | 45 | |
47 | 46 | def bump_version(version): |
48 | 47 | try: |
49 | parts = map(int, version.split('.')) | |
48 | parts = [int(i) for i in version.split('.')] | |
50 | 49 | except ValueError: |
51 | 50 | fail('Current version is not numeric') |
51 | ||
52 | 52 | parts[-1] += 1 |
53 | 53 | return '.'.join(map(str, parts)) |
54 | 54 | |
55 | 55 | |
56 | 56 | def parse_date(string): |
57 | string = _date_clean_re.sub(r'\1', string) | |
57 | string = _date_strip_re.sub('', string) | |
58 | 58 | return datetime.strptime(string, '%B %d %Y') |
59 | 59 | |
60 | 60 | |
65 | 65 | before, old, after = match.groups() |
66 | 66 | changed.append(True) |
67 | 67 | return before + version_number + after |
68 | ||
68 | 69 | 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 | ) | |
72 | 75 | |
73 | 76 | if not changed: |
74 | 77 | fail('Could not find %s in %s', pattern, filename) |
82 | 85 | set_filename_version('werkzeug/__init__.py', version, '__version__') |
83 | 86 | |
84 | 87 | |
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() | |
92 | 91 | |
93 | 92 | |
94 | 93 | def fail(message, *args): |
95 | print >> sys.stderr, 'Error:', message % args | |
94 | print('Error:', message % args, file=sys.stderr) | |
96 | 95 | sys.exit(1) |
97 | 96 | |
98 | 97 | |
99 | 98 | def info(message, *args): |
100 | print >> sys.stderr, message % args | |
99 | print(message % args, file=sys.stderr) | |
101 | 100 | |
102 | 101 | |
103 | 102 | 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 | ) | |
107 | 106 | |
108 | 107 | |
109 | 108 | def git_is_clean(): |
124 | 123 | os.chdir(os.path.join(os.path.dirname(__file__), '..')) |
125 | 124 | |
126 | 125 | rv = parse_changelog() |
126 | ||
127 | 127 | if rv is None: |
128 | 128 | fail('Could not parse changelog') |
129 | 129 | |
130 | 130 | version, release_date, codename = rv |
131 | dev_version = bump_version(version) + '-dev' | |
131 | dev_version = bump_version(version) + '.dev' | |
132 | 132 | |
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 | ) | |
135 | 137 | tags = get_git_tags() |
136 | 138 | |
137 | 139 | if version in tags: |
138 | 140 | fail('Version "%s" is already tagged', version) |
141 | ||
139 | 142 | 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 | ) | |
142 | 147 | |
143 | 148 | if not git_is_clean(): |
144 | 149 | fail('You have uncommitted changes in git') |
145 | 150 | |
151 | try: | |
152 | import wheel # noqa: F401 | |
153 | except ImportError: | |
154 | fail('You need to install the wheel package.') | |
155 | ||
146 | 156 | set_init_version(version) |
147 | 157 | make_git_commit('Bump version number to %s', version) |
148 | 158 | make_git_tag(version) |
149 | build_and_upload() | |
159 | build() | |
150 | 160 | set_init_version(dev_version) |
151 | 161 | |
152 | 162 |
0 | [aliases] | |
1 | release = egg_info -RDb '' | |
2 | ||
3 | 0 | [tool:pytest] |
4 | norecursedirs = .* env* docs *.egg tests/hypothesis | |
1 | minversion = 3.0 | |
2 | testpaths = tests | |
3 | norecursedirs = tests/hypothesis | |
5 | 4 | |
6 | 5 | [bdist_wheel] |
7 | 6 | universal = 1 |
0 | # -*- coding: utf-8 -*- | |
1 | """ | |
2 | Werkzeug | |
3 | ======== | |
0 | #!/usr/bin/env python | |
1 | import io | |
2 | import re | |
4 | 3 | |
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 | |
11 | 5 | |
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() | |
18 | 8 | |
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) | |
83 | 12 | |
84 | 13 | setup( |
85 | 14 | name='Werkzeug', |
86 | 15 | version=version, |
87 | url='http://werkzeug.pocoo.org/', | |
16 | url='https://www.palletsprojects.org/p/werkzeug/', | |
88 | 17 | license='BSD', |
89 | 18 | author='Armin Ronacher', |
90 | 19 | 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, | |
93 | 22 | classifiers=[ |
94 | 23 | 'Development Status :: 5 - Production/Stable', |
95 | 24 | 'Environment :: Web Environment', |
104 | 33 | 'Programming Language :: Python :: 3.3', |
105 | 34 | 'Programming Language :: Python :: 3.4', |
106 | 35 | 'Programming Language :: Python :: 3.5', |
36 | 'Programming Language :: Python :: 3.6', | |
107 | 37 | 'Topic :: Internet :: WWW/HTTP :: Dynamic Content', |
108 | 'Topic :: Software Development :: Libraries :: Python Modules' | |
38 | 'Topic :: Software Development :: Libraries :: Python Modules', | |
109 | 39 | ], |
110 | packages=['werkzeug', 'werkzeug.debug', 'werkzeug.contrib'], | |
40 | packages=find_packages(exclude=('tests*',)), | |
111 | 41 | extras_require={ |
112 | 42 | 'watchdog': ['watchdog'], |
113 | 43 | 'termcolor': ['termcolor'], |
44 | 'dev': [ | |
45 | 'pytest', | |
46 | 'coverage', | |
47 | 'tox', | |
48 | 'sphinx', | |
49 | ], | |
114 | 50 | }, |
115 | cmdclass=dict(test=TestCommand), | |
116 | 51 | include_package_data=True, |
117 | 52 | zip_safe=False, |
118 | 53 | 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 | ||
13 | 0 | 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.""" | |
15 | 3 | __tracebackhide__ = True |
16 | 4 | assert x == y |
17 | 5 | assert issubclass(type(x), type(y)) or issubclass(type(y), type(x)) |
5 | 5 | :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. |
6 | 6 | :license: BSD, see LICENSE for more details. |
7 | 7 | """ |
8 | ||
9 | from __future__ import with_statement | |
8 | from __future__ import with_statement, print_function | |
10 | 9 | |
11 | 10 | import os |
12 | 11 | import signal |
25 | 24 | try: |
26 | 25 | __import__('pytest_xprocess') |
27 | 26 | except ImportError: |
28 | @pytest.fixture | |
27 | @pytest.fixture(scope='session') | |
29 | 28 | def subprocess(): |
30 | 29 | pytest.skip('pytest-xprocess not installed.') |
31 | 30 | else: |
32 | @pytest.fixture | |
31 | @pytest.fixture(scope='session') | |
33 | 32 | def subprocess(xprocess): |
34 | 33 | return xprocess |
35 | 34 | |
37 | 36 | def _patch_reloader_loop(): |
38 | 37 | def f(x): |
39 | 38 | 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() | |
40 | 43 | return time.sleep(x) |
41 | 44 | |
42 | 45 | 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 | # -*- 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 |
35 | 35 | assert not c3.new |
36 | 36 | assert c3 == {} |
37 | 37 | |
38 | c4 = SecureCookie({'x': 42}, 'foo') | |
39 | c4_serialized = c4.serialize() | |
40 | assert SecureCookie.unserialize(c4_serialized, 'foo') == c4 | |
41 | ||
38 | 42 | |
39 | 43 | def test_wrapper_support(): |
40 | 44 | req = Request.from_values() |
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 |
542 | 542 | assert sorted(iterlists(ab)) == [('key_a', ['value_a']), ('key_b', ['value_b'])] |
543 | 543 | assert sorted(iterlistvalues(ab)) == [['value_a'], ['value_b']] |
544 | 544 | 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]) | |
545 | 563 | |
546 | 564 | |
547 | 565 | class TestCombinedMultiDict(object): |
806 | 824 | storage_class = datastructures.ImmutableList |
807 | 825 | |
808 | 826 | 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 | |
813 | 831 | |
814 | 832 | |
815 | 833 | def make_call_asserter(func=None): |
127 | 127 | assert a.realm == 'testrealm@host.invalid' |
128 | 128 | assert a.nonce == 'dcd98b7102dd2f0e8b11d0f600bfb0c093' |
129 | 129 | assert a.uri == '/dir/index.html' |
130 | assert 'auth' in a.qop | |
130 | assert a.qop == 'auth' | |
131 | 131 | assert a.nc == '00000001' |
132 | 132 | assert a.cnonce == '0a4f113b' |
133 | 133 | assert a.response == '6629fae49393a05397450978507c4ef1' |
279 | 279 | ('form-data', {'name': u'\u016an\u012dc\u014dde\u033d', |
280 | 280 | 'filename': 'some_file.txt'}) |
281 | 281 | |
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 | ||
282 | 290 | def test_parse_options_header_broken_values(self): |
283 | 291 | # Issue #995 |
284 | 292 | assert http.parse_options_header(' ') == ('', {}) |
416 | 424 | val = http.dump_cookie('foo', 'bar', domain=u'.foo.com') |
417 | 425 | strict_eq(val, 'foo=bar; Domain=.foo.com; Path=/') |
418 | 426 | |
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 | ||
419 | 442 | |
420 | 443 | class TestRange(object): |
421 | 444 |
18 | 18 | |
19 | 19 | |
20 | 20 | def test_basic_local(): |
21 | l = local.Local() | |
22 | l.foo = 0 | |
21 | ns = local.Local() | |
22 | ns.foo = 0 | |
23 | 23 | values = [] |
24 | 24 | |
25 | 25 | def value_setter(idx): |
26 | 26 | time.sleep(0.01 * idx) |
27 | l.foo = idx | |
27 | ns.foo = idx | |
28 | 28 | time.sleep(0.02) |
29 | values.append(l.foo) | |
29 | values.append(ns.foo) | |
30 | 30 | threads = [Thread(target=value_setter, args=(x,)) |
31 | 31 | for x in [1, 2, 3]] |
32 | 32 | for thread in threads: |
35 | 35 | assert sorted(values) == [1, 2, 3] |
36 | 36 | |
37 | 37 | def delfoo(): |
38 | del l.foo | |
38 | del ns.foo | |
39 | 39 | delfoo() |
40 | pytest.raises(AttributeError, lambda: l.foo) | |
40 | pytest.raises(AttributeError, lambda: ns.foo) | |
41 | 41 | pytest.raises(AttributeError, delfoo) |
42 | 42 | |
43 | local.release_local(l) | |
43 | local.release_local(ns) | |
44 | 44 | |
45 | 45 | |
46 | 46 | 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') | |
51 | 51 | |
52 | 52 | ls = local.LocalStack() |
53 | 53 | ls.push(42) |
138 | 138 | |
139 | 139 | def test_custom_idents(): |
140 | 140 | ident = 0 |
141 | l = local.Local() | |
141 | ns = local.Local() | |
142 | 142 | 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 | |
146 | 146 | stack.push({'foo': 42}) |
147 | 147 | ident = 1 |
148 | l.foo = 23 | |
148 | ns.foo = 23 | |
149 | 149 | stack.push({'foo': 23}) |
150 | 150 | ident = 0 |
151 | assert l.foo == 42 | |
151 | assert ns.foo == 42 | |
152 | 152 | assert stack.top['foo'] == 42 |
153 | 153 | stack.pop() |
154 | 154 | assert stack.top is None |
155 | 155 | ident = 1 |
156 | assert l.foo == 23 | |
156 | assert ns.foo == 23 | |
157 | 157 | assert stack.top['foo'] == 23 |
158 | 158 | stack.pop() |
159 | 159 | assert stack.top is None |
198 | 198 | partial_proxy = local.LocalProxy(partial_lookup_func) |
199 | 199 | assert partial_proxy.__wrapped__ == partial_lookup_func |
200 | 200 | |
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__) |
9 | 9 | """ |
10 | 10 | import os |
11 | 11 | import ssl |
12 | import sys | |
12 | 13 | import subprocess |
13 | 14 | import textwrap |
14 | 15 | |
277 | 278 | 'content_type': 'application/json' |
278 | 279 | }) |
279 | 280 | 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() |
168 | 168 | assert b.form.getlist('foo') == ['bar1', 'bar2'] |
169 | 169 | |
170 | 170 | 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: | |
174 | 174 | assert isinstance(obj, FileStorage) |
175 | 175 | |
176 | 176 | b = EnvironBuilder(data={'foo': BytesIO()}) |
294 | 294 | assert utils.secure_filename('../../../etc/passwd') == 'etc_passwd' |
295 | 295 | assert utils.secure_filename(u'i contain cool \xfcml\xe4uts.txt') == \ |
296 | 296 | 'i_contain_cool_umlauts.txt' |
297 | assert utils.secure_filename('__filename__') == 'filename' | |
298 | assert utils.secure_filename('foo$&^*)bar') == 'foobar' |
7 | 7 | :copyright: (c) 2014 by Armin Ronacher. |
8 | 8 | :license: BSD, see LICENSE for more details. |
9 | 9 | """ |
10 | import contextlib | |
10 | 11 | import os |
11 | 12 | |
12 | 13 | import pytest |
13 | 14 | |
14 | 15 | import pickle |
15 | 16 | from io import BytesIO |
16 | from datetime import datetime | |
17 | from datetime import datetime, timedelta | |
17 | 18 | from werkzeug._compat import iteritems |
18 | 19 | |
19 | 20 | from tests import strict_eq |
291 | 292 | response.status = 'wtf' |
292 | 293 | strict_eq(response.status_code, 0) |
293 | 294 | 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) | |
294 | 304 | |
295 | 305 | |
296 | 306 | def test_type_forcing(): |
686 | 696 | response.content_length = '42' |
687 | 697 | assert response.content_length == 42 |
688 | 698 | |
689 | for attr in 'date', 'age', 'expires': | |
699 | for attr in 'date', 'expires': | |
690 | 700 | assert getattr(response, attr) is None |
691 | 701 | setattr(response, attr, now) |
692 | 702 | 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) | |
693 | 710 | |
694 | 711 | assert response.retry_after is None |
695 | 712 | response.retry_after = now |
858 | 875 | assert resp.headers['content-length'] == '6' |
859 | 876 | |
860 | 877 | |
878 | def test_response_content_length_uses_encode(): | |
879 | r = wrappers.Response(u'你好') | |
880 | assert r.calculate_content_length() == 6 | |
881 | ||
882 | ||
861 | 883 | def test_other_method_payload(): |
862 | 884 | data = b'Hello World' |
863 | 885 | req = wrappers.Request.from_values(input_stream=BytesIO(data), |
1087 | 1109 | assert req.method == 'GET' |
1088 | 1110 | |
1089 | 1111 | |
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 | ||
1090 | 1141 | class TestSetCookie(object): |
1091 | 1142 | """Tests for :meth:`werkzeug.wrappers.BaseResponse.set_cookie`.""" |
1092 | 1143 |
7 | 7 | :copyright: (c) 2014 by Armin Ronacher. |
8 | 8 | :license: BSD, see LICENSE for more details. |
9 | 9 | """ |
10 | import io | |
11 | import json | |
10 | 12 | import os |
13 | from contextlib import closing | |
14 | from os import path | |
11 | 15 | |
12 | 16 | import pytest |
13 | 17 | |
14 | from os import path | |
15 | from contextlib import closing | |
16 | ||
17 | 18 | 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 | |
20 | 22 | from werkzeug.exceptions import BadRequest, ClientDisconnected |
21 | 23 | 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 | |
25 | 25 | from werkzeug.wsgi import _RangeWrapper, wrap_file |
26 | 26 | |
27 | 27 | |
39 | 39 | with open(path.join(test_dir, to_native(u'äöü', 'utf-8')), 'w') as test_file: |
40 | 40 | test_file.write(u'FOUND') |
41 | 41 | |
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')) | |
52 | 59 | 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' | |
66 | 67 | |
67 | 68 | |
68 | 69 | def test_dispatchermiddleware(): |
241 | 242 | io = StringIO(u'123\n456\n') |
242 | 243 | stream = wsgi.LimitedStream(io, 8) |
243 | 244 | 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'} | |
244 | 254 | |
245 | 255 | |
246 | 256 | def test_limited_stream_disconnection(): |
0 | 0 | [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 | |
2 | 8 | |
3 | 9 | [testenv] |
4 | 10 | 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 | ||
15 | 31 | uwsgi: uwsgi |
16 | stylecheck: flake8 | |
17 | 32 | |
18 | whitelist_externals= | |
33 | whitelist_externals = | |
19 | 34 | redis-server |
20 | 35 | memcached |
21 | 36 | uwsgi |
22 | 37 | |
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 |
18 | 18 | |
19 | 19 | from werkzeug._compat import iteritems |
20 | 20 | |
21 | __version__ = '0.12.2-dev' | |
21 | __version__ = '0.13' | |
22 | 22 | |
23 | 23 | |
24 | 24 | # This import magic raises concerns quite often which is why the implementation |
97 | 97 | } |
98 | 98 | |
99 | 99 | # modules that should be imported when accessed as attributes of werkzeug |
100 | attribute_modules = frozenset(['exceptions', 'routing', 'script']) | |
100 | attribute_modules = frozenset(['exceptions', 'routing']) | |
101 | 101 | |
102 | 102 | |
103 | 103 | object_origins = {} |
123 | 123 | def __dir__(self): |
124 | 124 | """Just show what we want to show.""" |
125 | 125 | result = list(new_module.__all__) |
126 | result.extend(('__file__', '__path__', '__doc__', '__all__', | |
126 | result.extend(('__file__', '__doc__', '__all__', | |
127 | 127 | '__docformat__', '__name__', '__path__', |
128 | 128 | '__package__', '__version__')) |
129 | 129 | return result |
125 | 125 | |
126 | 126 | def get_many(self, *keys): |
127 | 127 | """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:: | |
129 | 129 | |
130 | 130 | foo, bar = cache.get_many("foo", "bar") |
131 | 131 | |
134 | 134 | :param keys: The function accepts multiple keys as positional |
135 | 135 | arguments. |
136 | 136 | """ |
137 | return map(self.get, keys) | |
137 | return [self.get(k) for k in keys] | |
138 | 138 | |
139 | 139 | def get_dict(self, *keys): |
140 | 140 | """Like :meth:`get_many` but return a dict:: |
354 | 354 | - ``pylibmc`` |
355 | 355 | - ``google.appengine.api.memcached`` |
356 | 356 | - ``memcached`` |
357 | - ``libmc`` | |
357 | 358 | |
358 | 359 | Implementation notes: This cache backend works around some limitations in |
359 | 360 | memcached to simplify the interface. For example unicode keys are encoded |
506 | 507 | else: |
507 | 508 | return memcache.Client(servers) |
508 | 509 | |
510 | try: | |
511 | import libmc | |
512 | except ImportError: | |
513 | pass | |
514 | else: | |
515 | return libmc.Client(servers) | |
516 | ||
509 | 517 | |
510 | 518 | # backwards compatibility |
511 | 519 | GAEMemcachedCache = MemcachedCache |
171 | 171 | elif 100 <= status_code < 200 or status_code == 204: |
172 | 172 | if content_length != 0: |
173 | 173 | warn(HTTPWarning('%r responses must have an empty ' |
174 | 'content length') % status_code) | |
174 | 'content length' % status_code)) | |
175 | 175 | if bytes_sent: |
176 | 176 | warn(HTTPWarning('%r responses must not have a body' % |
177 | 177 | status_code)) |
93 | 93 | from time import time |
94 | 94 | from hashlib import sha1 as _default_hash |
95 | 95 | |
96 | from werkzeug._compat import iteritems, text_type | |
96 | from werkzeug._compat import iteritems, text_type, to_bytes | |
97 | 97 | from werkzeug.urls import url_quote_plus, url_unquote_plus |
98 | 98 | from werkzeug._internal import _date_to_unix |
99 | 99 | from werkzeug.contrib.sessions import ModificationTrackingDict |
151 | 151 | # explicitly convert it into a bytestring because python 2.6 |
152 | 152 | # no longer performs an implicit string conversion on hmac |
153 | 153 | if secret_key is not None: |
154 | secret_key = bytes(secret_key) | |
154 | secret_key = to_bytes(secret_key, 'utf-8') | |
155 | 155 | self.secret_key = secret_key |
156 | 156 | self.new = new |
157 | 157 |
298 | 298 | """ |
299 | 299 | try: |
300 | 300 | rv = self[key] |
301 | if type is not None: | |
301 | except KeyError: | |
302 | return default | |
303 | if type is not None: | |
304 | try: | |
302 | 305 | rv = type(rv) |
303 | except (KeyError, ValueError): | |
304 | rv = default | |
306 | except ValueError: | |
307 | rv = default | |
305 | 308 | return rv |
306 | 309 | |
307 | 310 | |
2453 | 2456 | The opaque header from the server returned unchanged by the client. |
2454 | 2457 | It is recommended that this string be base64 or hexadecimal data. |
2455 | 2458 | 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.''') | |
2467 | 2463 | |
2468 | 2464 | |
2469 | 2465 | class WWWAuthenticate(UpdateDictMixin, dict): |
10 | 10 | """ |
11 | 11 | import re |
12 | 12 | import codecs |
13 | from io import BytesIO | |
14 | from tempfile import TemporaryFile | |
13 | from tempfile import SpooledTemporaryFile | |
15 | 14 | from itertools import chain, repeat, tee |
16 | 15 | from functools import update_wrapper |
17 | 16 | |
37 | 36 | def default_stream_factory(total_content_length, filename, content_type, |
38 | 37 | content_length=None): |
39 | 38 | """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+') | |
43 | 40 | |
44 | 41 | |
45 | 42 | def parse_form_data(environ, stream_factory=None, charset='utf-8', |
16 | 16 | :license: BSD, see LICENSE for more details. |
17 | 17 | """ |
18 | 18 | import re |
19 | import warnings | |
19 | 20 | from time import time, gmtime |
20 | 21 | try: |
21 | 22 | from email.utils import parsedate_tz |
60 | 61 | '^_`abcdefghijklmnopqrstuvwxyz|~') |
61 | 62 | _etag_re = re.compile(r'([Ww]/)?(?:"(.*?)"|(.*?))(?:\s*,\s*|$)') |
62 | 63 | _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) | |
69 | 88 | _option_header_start_mime_type = re.compile(r',\s*([^;,\s]+)([;,]\s*.+)?') |
70 | 89 | |
71 | 90 | _entity_headers = frozenset([ |
780 | 799 | return _dump_date(timestamp, ' ') |
781 | 800 | |
782 | 801 | |
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 | ||
783 | 845 | def is_resource_modified(environ, etag=None, data=None, last_modified=None, |
784 | 846 | ignore_if_range=True): |
785 | 847 | """Convenience method for conditional requests. |
936 | 998 | |
937 | 999 | def dump_cookie(key, value='', max_age=None, expires=None, path='/', |
938 | 1000 | domain=None, secure=False, httponly=False, |
939 | charset='utf-8', sync_expires=True): | |
1001 | charset='utf-8', sync_expires=True, max_size=4093): | |
940 | 1002 | """Creates a new Set-Cookie header without the ``Set-Cookie`` prefix |
941 | 1003 | The parameters are the same as in the cookie Morsel object in the |
942 | 1004 | Python standard library but it accepts unicode data, too. |
972 | 1034 | :param charset: the encoding for unicode values. |
973 | 1035 | :param sync_expires: automatically set expires if max_age is defined |
974 | 1036 | 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/ | |
975 | 1042 | """ |
976 | 1043 | key = to_bytes(key, charset) |
977 | 1044 | value = to_bytes(value, charset) |
1020 | 1087 | rv = b'; '.join(buf) |
1021 | 1088 | if not PY2: |
1022 | 1089 | 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 | ||
1023 | 1112 | return rv |
1024 | 1113 | |
1025 | 1114 |
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 |
252 | 252 | cannot be done, this function returns ``None``. |
253 | 253 | |
254 | 254 | :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. | |
256 | 256 | """ |
257 | 257 | parts = [directory] |
258 | 258 | for filename in pathnames: |
27 | 27 | You can also pass it a `extra_files` keyword argument with a list of |
28 | 28 | additional files (like configuration files) you want to observe. |
29 | 29 | |
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. | |
32 | 32 | |
33 | 33 | |
34 | 34 | :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. |
36 | 36 | """ |
37 | 37 | from __future__ import with_statement |
38 | 38 | |
39 | import io | |
39 | 40 | import os |
40 | 41 | import socket |
41 | 42 | import sys |
94 | 95 | |
95 | 96 | LISTEN_QUEUE = 128 |
96 | 97 | 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 | |
97 | 151 | |
98 | 152 | |
99 | 153 | class WSGIRequestHandler(BaseHTTPRequestHandler, object): |
139 | 193 | if key not in ('CONTENT_TYPE', 'CONTENT_LENGTH'): |
140 | 194 | key = 'HTTP_' + key |
141 | 195 | 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']) | |
142 | 200 | |
143 | 201 | if request_url.scheme and request_url.netloc: |
144 | 202 | environ['HTTP_HOST'] = request_url.netloc |
295 | 353 | |
296 | 354 | if code[0] == '1': # 1xx - Informational |
297 | 355 | msg = color(msg, attrs=['bold']) |
298 | if code[0] == '2': # 2xx - Success | |
356 | elif code[0] == '2': # 2xx - Success | |
299 | 357 | msg = color(msg, color='white') |
300 | 358 | elif code == '304': # 304 - Resource Not Modified |
301 | 359 | msg = color(msg, color='cyan') |
341 | 399 | |
342 | 400 | subject = cert.get_subject() |
343 | 401 | subject.CN = cn |
344 | subject.O = 'Dummy Certificate' | |
402 | subject.O = 'Dummy Certificate' # noqa: E741 | |
345 | 403 | |
346 | 404 | issuer = cert.get_issuer() |
347 | 405 | issuer.CN = 'Untrusted Authority' |
348 | issuer.O = 'Self-Signed' | |
406 | issuer.O = 'Self-Signed' # noqa: E741 | |
349 | 407 | |
350 | 408 | pkey = crypto.PKey() |
351 | 409 | pkey.generate_key(crypto.TYPE_RSA, 2048) |
657 | 715 | with a different |
658 | 716 | :class:`~BaseHTTPServer.BaseHTTPRequestHandler` |
659 | 717 | 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 | |
662 | 720 | just wrapping the application in that middleware before |
663 | 721 | serving. |
664 | 722 | :param passthrough_errors: set this to `True` to disable the error catching. |
670 | 728 | the server should automatically create one, or ``None`` |
671 | 729 | to disable SSL (which is the default). |
672 | 730 | """ |
731 | if not isinstance(port, int): | |
732 | raise TypeError('port must be an integer') | |
673 | 733 | if use_debugger: |
674 | 734 | from werkzeug.debug import DebuggedApplication |
675 | 735 | application = DebuggedApplication(application, use_evalex) |
37 | 37 | ((a + b).encode(), int(a + b, 16)) |
38 | 38 | for a in _hexdigits for b in _hexdigits |
39 | 39 | ) |
40 | _bytetohex = [ | |
41 | ('%%%02X' % char).encode('ascii') for char in range(256) | |
42 | ] | |
40 | 43 | |
41 | 44 | |
42 | 45 | _URLTuple = fix_tuple_repr(namedtuple( |
470 | 473 | if char in safe: |
471 | 474 | rv.append(char) |
472 | 475 | else: |
473 | rv.extend(('%%%02X' % char).encode('ascii')) | |
476 | rv.extend(_bytetohex[char]) | |
474 | 477 | return to_native(bytes(rv)) |
475 | 478 | |
476 | 479 |
116 | 116 | - `aix` |
117 | 117 | - `amiga` |
118 | 118 | - `android` |
119 | - `blackberry` | |
119 | 120 | - `bsd` |
120 | 121 | - `chromeos` |
122 | - `dragonflybsd` | |
123 | - `freebsd` | |
121 | 124 | - `hpux` |
125 | - `ipad` | |
122 | 126 | - `iphone` |
123 | - `ipad` | |
124 | 127 | - `irix` |
125 | 128 | - `linux` |
126 | 129 | - `macos` |
130 | - `netbsd` | |
131 | - `openbsd` | |
127 | 132 | - `sco` |
128 | 133 | - `solaris` |
134 | - `symbian` | |
129 | 135 | - `wii` |
130 | 136 | - `windows` |
131 | 137 | |
136 | 142 | |
137 | 143 | - `aol` * |
138 | 144 | - `ask` * |
145 | - `baidu` * | |
146 | - `bing` * | |
139 | 147 | - `camino` |
140 | 148 | - `chrome` |
141 | 149 | - `firefox` |
145 | 153 | - `konqueror` |
146 | 154 | - `links` |
147 | 155 | - `lynx` |
156 | - `mozilla` | |
148 | 157 | - `msie` |
149 | 158 | - `msn` |
150 | 159 | - `netscape` |
452 | 452 | also recursively list modules but in that case it will import all the |
453 | 453 | packages to get the correct load path of that module. |
454 | 454 | |
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. | |
456 | 456 | :param include_packages: set to `True` if packages should be returned, too. |
457 | 457 | :param recursive: set to `True` if recursion should happen. |
458 | 458 | :return: generator |
21 | 21 | """ |
22 | 22 | from functools import update_wrapper |
23 | 23 | from datetime import datetime, timedelta |
24 | from warnings import warn | |
24 | 25 | |
25 | 26 | from werkzeug.http import HTTP_STATUS_CODES, \ |
26 | 27 | parse_accept_header, parse_cache_control_header, parse_etags, \ |
29 | 30 | parse_www_authenticate_header, remove_entity_headers, \ |
30 | 31 | parse_options_header, dump_options_header, http_date, \ |
31 | 32 | 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 | |
33 | 35 | from werkzeug.urls import url_decode, iri_to_uri, url_join |
34 | 36 | from werkzeug.formparser import FormDataParser, default_stream_factory |
35 | 37 | from werkzeug.utils import cached_property, environ_property, \ |
61 | 63 | to the WSGI server is not a string. |
62 | 64 | """ |
63 | 65 | if isinstance(iterable, string_types): |
64 | from warnings import warn | |
65 | 66 | warn(Warning('response iterable was set to a string. This appears ' |
66 | 67 | 'to work but means that the server will send the ' |
67 | 68 | 'data to the client char, by char. This is almost ' |
321 | 322 | not provided because webbrowsers do not provide |
322 | 323 | this value. |
323 | 324 | """ |
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) | |
326 | 330 | |
327 | 331 | @property |
328 | 332 | def want_form_data_parsed(self): |
334 | 338 | return bool(self.environ.get('CONTENT_TYPE')) |
335 | 339 | |
336 | 340 | def make_form_data_parser(self): |
337 | """Creates the form data parser. Instanciates the | |
341 | """Creates the form data parser. Instantiates the | |
338 | 342 | :attr:`form_data_parser_class` with some parameters. |
339 | 343 | |
340 | 344 | .. versionadded:: 0.8 |
668 | 672 | |
669 | 673 | .. versionadded:: 0.7''') |
670 | 674 | |
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`` | |
675 | 679 | 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 | ||
677 | 693 | is_secure = property(lambda x: x.environ['wsgi.url_scheme'] == 'https', |
678 | 694 | doc='`True` if the request is secure.') |
679 | 695 | is_multithread = environ_property('wsgi.multithread', doc=''' |
789 | 805 | #: |
790 | 806 | #: .. versionadded:: 0.8 |
791 | 807 | 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 | |
792 | 818 | |
793 | 819 | def __init__(self, response=None, status=None, headers=None, |
794 | 820 | mimetype=None, content_type=None, direct_passthrough=False): |
918 | 944 | return self._status |
919 | 945 | |
920 | 946 | 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 | ||
922 | 952 | try: |
923 | 953 | self._status_code = int(self._status.split(None, 1)[0]) |
924 | 954 | except ValueError: |
925 | 955 | self._status_code = 0 |
926 | 956 | self._status = '0 %s' % self._status |
957 | except IndexError: | |
958 | raise ValueError('Empty status argument') | |
927 | 959 | status = property(_get_status, _set_status, doc='The HTTP Status code') |
928 | 960 | del _get_status, _set_status |
929 | 961 | |
974 | 1006 | self._ensure_sequence() |
975 | 1007 | except RuntimeError: |
976 | 1008 | return None |
977 | return sum(len(x) for x in self.response) | |
1009 | return sum(len(x) for x in self.iter_encoded()) | |
978 | 1010 | |
979 | 1011 | def _ensure_sequence(self, mutable=False): |
980 | 1012 | """This method can be called by methods that need a sequence. If |
1033 | 1065 | path='/', domain=None, secure=False, httponly=False): |
1034 | 1066 | """Sets a cookie. The parameters are the same as in the cookie `Morsel` |
1035 | 1067 | 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. | |
1036 | 1071 | |
1037 | 1072 | :param key: the key (name) of the cookie to be set. |
1038 | 1073 | :param value: the value of the cookie. |
1052 | 1087 | extension to the cookie standard and probably not |
1053 | 1088 | supported by all browsers. |
1054 | 1089 | """ |
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 | )) | |
1064 | 1102 | |
1065 | 1103 | def delete_cookie(self, key, path='/', domain=None): |
1066 | 1104 | """Delete a cookie. Fails silently if key doesn't exist. |
1646 | 1684 | self.response._ensure_sequence(mutable=True) |
1647 | 1685 | self.response.response.append(value) |
1648 | 1686 | self.response.headers.pop('Content-Length', None) |
1687 | return len(value) | |
1649 | 1688 | |
1650 | 1689 | def writelines(self, seq): |
1651 | 1690 | for item in seq: |
1662 | 1701 | if self.closed: |
1663 | 1702 | raise ValueError('I/O operation on closed file') |
1664 | 1703 | return False |
1704 | ||
1705 | def tell(self): | |
1706 | self.response._ensure_sequence() | |
1707 | return sum(map(len, self.response.response)) | |
1665 | 1708 | |
1666 | 1709 | @property |
1667 | 1710 | def encoding(self): |
1805 | 1848 | The Location response-header field is used to redirect the recipient |
1806 | 1849 | to a location other than the Request-URI for completion of the request |
1807 | 1850 | 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=''' | |
1809 | 1852 | The Age response-header field conveys the sender's estimate of the |
1810 | 1853 | amount of time since the response (or its revalidation) was |
1811 | 1854 | generated at the origin server. |
7 | 7 | :copyright: (c) 2014 by the Werkzeug Team, see AUTHORS for more details. |
8 | 8 | :license: BSD, see LICENSE for more details. |
9 | 9 | """ |
10 | import re | |
10 | import io | |
11 | import mimetypes | |
11 | 12 | import os |
12 | 13 | import posixpath |
13 | import mimetypes | |
14 | from itertools import chain | |
15 | from zlib import adler32 | |
16 | from time import time, mktime | |
14 | import re | |
17 | 15 | from datetime import datetime |
18 | 16 | 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 | |
23 | 24 | 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 | |
26 | 25 | 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 | |
27 | 28 | |
28 | 29 | |
29 | 30 | def responder(f): |
164 | 165 | |
165 | 166 | def get_content_length(environ): |
166 | 167 | """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. | |
168 | 170 | |
169 | 171 | .. versionadded:: 0.9 |
170 | 172 | |
171 | 173 | :param environ: the WSGI environ to fetch the content length from. |
172 | 174 | """ |
175 | if environ.get('HTTP_TRANSFER_ENCODING', '') == 'chunked': | |
176 | return None | |
177 | ||
173 | 178 | content_length = environ.get('CONTENT_LENGTH') |
174 | 179 | if content_length is not None: |
175 | 180 | try: |
180 | 185 | |
181 | 186 | def get_input_stream(environ, safe_fallback=True): |
182 | 187 | """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 | |
184 | 189 | raw WSGI stream in most cases but one that is safe to read from |
185 | 190 | without taking into account the content length. |
186 | 191 | |
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 | ||
187 | 196 | .. versionadded:: 0.9 |
188 | 197 | |
189 | 198 | :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. | |
194 | 202 | """ |
195 | 203 | stream = environ['wsgi.input'] |
196 | 204 | content_length = get_content_length(environ) |
201 | 209 | if environ.get('wsgi.input_terminated'): |
202 | 210 | return stream |
203 | 211 | |
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. | |
208 | 215 | if content_length is None: |
209 | 216 | return safe_fallback and _empty_stream or stream |
210 | 217 | |
480 | 487 | |
481 | 488 | :param app: the application to wrap. If you don't want to wrap an |
482 | 489 | 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. | |
484 | 491 | :param disallow: a list of :func:`~fnmatch.fnmatch` rules. |
485 | 492 | :param fallback_mimetype: the fallback mimetype for unknown files. |
486 | 493 | :param cache: enable or disable caching headers. |
490 | 497 | def __init__(self, app, exports, disallow=None, cache=True, |
491 | 498 | cache_timeout=60 * 60 * 12, fallback_mimetype='text/plain'): |
492 | 499 | self.app = app |
493 | self.exports = {} | |
500 | self.exports = [] | |
494 | 501 | self.cache = cache |
495 | 502 | 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: | |
497 | 506 | if isinstance(value, tuple): |
498 | 507 | loader = self.get_package_loader(*value) |
499 | 508 | elif isinstance(value, string_types): |
503 | 512 | loader = self.get_directory_loader(value) |
504 | 513 | else: |
505 | 514 | raise TypeError('unknown def %r' % value) |
506 | self.exports[key] = loader | |
515 | self.exports.append((key, loader)) | |
507 | 516 | if disallow is not None: |
508 | 517 | from fnmatch import fnmatch |
509 | 518 | self.is_allowed = lambda x: not fnmatch(x, disallow) |
584 | 593 | path = '/' + '/'.join(x for x in cleaned_path.split('/') |
585 | 594 | if x and x != '..') |
586 | 595 | file_loader = None |
587 | for search_path, loader in iteritems(self.exports): | |
596 | for search_path, loader in self.exports: | |
588 | 597 | if search_path == path: |
589 | 598 | real_filename, file_loader = loader(None) |
590 | 599 | if file_loader is not None: |
1040 | 1049 | |
1041 | 1050 | |
1042 | 1051 | @implements_iterator |
1043 | class LimitedStream(object): | |
1052 | class LimitedStream(io.IOBase): | |
1044 | 1053 | |
1045 | 1054 | """Wraps a stream so that it doesn't read more than n bytes. If the |
1046 | 1055 | stream is exhausted and the caller tries to get more bytes from it |
1192 | 1201 | if not line: |
1193 | 1202 | raise StopIteration() |
1194 | 1203 | return line |
1204 | ||
1205 | def readable(self): | |
1206 | return True |