I want to catch an Exception in a for loop, and print a message in all items of the for loop, but at the end, still throw an Exception, so that I know the script failed. How to do that?
I'm trying this for now:
for key, value in marketplaces.items():
try:
r = requests.post(value, data=json.dumps({'Content': message(key, res)}), verify=False)
except:
r = requests.post(value, data=json.dumps({'Content': "Sorry, an error occured"}), verify=False)
But with this code, I only catch the exception and post an error message to all items in the for loop, but don't know that the script failed. I want to know this and throw and exception, after posting an error message to all items in for loop.
Any suggestions? Thank you!
The idea is to set a variable inside the except clause and check it after the loop is done. The example below is roughly what you need.
Note that there could be more than one failure, so we use a list to store all the errors, and report them all at the end.
Also note that the correct way to catch an exception in Python is to catch a specific exception -- in this case, it suffices to catch the requests.exceptions base class.
The code below stores the exception string e in the list of errors and afterwards, we check if there are any errors, and raise a new exception with the entire list of errors.
In this case, we're just raising the most generic exception type for requests exceptions -- requests.exceptions.RequestException (i.e. the base class once again). This is because there are various possible errors which could have occurred during the requests. Depending on your use case, you might prefer another more standard exception type, such as RuntimeError -- see the docs for the standard exception types.
import requests
errors = []
for key, value in marketplaces.items():
try:
r = requests.post(value, data=json.dumps({'Content': message(key, res)}), verify=False)
except requests.exceptions.RequestException as e:
r = requests.post(value, data=json.dumps({'Content': "Sorry, an error occured"}), verify=False)
errors.append(str(e))
if errors:
raise requests.exceptions.RequestException(errors)
Alternative:
Though the above is a fairly common approach that suits many use cases, it may be convoluted in some situations. Hence, I'm proposing a much simpler and more trivial alternative, which may also suit your needs, or anyone else who stumbles across this. Simply print out an error message each time an exception is raised (i.e. each time the code enters the except clause), like this:
import requests
import sys
for key, value in marketplaces.items():
try:
r = requests.post(value, data=json.dumps({'Content': message(key, res)}), verify=False)
except requests.exceptions.RequestException as e:
r = requests.post(value, data=json.dumps({'Content': "Sorry, an error occured"}), verify=False)
print(str(e), file=sys.stderr)
Hope this helps!
I am using the following approach for handling Twilio exceptions in Python:
try:
#code for sending the sms
print(message.sid)
except TwilioRestException as e:
print(e)
This allows me to send sms and Exceptions are handled by Python.
Now I need to "return" the exceptions codes in order to process them, let's say, give user a message depending on the exception.
How can I achieve that?
If raising exception is not an option you could simply add return under except block.
def func():
# your logic
try:
#code for sending the sms
print(message.sid)
except TwilioRestException as e:
print(e)
return e.code # or whetever attribute e has for it...
By default function will return None if everything goes right. In client code it will be like
err = func()
if err:
# sms wasn't sent, action required
If i have some bad authorization data (for example wrong password) SUDS rises exception (400, u'Bad Request') from which i cant get anything, but in teh log is response, which contains data that password is wrong, but how to get this response? I tried like this:
except Exception as e:
print str(e)
print self._client.last_received()
It prints:
(400, u'Bad Request')
None
But in log there is long xml which contains <SOAP-ENV:Reason><SOAP-ENV:Text xml:lang="en">Sender not authorized</SOAP-ENV:Text></SOAP-ENV:Reason>
I am pulling this out of a comment and into an answer because of the code block.
import suds.client
try:
auth_url = "https://url.to.my.service/authenticator?wsdl"
auth_client = suds.client.Client(auth_url)
cookie = auth_client.service.authenticate(user,password)
except Exception as e:
print str(e)
print auth_client.last_received()
Using this code, I receive the appropriate response from my service if I pass an invalid password:
Server raised fault: 'error.pwd.incorrect'
None
And an appropriate response if I pass an invalid user id:
Server raised fault: 'error.uid.missing'
None
Something you may want to consider doing, is changing your except statement to catch suds.WebFault instead of the generic exception. There may be something else that is occurring and triggering your exception block.
One other thing that may help with your issue, is to pass faults=True in your Client() call.
The Client can be configured to throw web faults as WebFault or to
return a tuple (, )
The code I posted above would look like this:
auth_client = suds.client.Client(auth_url, faults=True)
When I have two Python exceptions that are the same exception class but a different error message, how do I catch them separately?
For specific use-case:
I'm using the Facepy library to hit the Facebook Graph API. When the API returns an error that isn't Oauth related, Facepy raises a facepy.exceptions.FacebookError and passes the error message given by the Facebook API.
I'm consistently hitting two different errors that I'd like to treat differently and the only way to parse them is the error message, but I can't figure out how to write my except clause--here it is in pseudo-code:
try:
#api query
except facepy.exceptions.OAuthError and error_message = 'object does not exist':
# do something
except facepy.exceptions.OAuthError and error_message = 'Hit API rate limit':
# do something else
How do I write these except clauses to trigger off both the exception and the error message?
Assuming the Exception's error message is in the error_message attribute (it may be something else — look at the Exception's __dict__ or source to find out):
try:
#api query
except facepy.exceptions.OAuthError as e:
if e.error_message == "object does not exist":
print "Do X"
elif e.error_message == "Hit API rate limit":
print "Do Y"
else:
raise
facepy's OAuthError derives from FacebookError and that has message attribute. https://github.com/jgorset/facepy/blob/master/facepy/exceptions.py#L8. So, you can use if condition with the message like this
try:
#api query
except facepy.exceptions.OAuthError as error:
if 'object does not exist' == error.message:
# do something
elif 'Hit API rate limit' == error.message:
# do something else
else:
raise
I have a python class with many methods():
Method1()
Method2()
...........
...........
MethodN()
All methods -- while performing different tasks -- have the same scheme:
do something
do something else
has anything gone wrong?
raise an exception
I want to be able to get an email whenever an exception is raised anywhere in the class.
Is there some easy way to combine this logic into the class, rather than calling SendEmail() before every raise Exception statement? what is the right, pythonic way to deal with such a case? canh a 'generalized' Exception handler be the solution? I'd be glad for any ideas you may have.
like #User said before Python has logging.handlers.SMTPHandler to send logged error message. Use logging module! Overriding exception class to send an email is a bad idea.
Quick example:
import logging
import logging.handlers
smtp_handler = logging.handlers.SMTPHandler(mailhost=("smtp.example.com", 25),
fromaddr="from#example.com",
toaddrs="to#example.com",
subject=u"AppName error!")
logger = logging.getLogger()
logger.addHandler(smtp_handler)
try:
break
except Exception as e:
logger.exception('Unhandled Exception')
Note: Although this is a simple, obvious solution to the problem as stated, the below answer is probably better in most cases.
If the alternative is this:
if problem_test():
SendEmail()
raise Exception
Then why don't you just define a custom raise_email method?
def raise_email(self, e):
SendEmail()
raise e
Python stdlib has dedicated class to do what you want. See logging.handlers.SMTPHandler
Gist Link
The most important trick is here if secure parameter is not passed, the default value is None which raises exception if you are trying to authenticate with TLS/SSL enabled STMP Servers lik Gmail's, Yahoo's, Yandex's, STMP servers.
We passed an empty tuple to trigger smtp.ehlo() to authenticate correctly with SSL.
...
if self.secure is not None:
smtp.ehlo()
smtp.starttls(*self.secure)
smtp.ehlo()
...
import logging
import logging.handlers
__author__ = 'Ahmed Şeref GÜNEYSU'
def foo():
raise Exception("Foo Bar")
def main():
logger = logging.getLogger()
logger.addHandler(logging.handlers.SMTPHandler(
mailhost=("smtp.mail.yahoo.com", 587),
fromaddr="boss#example.com",
toaddrs="me#example.com",
subject="EXCEPTION",
credentials=('smtpuser#example.com', 'MY SECRET PASSWORD'),
secure=()))
try:
foo()
except Exception, e:
logging.exception(e)
if __name__ == '__main__':
main()
Beware the wizard's apprentice!
It would be better to log those errors, then check to see when the last email was sent, and if the timespan is too short, do not send another message because the human being will already be looking at the log file. For many things, one message per day would be enough, but even for system critical things, if you have already had one failure, what could go wrong if you wait two hours to send the next email?
If you send one email per two hour timespan, then the maximum number of emails per day is 12. And if you get a cascading failure (you will!) then it will most likely happen within a couple of hours of the first failure event.
Most large networking companies offer an SLA of 4 hour to fix a failure, measured from the time it first occurs (because cascading failures tend to repeat) until the customer is satisified that it is fixed. If you have a tighter SLA than that, then unless it is some finance industry service, you probably are offering too high of a service level.
But if you do have a 4 hour SLA, then I would make sure that any email sent within 2 - 4 hours of the last email, should use whatever bells and whistles you can to prioritise it, highlight it, etc. For instance use the X-Priority header and put the word URGENT in the subject so that your mail client can display it in large bold red letters.
How about this:
class MyException(Exception):
def __init__(self):
SendEmail()
Something like this, perhaps?
def mailexception(ex):
# Be creative.
print 'Mailing... NOW!'
def pokemontrainer(cls):
class Rye(cls):
def __getattribute__(self, name):
def catcher(func):
def caller(*args, **kwargs):
try:
func(*args, **kwargs)
except Exception, e:
mailexception(e)
raise
return caller
ref = cls.__getattribute__(self, name)
if hasattr(cls, name) and hasattr(getattr(cls, name), '__call__'):
return catcher(ref)
return Rye
#pokemontrainer
class Exceptor(object):
def toss(self, e):
raise e('Incoming salad!')
ex = Exceptor()
ex.toss(ValueError)
By 2019, the easiest and best option seems to be sentry.
You need just two lines of code:
import sentry_sdk
sentry_sdk.init("https://xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx#sentry.io/xxxxxxx")
and it will send you a detailed email with any raised error.
After this you can further inspect the error on the Sentry website where there is impressive debug info - exceptions traceback, logs, data passed to functions, packages versions etc...
I'd just subclass Exception and send the e-mail in the custom Exception.
You can use an except hook to send an email when an exception is not caught.
see sys.excepthook
You can use python alerting library. It supports email (via mailgun and sendgrid), telegram and slack notifications for sending alerts.
https://github.com/sinarezaei/alerting
Sample code:
from alerting import Alerting
from alerting.clients import AlertingMailGunClient, AlertingSlackClient, AlertingTelegramClient
alerts = Alerting(
clients=[
AlertingMailGunClient(your_mailgun_api_key, your_domain, from_email, target_email),
AlertingSlackClient(your_bot_user_oauth, target_channel),
AlertingTelegramClient(bot_token, chat_id)
]
)
try:
# something
except Exception as ex:
alerting.send_alert(title='some bad error happened', message=str(ex))
I like the answers that use the logging module, but I use the smtp library (smtplib) for this. To send error message, I do something like the following in the exception branch of the try/except block:
import smtplib as smtp
s = smtplib.SMTP('smtp.gmail.com', 587)
s.starttls()
from_user = r"foo#gmail.com"
to_user = r"bar#gmail.com"
password = "xxxxxxxx"
s.login(from_user, password)
subject = "Uh oh"
text = "XYZ error message you blew it!"
message = f"Subject: {subject}\n\n{text}"
s.sendmail(from_user, to_user, message);
This works well, but isn't the most secure option in the world. You actually have to tell google you want to let less secure apps connect (you can change this setting here: https://myaccount.google.com/lesssecureapps).