The following code (which tries to “open” an encrypted RAR-file supplying a wrong password):
from unrar import rarfile
import unrar
try:
rarfile.RarFile("encrypted.rar", pwd="wrong_password")
except Exception as e:
print(type(e))
print(e)
mostly, though nothing else is wrong with the RAR-file (can be decrypted without errors by using the correct password), outputs:
<class 'unrar.rarfile.BadRarFile'>
Invalid RAR file.
but sometimes it outputs:
<class 'RuntimeError'>
Bad password for Archive
How do I check if a password for a RAR-file is correct with Python's UnRAR library without chaining the exceptions?
In short: UnRAR library raises (randomly?) different exception for the same type of error (namely, wrong password supplied). In most of the cases it raises BadRarFile but sometimes it raises RuntimeError. Catching RuntimeError is bad enough (yet here we can at least check the args), but if one also catches except unrar.rarfile.BadRarFile, one cannot even differentiate between the error that (a) the password is wrong or (b) that the RAR-file is bad.
You could chain multiple except to narrow down the error. Unfortunately, your unrar library seems to raise the unspecific exception RuntimeError in case a bad password is provided. So you cannot be 100% sure if a bad password is the reason for the error.
try:
unrar.rarfile.RarFile("encrypted.rar", pwd="wrong_password")
except unrar.rarfile.BadRarFile:
print("Specified file doesn't seem to be a proper RAR archive")
except RuntimeError:
print("RuntimeError, possibly a wrong password")
except:
print("Something else happened")
Other than using different error messages "Wrong password or defective file" and "Wrong password or something else", unfortunately, I don't see any possibility for improvement.
In short: UnRAR library raises (randomly?) different exception for the same type of error (namely, wrong password supplied). In most of the cases it raises BadRarFile but sometimes it raises RuntimeError.
It's possible that depending on the version of the RAR file specs, there have been changes how a bad password is being handled. Maybe it isn't possible to differentiate between corrupt files and a wrong password with RAR files of a newer version while this was possible for older files. (Or the other way around.)
If the "original" unrar command doesn't have this issue, it's possibly a bug upstream in your Python wrapper library.
Related
I have this block of code:
path = askdirectory(title='Choose folder')
os.chdir(path)
try:
os.system('currency.py')
except:
#Error message
ctypes.windll.user32.MessageBoxW(0, u"Error", u"Error", 0)
What I want to accomplish is that when the user chooses the wrong folder (the one in which the 'currency.py' file is not in), it throws this little Error message box.
Instead, when I purposely choose the wrong folder, it says:
"currency.py "is not recognized as an internal or external command
But it doesn't show me the error window. Is there a way to make python recognize this error as an exception? Thank you!
It appears you are using Python to run a Python file using the operating system shell.
You can run the file by importing it and (if needed) instantiating it.
try:
# import the python file
import currency.py
# instantiate the class/function if required
except ImportError:
ctypes.windll.user32.MessageBoxW(0, u"Error", u"Error", 0)
Nevertheless you can avoid using the try/catch scenario by seeing if the file exists, if not, display your error message:
if os.path.isfile(file_path):
os.system('currency.py')
else:
ctypes.windll.user32.MessageBoxW(0, u"Error", u"Error", 0)
You could try listening for the %ErrorLevel% code for anything different than 0 (successful) and, if it matches, then raise Exception.
It would work like this, for example:
if os.system('currency.py'):
raise Exception
So, we had instance in the past where code were broken in IOT devices because of syntax errors.
While there is exception handling in the code. I wanted to create a script to check and make sure that the codes compiles and run without syntax error, else the script replace the broken code by an earlier version.
I tried this
from delta_script import get_update
def test_function():
try:
get_update()
except SyntaxError as syntaxError:
replace_script("utility.py", syntaxError)
except Exception as ignored:
pass
However the problem it when it hit a SyntaxError, it just throw it on the screen and replace_script
because the exception happens on delta_script.py from which get_update() was imported.
So what's the solution in this case?
I have also another function
def compile():
try:
for file in compile_list:
py_compile.compile(file)
except Exception as exception:
script_heal(file, exception)
however in this one, it never report any exception, because I go and introduce syntaxError and the code still compile without reporting an error
Any one could help me figure out a better way to solve those two problems?
thanks,
SyntaxErrors occur at compile time, not run time, so you generally can't catch them. There are exceptions, involving run time compilation using eval/exec, but in general, except SyntaxError: is nonsensical; something goes wrong compiling the code before it can run the code that sets up the try/except to catch the error.
The solution is to not write syntactically invalid code, or if you must write it (e.g. to allow newer Python syntax only when supported) to evaluate strings of said code dynamically with eval (often wrapping compile if you need something more complicated than a single expression) or exec.
I'm trying to write to a file like this:
with open(filename), 'wb') as f:
f.write(data)
But when opening a file and writing to it many things might go wrong and I want to properly report what went wrong to the user. So I don't want to do:
try:
with open(filename), 'wb') as f:
f.write(data)
except:
print("something went wrong")
But instead I want to tell the user more specifics so that they can fix the problem on their end. So I started with catching the PermissionError to tell the user to fix their permissions. But soon I got a bugreport that the program would fail if filename pointed to a directory, so I also caught and reported the IsADirectoryError. But this is not all - it might be that the path to filename doesn't exist, so later I also added the FileNotFoundError.
Opening a file and writing to it is a very simple pattern but still many things can go wrong. I'm wondering whether there is an easy recipe that allows me to catch these errors and report them properly without giving a generic "some error happened" message. This kind of pattern has been used for decades, so there must be some list of things to check for, no?
I now caught a number of exceptions but I can imagine that even more are lurking around. Things probably become even worse when running this on non-Linux platforms...
What are the best practices for opening a file and writing to it in Python that give a maximum of information back to the user when things go wrong without ressorting to dumping the bare traceback?
On the operating system level there are only so many things that can go wrong when doing an open() and write() system call so there must exist a complete list?
You could catch the higher level exception OSError and then present the value of the strerror field to the user as the explanation. This relies on the fact that exceptions such as FileNotFoundError are subclasses of OSError and these subclasses can all be caught by using the base class.
Here's an example:
try:
filename = 'missing_file'
with open(filename) as f:
for line in f:
# whatever....
pass
except OSError as exc:
print('{!r}: {}'.format(filename, exc.strerror))
Running this code when missing_file does not exist produces this error message:
'missing_file': No such file or directory
This may be an open ended or awkward question, but I find myself running into more and more exception handling concerns where I do not know the "best" approach in handling them.
Python's logging module raises an IOError if you try to configure a FileHandler with a file that does not exist. The module does not handle this exception, but simply raises it. Often times, it is that the path to the file does not exist (and therefore the file does not exist), so we must create the directories along the path if we want to handle the exception and continue.
I want my application to properly handle this error, as every user has asked why we don't make the proper directory for them.
The way I have decided to handle this can be seen below.
done = False
while not done:
try:
# Configure logging based on a config file
# if a filehandler's full path to file does not exist, it raises an IOError
logging.config.fileConfig(filename)
except IOError as e:
if e.args[0] == 2 and e.filename:
# If we catch the IOError, we can see if it is a "does not exist" error
# and try to recover by making the directories
print "Most likely the full path to the file does not exist, so we can try and make it"
fp = e.filename[:e.rfind("/")]
# See http://stackoverflow.com/questions/273192/python-best-way-to-create-directory-if-it-doesnt-exist-for-file-write#273208 for why I don't just leap
if not os.path.exists(fp):
os.makedirs(fp)
else:
print "Most likely some other error...let's just reraise for now"
raise
else:
done = True
I need to loop (or recurse I suppose) since there is N FileHandlers that need to be configured and therefore N IOErrors that need to be raised and corrected for this scenario.
Is this the proper way to do this? Is there a better, more Pythonic way, that I don't know of or may not understand?
This is not something specific to the logging module: in general, Python code does not automatically create intermediate directories for you automatically; you need to do this explicitly using os.makedirs(), typically like this:
if not os.path.exists(dirname):
os.makedirs(dirname)
You can replace the standard FileHandler provided by logging with a subclass which does the checks you need and, when necessary, creates the directory for the logging file using os.makedirs(). Then you can specify this handler in the config file instead of the standard handler.
Assuming it only needs to be done once at the beginning of your app's execution, I would just os.makedirs() all the needed directories without checking for their existence first or even waiting for the logging module to raise an error. If you then you get an error trying to start a logger, you can just handle it the way you likely already did: print an error, disable the logger. You went above and beyond just by trying to create the directory. If the user gave you bogus information, you're no worse off than you are now, and you're better in the vast majority of cases.
I'm writing some code to manipulate the Windows clipboard. The first thing I do is to try and open the clipboard with OpenClipboard() function from the Windows API:
if OpenClipboard(None):
# Access the clipboard here
else:
# Handle failure
This function can fail. So if it does, I would like to raise an exception. My question is, which of the standard Python exceptions should I raise? I'm thinking WindowsError would be the right one, but not sure. Could someone please give me a suggestion?
It is better to avoid raising standard exceptions directly. Create your own exception class, inherit it from the most appropriate one (WindowsError is ok) and raise it. This way you'll avoid confusion between your own errors and system errors.
Raise the windows error and give it some extra infomation, for example
raise WindowsError("Clipboard can't be opened")
Then when its being debugged they can tell what your windows error means rather than just a random windowserror over nothing.
WindowsError seems a reasonable choice, and it will record extra error information for you. From the docs:
exception WindowsError
Raised when a Windows-specific error occurs or when the error number does not correspond to an errno value. The winerror and strerror values are created from the return values of the GetLastError() and FormatMessage() functions from the Windows Platform API. The errno value maps the winerror value to corresponding errno.h values. ...