From 2e022cb2727cfa99355261f2c18d1e0fc56e82ee Mon Sep 17 00:00:00 2001 From: Armin Ronacher Date: Fri, 5 Aug 2011 16:43:42 +0200 Subject: [PATCH] Added debughelpers. Flask will now tell you if you forget enctype --- CHANGES | 2 ++ flask/debughelpers.py | 52 +++++++++++++++++++++++++++++++++++++++++++ flask/wrappers.py | 11 +++++++++ 3 files changed, 65 insertions(+) create mode 100644 flask/debughelpers.py diff --git a/CHANGES b/CHANGES index ae9cbdb9..b7be2c52 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,8 @@ Relase date to be decided, codename to be chosen. OPTIONS implementation. - HTTP exceptions and Bad Request Key Errors can now be trapped so that they show up normally in the traceback. +- Flask in debug mode is now detecting some common problems and tries to + warn you about them. Version 0.7.3 ------------- diff --git a/flask/debughelpers.py b/flask/debughelpers.py new file mode 100644 index 00000000..dd1fffe9 --- /dev/null +++ b/flask/debughelpers.py @@ -0,0 +1,52 @@ +# -*- coding: utf-8 -*- +""" + flask.debughelpers + ~~~~~~~~~~~~~~~~~~ + + Various helpers to make the development experience better. + + :copyright: (c) 2011 by Armin Ronacher. + :license: BSD, see LICENSE for more details. +""" + + +class DebugFilesKeyError(KeyError, AssertionError): + """Raised from request.files during debugging. The idea is that it can + provide a better error message than just a generic KeyError/BadRequest. + """ + + def __init__(self, request, key): + form_matches = request.form.getlist(key) + buf = ['You tried to access the file "%s" in the request.files ' + 'dictionary but it does not exist. The mimetype for the request ' + 'is "%s" instead of "multipart/form-data" which means that no ' + 'files were transmitted. To fix this error you most likely have ' + 'to provide enctype="multipart/form-data" in your form.' % + (key, request.mimetype)] + if form_matches: + buf.append('\n\nThe browser instead most likely submitted the ' + 'filenames in the form. This was submitted: %s' % + ', '.join('"%s"' % x for x in form_matches)) + self.msg = ''.join(buf) + + def __str__(self): + return self.msg + + +def make_enctype_error_multidict(request): + """Since Flask 0.8 we're monkeypatching the files object in case a + request is detected that does not use multipart form data but the files + object is accessed. + """ + oldcls = request.files.__class__ + class newcls(oldcls): + def __getitem__(self, key): + try: + return oldcls.__getitem__(self, key) + except KeyError, e: + if key not in request.form: + raise + raise DebugFilesKeyError(request, key) + newcls.__name__ = oldcls.__name__ + newcls.__module__ = oldcls.__module__ + request.files.__class__ = newcls diff --git a/flask/wrappers.py b/flask/wrappers.py index 57c62ce1..06f60beb 100644 --- a/flask/wrappers.py +++ b/flask/wrappers.py @@ -12,6 +12,7 @@ from werkzeug.wrappers import Request as RequestBase, Response as ResponseBase from werkzeug.utils import cached_property +from .debughelpers import make_enctype_error_multidict from .helpers import json, _assert_have_json from .globals import _request_ctx_stack @@ -99,6 +100,16 @@ class Request(RequestBase): return json.loads(self.data, encoding=request_charset) return json.loads(self.data) + def _load_form_data(self): + RequestBase._load_form_data(self) + + # in debug mode we're replacing the files multidict with an ad-hoc + # subclass that raises a different error for key errors. + ctx = _request_ctx_stack.top + if ctx is not None and ctx.app.debug and \ + self.mimetype != 'multipart/form-data': + make_enctype_error_multidict(self) + class Response(ResponseBase): """The response object that is used by default in Flask. Works like the