Repository: pallets/werkzeug

Issue:
fallback for platform who don't have SpooledTemporaryFile
On AppEngine SpooledTemporaryFile is not supported, this PR will fallback to the old code when SpooledTemporaryFile is not supported on the platform (As discussed on #1210)


Discussion:
Confirmed this fixes my particular case in GoogleCloudPlatform/python-docs-samples#1266
```
$ dev_appserver.py .                                           
INFO     2017-12-27 15:50:16,680 devappserver2.py:105] Skipping SDK update check.
INFO     2017-12-27 15:50:16,710 api_server.py:300] Starting API server at: http://localhost:37081
INFO     2017-12-27 15:50:16,742 dispatcher.py:251] Starting module "default" running at: http://localhost:8080
INFO     2017-12-27 15:50:16,742 admin_server.py:116] Starting admin server at: http://localhost:8000
ERROR    2017-12-27 15:50:19,038 wsgi.py:263] 
Traceback (most recent call last):
...
  File ".../test/lib/werkzeug/formparser.py", line 14, in <module>
    from tempfile import SpooledTemporaryFile
ImportError: cannot import name SpooledTemporaryFile
INFO     2017-12-27 15:50:19,045 module.py:821] default: "GET / HTTP/1.1" 500 -
$ pip install --upgrade -t lib git+https://github.com/erlichmen/werkzeug.git 
Collecting git+https://github.com/erlichmen/werkzeug.git
  Cloning https://github.com/erlichmen/werkzeug.git to /tmp/pip-cggmbu-build
Installing collected packages: Werkzeug
  Running setup.py install for Werkzeug ... done
Successfully installed Werkzeug-0.14.dev0
$ dev_appserver.py .                                                         
INFO     2017-12-27 15:51:01,358 devappserver2.py:105] Skipping SDK update check.
INFO     2017-12-27 15:51:01,390 api_server.py:300] Starting API server at: http://localhost:44439
INFO     2017-12-27 15:51:01,424 dispatcher.py:251] Starting module "default" running at: http://localhost:8080
INFO     2017-12-27 15:51:01,425 admin_server.py:116] Starting admin server at: http://localhost:8000
INFO     2017-12-27 15:51:04,695 module.py:821] default: "GET / HTTP/1.1" 200 2
```

Resolving patch:
diff --git a/werkzeug/formparser.py b/werkzeug/formparser.py
--- a/werkzeug/formparser.py
+++ b/werkzeug/formparser.py
@@ -11,7 +11,15 @@
 """
 import re
 import codecs
-from tempfile import SpooledTemporaryFile
+
+# there are some platforms where SpooledTemporaryFile is not available.
+# In that case we need to provide a fallback.
+try:
+    from tempfile import SpooledTemporaryFile
+except ImportError:
+    from tempfile import TemporaryFile
+    SpooledTemporaryFile = None
+
 from itertools import chain, repeat, tee
 from functools import update_wrapper
 
@@ -37,7 +45,12 @@
 def default_stream_factory(total_content_length, filename, content_type,
                            content_length=None):
     """The stream factory that is used per default."""
-    return SpooledTemporaryFile(max_size=1024 * 500, mode='wb+')
+    max_size = 1024 * 500
+    if SpooledTemporaryFile is not None:
+        return SpooledTemporaryFile(max_size=max_size, mode='wb+')
+    if total_content_length is None or total_content_length > max_size:
+        return TemporaryFile('wb+')
+    return BytesIO()
 
 
 def parse_form_data(environ, stream_factory=None, charset='utf-8',


Test patch: