I am a relative python newbie and I am getting confused with how to properly handle exceptions. Apologies for the dumb question.
In my main() I iterate through a list of dates and for each date I call a function, which downloads a csv file from a public web server. I want to properly catch exceptions for obvious reasons but especially because I do not know when the files of interest will be available for download. My program will execute as part of a cron job and will attempt to download these files every 3 hours if available.
What I want is to download the first file in the list of dates and if that results in a 404 then the program shouldn't proceed to the next file because the assumption is if the oldest date in the list is not available then none of the others that come after it will be available either.
I have the following python pseudo code. I have try/except blocks inside the function that attempts to download the files but if an exception occurred inside the function how do I properly handle it in the main() so I can make decisions whether to proceed to the next date or not. The reason why I created a function to perform the download is because I want to re-use that code later on in the same main() block for other file types.
def main():
...
...
# datelist is a list of date objects
for date in datelist:
download_file(date)
def download_file(date):
date_string = str(date.year) + str(date.strftime('%m')) + str(date.strftime('%d'))
request = HTTP_WEB_PREFIX+ date_string + FILE_SUFFIX
try:
response = urllib2.urlopen(request)
except urllib2.HTTPError, e:
print "HTTPError = " + str(e)
except urllib2.URLError, e:
print "URLError = " + str(e)
except httplib.HTTPException, e:
print "HTTPException = " + str(e)
except IOError:
print "IOError = " + str(e)
except Exception:
import traceback
print "Generic exception: " + traceback.format_exc()
else:
print "No problem downloading %s - continue..." % (response)
try:
with open(TMP_DOWNLOAD_DIRECTORY + response, 'wb') as f:
except IOError:
print "IOError = " + str(e)
else:
f.write(response.read())
f.close()
The key concept here is, if you can fix the problem, you should trap the exception; if you can't, it's the caller's problem to deal with. In this case, the downloader can't fix things if the file isn't there, so it should bubble up its exceptions to the caller; the caller should know to stop the loop if there's an exception.
So let's move all the exception handling out of the function into the loop, and fix it so it craps out if there's a failure downloading the file, as the spec requires:
for date in datelist:
date_string = str(date.year) +
str(date.strftime('%m')) +
str(date.strftime('%d'))
try:
download_file(date_string)
except:
e = sys.exc_info()[0]
print ( "Error downloading for date %s: %s" % (date_string, e) )
break
download_file should now, unless you want to put in retries or something like that, simply not trap the exceptions at all. Since you've decoded the date as you like in the caller, that code can come out of download_file as well, giving the much simpler
def download_file(date_string):
request = HTTP_WEB_PREFIX + date_string + FILE_SUFFIX
response = urllib2.urlopen(request)
print "No problem downloading %s - continue..." % (response)
with open(TMP_DOWNLOAD_DIRECTORY + response, 'wb') as f:
f.write(response.read())
f.close()
I would suggest that the print statement is superfluous, but that if you really want it, using logger is a more flexible way forward, as that will allow you to turn it on or off as you prefer later by changing a config file instead of the code.
From my understanding of your question... you should just insert code into the except blocks that you want to execute when you encounter the particular exception. You don't have to print out the encountered error, you can do whatever you feel is necessary when it is raised... provide a popup box with information/options or otherwise direct your program to the next step. Your else section should isolate that portion, so it will only execute if none of your exceptions are raised.
Related
Code:
def ScanNetwork():
nmScan = nmap.PortScanner()
s = nmScan.scan("192.168.1.1/24", '0-4444', arguments="-O")
for host in nmScan.all_hosts():
if nmScan[host].state() == "up":
print("Host : %s (%s)" % (host, nmScan[host].hostname()))
try:
print("Version: " + s['scan'][host]['osmatch'][0]['name'] + "\nType: " + s['scan'][host]['osmatch'][0]['osclass'][0]['type'])
except:
print("An Error occured while scanning this host!\nNo extra info was collected!")
continue
for proto in nmScan[host].all_protocols():
print("---------------------------")
print("\nProtocol : %s" % proto)
lport = nmScan[host][proto].keys()
lport = sorted(lport)
for port in lport:
print("\nport: " + f'{port}' + "\tstate: " + nmScan[host][proto][port]['state'])
print("---------------------------\n")
print("\n\nScan Completed!")
ScanNetwork()
Sometimes an exception occurs when the nmap fails to identify the Version or the Os running in a host. (It's a KeyError exception)
The part which is supposed to handle that exception is this:
try:
print("Version: " + s['scan'][host]['osmatch'][0]['name'] + "\nType: " + s['scan'][host]['osmatch'][0]['osclass'][0]['type'])
except:
print("An Error occured while scanning this host!\nNo extra info was collected!")
continue
I of course assume that there's nothing wrong with nmap's output and I've again made a huge beginner mistake and that's the reason my code is getting stuck..
Notes:
I've left the script running over night incase it returned some useful info but nothing happened!
Please do not tell me that I need to quit using the nmap module and turn to nmap3
Having tired you with all the above, does anyone know a way to still handle that exception without the code getting stuck?
You have forgotten to add the type of the exception to excpect while running the code inside the try-except block.
This should fix it:
try:
print("Version: " + s['scan'][host]['osmatch'][0]['name'] + "\n" + "Type: " + s['scan'][host]['osmatch'][0]['osclass'][0]['type'])
except KeyError:
print("An Error occured while scanning this host!\nNo extra info was collected!")
continue
In case you want to catch any possible exception that could occur (and yes custom ones included) then you should use the following code:
try:
<some critical code with possible exceptions to catch>
except Exception:
print("An exception occured")
screen shot
hi, I am trying to run this python code for twitter scraping. as it is shown in the screenshot, it gives me a syntax error that I can't figure out. here is the whole code:
print ('Filtering the public timeline for track="%s"' % (q,))
twitter_stream = twitter.TwitterStream(auth=twitter_api.auth)
stream = twitter_stream.statuses.filter(track=q)
for tweet in stream:
try:
if tweet['truncated']:
tweet_text = tweet['extended_tweet']['full_text']
else:
tweet_text = tweet['text']
# write the values to file
csvwriter.writerow([
tweet['created_at'],
getVal(tweet['user']['screen_name']),
getVal(tweet_text),
getVal(tweet['user']['location']),
tweet['user']['statuses_count'],
tweet['user']['followers_count'],
tweet['user']['friends_count'],
tweet['user']['created_at']
])
# print something to the screen, mostly so we can see what is going on...
print (tweet['user']['screen_name'].encode('utf-8'), tweet['text'].encode('utf-8'))
except Exception, err:
print err
pass
I can only assume you are running this using Python 3 and not 2.
This question explains that the format you've used for the except statement would be wrong in that case.
The TL;DR of that post is:
switch the comma to as: except Exception as err:
Hello I am trying to make 'try' only work under one condition:
try:
print "Downloading URL: ", url
contents = urllib2.urlopen(url).read()
except:
message = "No record retrieved."
print message
return None
I do not want the above code to work if the kwarg nodownload is True.
So I have tried the following:
try:
if nodownload:
print "Not downloading file!"
time.sleep(6)
raise
print "Downloading URL: ", url
contents = urllib2.urlopen(url).read()
except:
message = "No record retrieved."
print message
return None
The above always downloads no matter if the --nd argument is passed in the command line. The below always skips the file rather the argument is passed or not.
if not nodownload:
print "Not downloading file!"
time.sleep(6)
raise
print "Downloading URL: ", url
contents = urllib2.urlopen(url).read()
except:
message = "No record retrieved."
print message
return None
No download is input at the command line:
parser.add_argument('--nodownload', dest='nodownload', action='store_true',
help='This doesn't work for some reason')
You can use raise to cause an exception when you need to, thus making the try fail.
As others have mentioned, one can raise an exception.
Apart from using predefined exceptions, you may also use your own:
class BadFriend(Exception):
pass
class VirtualFriend(Exception):
pass
class DeadFriend(Exception):
pass
try:
name = raw_input("Tell me name of your friend: ")
if name in ["Elvis"]:
raise DeadFriend()
if name in ["Drunkie", "Monkey"]:
raise BadFriend()
if name in ["ET"]:
raise VirtualFriend()
print("It is nice, you have such a good friend.")
except BadFriend:
print("Better avoid bad friends.")
except VirtualFriend:
print("Whend did you shake hands with him last time?")
except DeadFriend:
print("I am very sorry to tell you, that...")
You can even pass some data via raised exception, but be careful not to abuse it too far (if
standard structures are working, use simpler ones).
Alright, I am fairly new to python and I am making a console witch will allow multiple features, one of those is to grab a page source and either print it on the page, or if they have another arg then name the file of that arg... The first arg would be the website url to grab the source from.
My imports are:
import os, urllib.request
This is my code:
def grab(command, args, argslist):
if args == "":
print("The " + command + " command wan't used correctly type help " + command + " for help...")
if args != "":
print("This may take a second...")
try:
argslistcheck = argslist[0]
if argslistcheck[0:7] != "http://":
argslist[0] = "http://" + argslist[0]
with urllib.request.urlopen(argslist[0]) as url:
source = url.read()
source = str(source, "utf-8")
except IndexError:
print("Couln't connect")
source = ""
try:
filesourcename = argslist[1] + ".txt"
filesourceopen = open(filesourcename, "w")
filesourceopen.write(source)
filesourceopen.close()
print("You can find the file save in " + os.getcwd() + " named " + argslist[1] + ".txt.")
except IndexError:
print(source)
Now while I will be ok with improving my code right now I'm focusing on the main point. Right now it works, I will improve the code later on, the only problem is that if the user inputs a fake website or a website page that doesn't exist then it returns lot's of errors. Yet if I change:
except IndexError:
print("Coulnd't connect")
source = ""
to just:
except:
print("Couldn't connect")
source = ""
Then it always says Couldn't connect...
Any help? I didn't put the rest of my code because I didn't think it would be useful, if you need it I can put it all.
The reason I titled this hide error is because it still works for some reason it just says that it was unable to connect, if the user types a second argument then it will save the source to the file he named.
try:
argslistcheck = argslist[0]
if argslistcheck[0:4] != "http://":
argslist[0] = "http://" + argslist[0]
with urllib.request.urlopen(argslist[0]) as url:
source = url.read()
source = str(source, "utf-8")
except IndexError:
print("Couln't connect")
source = ""
In that code block, the only thing that can raise an IndexError exception is the argslist[0]. This will happen if there is no element within that list. This is very likely not your problem.
Now if an invalid address is entered, urlopen will fail. But it will not raise an IndexError but rather an urllib.error.URLError or the more specialized urllib.error.HTTPError.
If you just write except IndexError you will only catch that error, but not the exception raised by the urlopen. If you want to catch those as well, you have to add another except case:
except IndexError:
print('Argument is missing')
except urllib.error.URLError:
print('Could not connect to the URL.')
The alternative is to just catch any exception by just not specifying any (this is what you did in your last code). Note that this is usually not recommended as it will hide any exceptions that occur which you might not have expected to ever happen; i.e. it will hide bugs. So if you know that there are only a few possible exceptions, just catch those and handle them explicitely.
I am having trouble getting this to work correctly (obviously) - I am ALMOST there, and I have a good idea of WHY it is not working - just not sure how to make it work.
This is suppose to attempt to read a file into memory, if it fails it goes to the "except" clause of the block of code (that part is 'duh'). The error file prints: "<main.DebugOutput instance at 0x04021EB8>". What I want it to do is print the actual Error. Like a FileIOError or TraceBackError or whatever it is to that error file. This is just the beginning stages and I plan to add things like date stamps and to have it append, not write/create - I just need the actual error printed to the file. Advice?
import os, sys
try:
myPidFile = "Zeznadata.txt"
myOpenPID_File = open(myPidFile, "r") #Attempts to open the file
print "Sucessfully opened the file: \"" + myPidFile + "\"."
except:
print "This file, \"" + myPidFile + "\", does not exist. Please check the file name and try again. "
myFileErr = open("PIDErrorlog.txt", "w")
myStdError = str(sys.stderr)
myFileErr.write(myStdError)
myFileErr.close()
print "\nThis error was logged in the file (and stored in the directory): "
First, you should use a logging library. It will help you deal with different logging levels (info/warn/error), timestamps and more.
http://docs.python.org/library/logging.html
Second, you need to catch the error, and then you can log details about it. This example comes from the Python documentation.
import sys
try:
f = open('myfile.txt')
s = f.readline()
i = int(s.strip())
except IOError as (errno, strerror):
print "I/O error({0}): {1}".format(errno, strerror)
except ValueError:
print "Could not convert data to an integer."
except:
print "Unexpected error:", sys.exc_info()[0]
raise
See how it catches an IOError and assigns the error number and error message to variables? You can have an except block for each type of error you want to deal with. In the last (generic error) block, it uses sys.exc_info()[0] to get the error details.
http://docs.python.org/tutorial/errors.html
the problem is here:
myStdError = str(sys.stderr)
myFileErr.write(myStdError)
sys.stderr is a file-like interface defined in POSIX standards, called standard error, not "error text" that you can write to a file. So what you (probably) wanted to do is:
sys.stderr = myFileErr
This is the python equivalent to python your_python_sctipt.py 2> PIDErrorLog.txt in unix shell.
Use the logging module. The logging module have exception formatters that will help you print exceptions in a pretty way.
http://docs.python.org/library/logging.html
Like Kimvais says, your problem is:
myStdError = str(sys.stderr)
myFileErr.write(myStdError)
sys.stderr is a file handle to stderr (stdout is what print writes to).
You should do something like:
try:
...open file...
except IOError as (errno, strerror):
...open errfile...
errfile.write(strerror)
errfile.close()
Alison's answer is also very good and well written.