I have a web app in python Pyramid which calls various other code in python which may raise an exception.
Instead of the user receiving a "500 Internal Error", I'd like them to get a more specific error, for instance if MyException is thrown, show a 503 Error. I tried to do this:
#view_config(context=MyException, permission='view')
def custom_exc(exc, request):
raise HTTPServiceUnavailable(exc.message)
However, that fails because it is for some reason unauthorized:
HTTPForbidden: Unauthorized: custom_exc failed permission check
My ACL is as follows:
class RootFactory(object):
__acl__ = [
(Allow, 'admin', ('view',))
]
I am connected with the user admin and it works perfectly for other views.
Does anyone know how to solve this or else how to "chain" exceptions in Pyramid in a different way?
Learn from a customized version of famous ToDoPyramid example application. This way I translate an internal technical event, a database exception, into a meaningful application specific message within custom exception view code. Some guys call this a layer of abstraction or information hiding.
Do not protect these exception views with permissions, since you should protect code that does stuff and CAN raise exceptions.
from sqlalchemy.exc import OperationalError as SqlAlchemyOperationalError
#view_config(context=SqlAlchemyOperationalError)
def failed_sqlalchemy(exception, request):
"""catch missing database, logout and redirect to homepage, add flash message with error
implementation inspired by pylons group message
https://groups.google.com/d/msg/pylons-discuss/BUtbPrXizP4/0JhqB2MuoL4J
"""
msg = 'There was an error connecting to database'
request.session.flash(msg, queue='error')
headers = forget(request)
# Send the user back home, everything else is protected
return HTTPFound(request.route_url('home'), headers=headers)
Related
I'm developing an application that requires the user to login. It's a terminal browser to navigate, and use, a popular email client.
I am struggling with the logic flow of actually logging the user in without things getting messy. I will try to explain what I'm trying to achieve in psedueo code, and then demonstrate what I've currently done.
username = 'joe#example.com'
password = 'itsasecret'
# User has logged in before using our application. Instead of
# logging in via HTTP, just inject the cookies into the session.
if userExistsInDatabase:
session.addCookies(db.getCookies(username))
# Check the session is still valid. Just because we load
# the cookie from the database doesn't mean it's valid as
# the account could be blocked, or session could have expired
if session.checkIfSessionIsValid():
print 'Logged In'
else:
# Login failed, now we need to do a HTTP request
# incase the session has died
if session.login(username, password):
# Login success
else:
# Login Failed
else:
# No session exists in DB, try to log in and add user to db
if session.login(username, password):
# Login success
else:
# Login Failed
I hope that code explains it better than I could in words. But, the problem I am having is everything is getting messy and fast, and it's a pain to have to repeat this code whenever I need to use it.
This is something I do regular on a lot of my projects, because most HTTP sites, at least the large ones, have a similar sort of login flow.
Any advice? If you need more info, please ask.
Assuming the supporting code make proper use of exceptions, you can solve this with less code duplication:
class Session():
def reconnect(self, username):
try:
cookie = db.getCookies(username)
except LookupError:
raise UserDoesNotExist
else:
self.addCookies(cookie)
if not self.checkIfSessionIsValid():
raise InvalidSession
def login(self, ...):
if not do_the_login(...):
raise LoginError
try:
try:
session.reconnect(...)
except (InvalidSession, UserDoesNotExist):
session.login(...)
except LoginError:
# failed
# at this point, either an unhandled exception terminated
# this code path or we are logged in.
I'm trying to implement the mailchimp python API in a django project similar, following their example on github. I was trying to make a connection in a class based view however when I load up the view I get the notice
Attribute Error at\
'module' object has no attribute 'session'
It's set up exactly like their example and the error occurs where I define
m = get_mailchimp_api()
I opened up the mailchimp.py file in my site packages after following the traceback and saw the following:
import requests
class Mailchimp(object):
root = 'https://api.mailchimp.com/2.0/'
def __init__(self, apikey=None, debug=False):
'''Initialize the API client
Args:
apikey (str|None): provide your MailChimp API key. If this is left as None, we will attempt to get the API key from the following locations::
- MAILCHIMP_APIKEY in the environment vars
- ~/.mailchimp.key for the user executing the script
- /etc/mailchimp.key
debug (bool): set to True to log all the request and response information to the "mailchimp" logger at the INFO level. When set to false, it will log at the DEBUG level. By default it will write log entries to STDERR
'''
self.session = requests.session()
The traceback ends at the self.session = requests.session() line.
This is my view where I am trying to call Mailchimp
from app.utils import get_mailchimp_api
import mailchimp
from django.views.generic import TemplateView
class HomeView(TemplateView):
template_name = 'home.html'
# print requests -- this is undefined
m = get_mailchimp_api()
Is it because the CBV doesn't have a request parameter? In the github example they show the connection being made in a function based view where the function takes a requests. If that's the case, how can I pass the response into the CBV? This is the exact example Mailchimp gives on github:
def index(request):
try:
m = get_mailchimp_api()
lists = m.lists.list()
except mailchimp.Error, e:
messages.error(request, 'An error occurred: %s - %s' % (e.__class__, e))
return redirect('/')
Requests doesn't have a session() method...but id does have a Session() object.
Sounds like a bug in the wrapper.
Requests aliases Session() with session(), so that's probably not the issue. It almost sounds like there's something up either with your get_mailchimp_api() method or something is weird with the imports. Other stackoverflow questions about similar error messages seem to come from mutual imports, typos, or other such things.
Presumably your app.utils module is importing mailchimp already, like MailChimp's does? If not, I'd try that. If so, maybe remove your import mailchimp from this file.
I'm attempting to set up Sentry for my current web app. It can be configured to act as a logger.
However, I have an Exception view that looks like this:
#view_config(context=Exception,
renderer='hitchedup:templates/errors/500.html')
def error_view(exc, request):
"""Display a friendly error message"""
return {}
The idea is that whenever any exception occurs on the site, I can deliver a friendly message to the user.
But, how do I still pass the errors through to the logger? Right now, the Exception view catches all exceptions, but then they never reach the logger.
I tried raising and catching exc inline in the Exception view, then sending it to Sentry through the client directly, but then I don't get the full stacktrace I would get if I caught the exception where it was originally raised.
How can I get both friendly 500 pages and still have good logging and error reporting?
The bare raise keyword is the ticket.
Instead of using logging, capture the Exception in a view with a friendly message. Then, use raise without an argument, which raises the original exception. Catch the exception and use the Sentry client to capture it and pass it along.
The final view looks like this:
from raven.base import Client
from pyramid.view import view_config
#view_config(context=Exception,
renderer='hitchedup:templates/errors/500.html')
def error(context, request):
"""Display an error message and record it in Sentry."""
client = Client()
try:
raise
except Exception:
client.captureException()
return {}
I am trying to use session to pass some data from one page to another page.
Here is the code i wrote in ajax.py.
def save_cookie(request, query):
request.session['query'] = query
But when i call this dajaxice function.An error will occurred. As we all know that when we try to use dajaxice in html page, the error msg always is "sth goes wrong".
I tried to debug save_cookie, but the mock request object i created has no session attr. However, if i do request.session="blah", it worked. If I directly use save_cookie(request,query). It will pop up the error msg that request object has no attr seesion...
The code is right straight forward. I didn't see any mistake in it. Does anyone know the cause?
Never used dajaxice / dajax so I can't really help here. Just a few points:
did you enable (and properly configue) the session support ? https://docs.djangoproject.com/en/1.3/topics/http/sessions/
you can use the logging module (or a plain "print" statement but then you won't have the whole traceback) to trace the exception, ie :
def save_cookie(request, query):
try:
request.session['query'] = query
except Exception, e:
print e
raise
The output of the print statement should now appear in the shell you started the dev server from (assuming you're working with the dev server... you ARE workin with the dev server, aren't you ?)
still using the dev server, you can use pdb to switch to interactive debugging:
def save_cookie(request, query):
import pdb; pdb.set_trace()
request.session['query'] = query
then try to access the url in your browser, switch back to your shell and you're in a pdb session where you can inspect the request and (if there's one) request.session object etc.
NB : don't do this if running behind Apache or any other web server - only with the builtin dev server.
"request.session='blah'" will create the "session" attribute on the "request" object if it doesn't exist (and possibly replace the real "session" object if it already existed), so it's neither a valid test nor something sensible to do
My 2 cents...
Disclaimer: I don't know anything about dajaxice.
The following will work on a mock request object:
def save_cookie(request, query):
if not hasattr(request, 'session'):
request.session = dict()
request.session['query'] = query
I've created a library that connects to some remote JSON api. I have a Django application that will render a internal server error, if my library fails connecting to the resource (504 error). Here is a hypothetical example that kind of shows the layout of my library's code.
mymodule.py
...
class Error(Exception):
"""class placeholder for errors"""
class theObject(object):
#classmethod
def get(self, params = {}):
url = 'http://somewhere.com/collection.json%s' % (urlencode(params))
try:
response = unicode(urlopen(url).read(), 'utf8').decode()
dictionary = json.loads(response)
result = ApiObject(dictionary)
return result
except HTTPError, e:
raise Error(e.read())
except (ValueError, KeyError), e:
raise Error('Invalid Response')
class Person(theObject):
#classmethod
def get_person(cls, person_id):
params = {'id':person_id)
result = super(Stuff, cls).get(params)
return result
view.py
import mymodule
...
remote_result = module.Person.get_person(person_id=person)
...
Is there a recommended way to alert the user that the resource is too busy? This would be better than rendering an internal server error from my current ajax requests.
Thanks!!
Figured my problem out. I feel stupid, because the solution is so simple. Hopefully, this will help someone out in the future who runs into the same issue. If a HTTP connection fails on an imported library, you can avoid rendering a server error page and produce a friendly notification for the client.
All it requires is that you find the class in the imported library that handles the errors. In this case, it is named "Error."
import mymodule
try:
[do a bunch of things...]
except mymodule.Error:
error = "Remote servers appear to be busy. Please try again."
return HttpResponse(error)
You can and probably should customize it to handle exceptions specific to the raised HTTPError.
Cheers!