AppEngine/Python: Why isn't the exception caught? - python

I'm trying to write a Google-Appengine app that will fail nicely when datastore writes are disabled
Currently my main() looks like this:
def main():
make_datastore_readonly()
try:
run_wsgi_app(application)
except CapabilityDisabledError:
run_wsgi_app(NoWrite)
If I set main to:
def main():
run_wsgi_app(application)
My app displays a traceback when the exception is raised.
If I set main to:
def main():
run_wsgi_app(NoWrite)
It will properly show my error message (although for every request).
Getting back to my modified version of main, this one:
def main():
make_datastore_readonly()
try:
run_wsgi_app(application)
except CapabilityDisabledError:
run_wsgi_app(NoWrite)
Instead of getting my error message, I still get a traceback that looks like this:
Traceback (most recent call last):
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/webapp/_webapp25.py", line 703, in __call__
handler.post(*groups)
File "/Users/kevin/Sche/main.py", line 232, in post
me.put();
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/db/__init__.py", line 1074, in put
return datastore.Put(self._entity, **kwargs)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/datastore.py", line 579, in Put
return PutAsync(entities, **kwargs).get_result()
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/datastore.py", line 556, in PutAsync
return _GetConnection().async_put(config, entities, local_extra_hook)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/datastore/datastore_rpc.py", line 1553, in async_put
return make_put_call(base_req, pbs, extra_hook)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/datastore/datastore_rpc.py", line 1543, in make_put_call
self.__put_hook, user_data)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/datastore/datastore_rpc.py", line 1188, in make_rpc_call
rpc.make_call(method, request, response, get_result_hook, user_data)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 519, in make_call
self.__service, method, request, response, self.__rpc)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/api/apiproxy_stub_map.py", line 207, in Call
function(service, call, request, response)
File "/Users/kevin/Sche/main.py", line 18, in hook
raise CapabilityDisabledError('Datastore is in read-only mode')
CapabilityDisabledError: Datastore is in read-only mode
So, my question is, why isn't the exception caught?
Edit:
This function is from this StackOverflow answer
def make_datastore_readonly():
"""Throw ReadOnlyError on put and delete operations."""
def hook(service, call, request, response):
assert(service == 'datastore_v3')
if call in ('Put', 'Delete'):
raise CapabilityDisabledError('Datastore is in read-only mode') //Line 18
apiproxy_stub_map.apiproxy.GetPreCallHooks().Push('readonly_datastore', hook, 'datastore_v3')

the main function only register this application. Therefore, the exception will not raise in the main function. Therefore the try ... catch statement won't work.
The way to handle this exception is defining a new RequestHandler. Then, all requests which want to have this feature should inherent from the new RequestHandler.
for example:
Class MyRequestHandler(RequestHandler):
def get(self):
try:
self.get_handler()
except CapabilityDisabledError:
pass
class MyRequest(MyRequestHandler):
def get_handler(self):
# ....
pass

Related

How to debug patched method with unittest.mock

I have the following (simplified) FBV:
def check_existing_contacts(request):
if request.is_ajax and request.method == "GET":
print('Function called')
return mailgun_validate_email(request)
return JsonResponse({"error": "Incorrect AJAX / GET request."}, status=400)
I want to test that the mailgun_validate_email function is called:
class TestCheckExistingContacts(TestCase):
#patch('myapp.mailgun_validate_email')
def test_new_contact(self, mock):
client = Client()
client.get('/check/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertTrue(mock.called)
I am certain the test calls mailgun_validate_email as the print('Function called') displays in the console. However I get an assertion error that the mock.called is False.
Where am I going wrong / how can I debug this?
************ UPDATE *******************
When patching the function in the same module as the view, I get the following error:
class TestCheckExistingContacts(TestCase):
#patch('[path to views.py with check_existing_contacts].mailgun_validate_email')
def test_new_contact(self, mock):
client = Client()
client.get('/check/', HTTP_X_REQUESTED_WITH='XMLHttpRequest')
self.assertTrue(mock.called)
Results in:
Failure
Traceback (most recent call last):
File "\tests\test_utils.py", line 123, in test_new_contact
response = self.client.get('/validate/',
File "\.venv\lib\site-packages\django\test\client.py", line 518, in get
response = super().get(path, data=data, secure=secure, **extra)
File "\.venv\lib\site-packages\django\test\client.py", line 344, in get
return self.generic('GET', path, secure=secure, **{
File "\.venv\lib\site-packages\django\test\client.py", line 421, in generic
return self.request(**r)
File "\.venv\lib\site-packages\django\test\client.py", line 496, in request
raise exc_value
File "\.venv\lib\site-packages\django\core\handlers\exception.py", line 34, in inner
response = get_response(request)
File "\.venv\lib\site-packages\django\utils\deprecation.py", line 96, in __call__
response = self.process_response(request, response)
File "\.venv\lib\site-packages\django\contrib\sessions\middleware.py", line 45, in process_response
patch_vary_headers(response, ('Cookie',))
File "\.venv\lib\site-packages\django\utils\cache.py", line 267, in patch_vary_headers
vary_headers = cc_delim_re.split(response['Vary'])
TypeError: expected string or bytes-like object
If you did from myapp import mailgun_validate_email for check_existing_contacts, then you need to patch the reference in that module instead of myapp.
E.g. if the import is in myapp/views.py, then patch myapp.views.mailgun_validate_email.
The view needs to return an instance of HttpResponse or one of its subclasses, same for mailgun_validate_email since you directly return mailgun_validate_email(...).
# #patch('myapp.mailgun_validate_email') # Change this
#patch('myapp.views.mailgun_validate_email', return_value=JsonResponse({})) # to this

Bottle GET request is broken with certain strings when running with paste server

I have a problem similar to this one:
Python bottle: UTF8 path string invalid when using app.mount()
When I try to GET /languages/Inglês I receive the error below (notice the accent "ê"). When passing [az] strings it works fine.
Critical error while processing request: /languages/Inglês
I tried the fix mentioned on the link above without success.
Working example:
from bottle import route, run, debug
#route('/languages/<name>')
def hello(name):
return name
if __name__ == '__main__':
debug(False)
#run(reloader=False, port = 8080) # works
run(server='paste', port = 8080) # fails
Running with server='paste' causes the crash, but using the Bottle server it runs OK. The problem seems to happen at the bottle._handle() method, where a UnicodeError is thrown (bottle.py line 844):
def _handle(self, environ):
path = environ['bottle.raw_path'] = environ['PATH_INFO']
if py3k:
try:
environ['PATH_INFO'] = path.encode('latin1').decode('utf8')
except UnicodeError:
return HTTPError(400, 'Invalid path string. Expected UTF-8')
I'm using Python 3.6.2, Bottle v0.12.13 and Paste 2.0.3 on a Windows 10 machine. What's going on? Is that a problem with Bottle or Paste?
Note: I've already solved my problem by refactoring all the code to use integer IDs instead of names. But I still would like to learn more about this.
Stack trace:
Critical error while processing request: /hello/inglês
Error:
RuntimeError('Request context not initialized.',)
Traceback:
Traceback (most recent call last):
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 1661, in fget
try: return ls.var
AttributeError: '_thread._local' object has no attribute 'var'
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 954, in wsgi
out = self._cast(self._handle(environ))
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 907, in _cast
out = self.error_handler.get(out.status_code, self.default_error_handler)(out)
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 842, in default_error_handler
return tob(template(ERROR_PAGE_TEMPLATE, e=res))
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 3619, in template
return TEMPLATES[tplid].render(kwargs)
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 3409, in render
self.execute(stdout, env)
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 3396, in execute
eval(self.co, env)
File "<string>", line 17, in <module>
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 1249, in url
return self.urlparts.geturl()
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 165, in __get__
key, storage = self.key, getattr(obj, self.attr)
File "C:\Users\fernando.filho\AppData\Local\Programs\Python\Python36\lib\site-packages\bottle.py", line 1663, in fget
raise RuntimeError("Request context not initialized.")
RuntimeError: Request context not initialized.
Answering my own question, quoting #GrahamDumpleton:
The Paste server is not tolerant of being sent non ASCII characters.
this is the bottle's problem!
In order to use paste server u can change this:
"""bottle.py"""
environ['PATH_INFO'] = path.encode('latin1').decode('utf8', 'ignore')
to:
environ['PATH_INFO'] = path.encode('utf8').decode('utf8', 'ignore') #utf-8 or else
it's work well.

try/except not catching a specific type of exception

I'm trying to catch a specific type of exception that is thrown inside a function call. I enclosed the function call inside a try/except block where the except block catches the specific exception being thrown. I still get a system fail stack trace for that exception, unless I also include a general catch for all exceptions. On including that block and checking the type of the exception being caught, I see that it is catching the type of exception I wanted to catch in the first block. No idea why this is happening.
Context: working on a google app engine app with webapp2 and ndb. The file functions has an init.py that imports all exceptions from exceptions.py
Mock Code and Structure
utils/functions/exceptions.py
"""
Custom exception types
"""
class InvalidParamsException(Exception):
def __init__(self, msg):
self.msg = msg
def __str__(self):
return repr(self.msg)
models/models.py
import os, sys
sys.path.append(os.path.join(os.path.dirname(__file__), ".."))
import utils.functions as func
<-->
class ModelClass(ndb.Model):
#classmethod
def new(cls):
<-->
raise func.InvalidParamsException("Invalid Params to function!")
<-->
routes.py
import utils.functions as func
from models import ModelClass
class ModelClassHandler(webapp2.RequestHandler):
def post(self):
try:
new_model = ModelClass.new()
except func.InvalidParamsException as e:
logging.debug("Caught the right Exception!!")
except Exception as e:
logging.debug(":(")
logging.debug("EXCEPTION TYPE - %s"%str(type(e)))
The output that I get if I don't include that second general except block is:
Traceback (most recent call last):
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1535, in __call__
rv = self.handle_exception(request, response, e)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1529, in __call__
rv = self.router.dispatch(request, response)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1278, in default_dispatcher
return route.handler_adapter(request, response)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 1102, in __call__
return handler.dispatch()
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 572, in dispatch
return self.handle_exception(e, self.app.debug)
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/lib/webapp2-2.5.2/webapp2.py", line 570, in dispatch
return method(*args, **kwargs)
File "{{my_path}}/routes.py", line 58, in post
new_model = ModelClass.new()
File "{{my_path}}/models/models.py", line 559, in new
raise func.InvalidParamsException("Invalid Params to function!")
InvalidParamsException: 'Invalid Params to function!'
If I do include that second block, I pass the route/function gracefully, and see this in the logs:
DEBUG 2016-03-25 01:01:03,221 routes.py:66] EXCEPTION TYPE - <class 'utils.functions.exceptions.InvalidParamsException'>
Help/Guidance much appreciated!!
It appears that Python raises exceptions as imported in the current namespace. My best evicende for that is the fact that the last line of the traceback calls the raised exception "InvalidParamsException" and not "somemodule.InvalidParamsException".
Thereby, I would suggest resolving the namespace conflicts importing explicitly the exception into the namespace of "routes.py":
from utils.functions.exceptions import InvalidParamsException
and catching the exception by its now-resolved namespace name:
except InvalidParamsException as inv_param_exp:
<...>

Override send_static_file() in Flask

I'm trying use this answer to my problem, but I don't know how to it use correctly.
My code:
app = Flask(name)
app = Flask(__name__, static_folder="static", static_path="")
class SecuredStaticFlask(app):
def send_static_file(self, filename):
# Get user from session
if current_user.is_authenticated():
return super(SecuredStaticFlask, self).send_static_file(filename)
else:
abort(403)
When I visit http://localhost:5000/some_existing_file in the browser, Flask give me an error:
Traceback (most recent call last):
File "/home/honza/Stažené/pycharm-community-3.0/helpers/pydev/pydevd.py", line 1498, in <module>
debugger.run(setup['file'], None, None)
File "/home/honza/Stažené/pycharm-community-3.0/helpers/pydev/pydevd.py", line 1139, in run
pydev_imports.execfile(file, globals, locals) #execute the script
File "/home/honza/workspace/web_development/login/toneid_web_api_jk.py", line 37, in <module>
class SecuredStaticFlask(app):
File "/usr/local/lib/python2.7/dist-packages/flask/app.py", line 507, in __init__
self.add_url_rule(self.static_url_path + '/<path:filename>',
TypeError: Error when calling the metaclass bases
can only concatenate tuple (not "str") to tuple
Instance of flask.Flask is app, right? So, I think subclass of Flask is subclass_name(parent_class).
Can anybody help me? Thanks.
In my opinion it should be in lines of :
class SecuredStaticFlask(Flask):
def send_static_file(self, filename):
# Get user from session
if current_user.is_authenticated():
return super(SecuredStaticFlask, self).send_static_file(filename)
else:
abort(403)
and
app = SecuredStaticFlask(__name__, static_folder="static", static_path="")

Flask-Sijax callbacks are "working outside of request context"

While inside a callback function, I lose the ability to access flask.session, flask.g, or functions such as url_for(). They all throw an error saying that I'm "working outside of request context".
Debugging middleware caught exception in streamed response at a point where response headers were already sent.
Traceback (most recent call last):
File "C:\Python27\site-packages\sijax\response\streaming.py", line 136, in _process_call_chain
for string in generator:
File "C:\Python27\site-packages\sijax\response\streaming.py", line 109, in _process_callback
response = self._perform_handler_call(callback, args)
File "C:\Python27\site-packages\sijax\response\base.py", line 258, in _perform_handler_call
return callback(self, *args)
File "C:\Dropbox\Code\Python 2.7\FlaskTesting\testpage.py", line 18, in myformhandler
sql_session = flask.g.sql_session
File "C:\Python27\lib\site-packages\werkzeug\local.py", line 336, in __getattr__
return getattr(self._get_current_object(), name)
File "C:\Python27\lib\site-packages\werkzeug\local.py", line 295, in _get_current_object
return self.__local()
File "C:\Python27\lib\site-packages\flask\globals.py", line 19, in _lookup_object
raise RuntimeError('working outside of request context')
RuntimeError: working outside of request context
192.168.1.141 - - [20/Jun/2012 16:33:04] "POST /testpage HTTP/1.1" 200 -
I've been unable to find out how to get around this problem. Any help would be appreciated.
Python v2.7
Flask v0.8
Flask-Sijax v0.3
You may have a try with stream_with_context. The code example copied from http://flask.pocoo.org/docs/0.12/patterns/streaming/#streaming-with-context
from flask import stream_with_context, request, Response
#app.route('/stream')
def streamed_response():
def generate():
yield 'Hello '
yield request.args['name']
yield '!'
return Response(stream_with_context(generate()))
It would be helpful if you posted your code, but try wrapping your code like this:
with app.app_context():
# do stuff...
or maybe this:
with app.test_request_context():
# do stuff...

Categories