File opening operation misbehaving in Python 2.7 - python-2.7

I am learning about exceptions and so performing some file operations and testing various parts of code that can possibly generate exceptions while working with files in Python. I am executing this Python 2.7 code on Canopy.
#!/usr/bin/python
import os
try:
fp = open('testfile', 'r')
except IOError:
print 'File not opened successfully'
else:
print 'File opened successfully'
try:
fp.write('Hello!')
except IOError:
print 'Write not allowed on this file'
else:
print 'Write successful'
try:
fp.close()
except IOError:
print 'File not closed properly'
else:
print 'File closed successfully'
finally:
if os.path.exists(fp.name):
os.remove(fp.name)
When I execute this code, I get the following output:
File not opened properly
NameErrorTraceback (most recent call last)
/home/sr/Python/tcs.py in ()
--> 185 if os.path.exists(fp.name)
NameError: name 'fp' is not defined
But if I change the access mode of file to 'w', Then everything seems to work properly with the correct output as:
File opened successfully
Write successful
File closed successfully
I cannot understand why the 'r' mode is not making the file open properly and thus the fp file object is not created. Please help me figure the problem out.
P.S.: Also I would like to know if there is a better way of implementing the same thing. But this is optional.

Explanation
The error combined with your printout should be pretty self-explanatory: the variable fp does not exist if you can't open the file.
The mode 'r' indicates that you want to open the file for reading. You can not read something that is not there, so you end up going to the finally block in your code after processing the IOError. But the error occurs before fp was set, so there is no variable fp, hence the error. [Solutions below]
The mode 'w' indicates that you want to open for writing, but from scratch. There is also an 'a' mode to append if the file already exists. You can write to a non-existent file just fine, so your code does not fail. In fact, if the file did exist in 'w' mode, it would be trucated and any previous contents would be lost.
Try creating an empty file and running with mode 'r'. You should get an exception that prints 'Write not allowed on this file'. That is because, as your error message correctly indicates, writing to a file opened in read mode is not allowed.
Improvements
There are two major improvements that you can make to your code. One is fixing the logical flaws, the other is a major stylistic improvement using with statements.
You have two major logic errors. The first is in the outermost finally block that you already saw. The simplest fix is moving the contents of the finally block into the else, since you don't have any action to take if the file was not opened. Another solution is to refer to the file name you are trying to open in the first place. For example, you could store the file name into a variable and use that:
filename = 'testfile'
try:
fp = open(filename, 'r')
...
finally:
if os.path.exists(filename):
os.remove(filename)
The second major logic error is that you do not close the file if your write fails. Notice that you call fp.close() only in the else clause of your try block. If should instead appear in a finally block. The print statement should of course stay in the else. Change
else:
print 'Write successful'
try:
fp.close()
...
to
else:
print 'Write successful'
finally:
try:
fp.close()
...
The entire code can be improved stylistically by using with blocks to manage your file operations. The obvious way to do this is as follows:
fname = 'testfile'
with open(fname, 'r') as fp:
fp.write('Hello!')
if os.path.exists(fname):
os.remove(fname)
You will not get as many detailed messages when things fail, but overall, this code is cleaner, shorter and more robust than what you have. It guarantees that the file will be closed whether or not an exception occurred anywhere along the way. If you need the detailed error output that you currently have, keep using the current try blocks. The reason that most people will prefer with is that any error that occurs will have a detailed desciption and a line number that it happened on, so you basically get all the necessary information with a lot less work.
Here are some additional resources to help you understand with and context managers:
Understanding Python's "with" statement (from effbot.org)
Official documentation for with
SO 1, 2, 3, 4

Related

How to close file descriptors in python?

I have the following code in python:
import os
class suppress_stdout_stderr(object):
'''
A context manager for doing a "deep suppression" of stdout and stderr in
Python, i.e. will suppress all print, even if the print originates in a
compiled C/Fortran sub-function.
This will not suppress raised exceptions, since exceptions are printed
to stderr just before a script exits, and after the context manager has
exited (at least, I think that is why it lets exceptions through).
'''
def __init__(self):
# Open a pair of null files
self.null_fds = [os.open(os.devnull,os.O_RDWR) for x in range(2)]
# Save the actual stdout (1) and stderr (2) file descriptors.
self.save_fds = (os.dup(1), os.dup(2))
def __enter__(self):
# Assign the null pointers to stdout and stderr.
os.dup2(self.null_fds[0],1)
os.dup2(self.null_fds[1],2)
def __exit__(self, *_):
# Re-assign the real stdout/stderr back to (1) and (2)
os.dup2(self.save_fds[0],1)
os.dup2(self.save_fds[1],2)
# Close the null files
os.close(self.null_fds[0])
os.close(self.null_fds[1])
for i in range(10**6):
with suppress_stdout_stderr():
print 'plop'
if i % 50 == 0:
print i
it fails at 5100 on OSX with OSError: [Errno 24] Too many open files. I'm wondering why and if there is a solution to close the file descriptor. I'm looking for a solution for a context manager which closes stdout and stderr.
I executed your code on a Linux machine and got the same error but at a different number of iterations.
I added the following two lines in the __exit__(self, *_) function of your class:
os.close(self.save_fds[0])
os.close(self.save_fds[1])
With this change I do not get an error and the script returns successfully. I assume that the duplicated file descriptors stored in self.save_fds are kept open if you don't close them with os.close(fds) and so you get the too many files open error.
Anyway my console printed "plop", but maybe this depends on my platform.
Let me know if it works :)

Script failing to open and append multiple files simultaneously

So trying to finish a very simple script that has given me a unbelievably hard time. It's supposed to iterate through specified directories and open all text files in them and append them all with the same specified string.
The issue is it's not doing anything to the files at all. Using print to test my logic I've replaced lines 10 and 11 with print f (the write and close functions), and get the following output:
<open file '/Users/russellculver/documents/testfolder/.DS_Store', mode 'a+' at
So I think it is storing the correct files in the f variable for the write function, however I am not familiar with how Mac's handle DS_STORE or the exact role it plays in temporary location tracking.
Here is the actual script:
import os
x = raw_input("Enter the directory path here: ")
def rootdir(x):
for dirpaths, dirnames, files in os.walk(x):
for filename in files:
try:
with open(os.path.join(dirpaths, filename), 'a+') as f:
f.write('new string content')
f.close()
except:
print "Directory empty or unable to open file."
return x
rootdir(x)
And the exact return in Terminal after execution:
Enter the directory path here: /Users/russellculver/documents/testfolder
Exit status: 0
logout
[Process completed]
Yet nothing written to the .txt files in the provided directory.
The way the indentation is in the question, you return from the function right after writing the first file; either of the for-loops never finish. Which is relatively easy to surmise from the fact that you only get one output file printed.
Since you're not doing anything with the result of the rootdir function, I would just remove the return statement entirely.
An aside: there is no need to use f.close() when you open a file with the with statement: it will automatically be closed (even upon an exception). That is in fact what the with statement was introduced for (see the pep on context managers if necessary).
To be complete, here's the function the way I would have (roughly) written it:
def rootdir(x):
for dirpaths, dirnames, files in os.walk(x):
for filename in files:
path = os.path.join(dirpaths, filename)
try:
with open(path, 'a+') as f:
f.write('new string content')
except (IOError, OSError) as exc:
print "Directory empty or unable to open file:", path
(Note that I'm catching only the relevant I/O errors; any other exceptions (though unlikely) will not be caught, as they are likely not to be related to non-existing/unwritable file.)
Return was indented wrong, ending the iteration after a single loop. Wasn't even necessary so was removed entirely.

Can exceptions be raised/handled twice in a Python script (2.7.8)

So I'm developing a program, for fun, that tells me how many times I have played a specific game at the same time as another person. There are three separate directories that the chat logs (that I'm pulling usernames from) can be found. I'm making the program diverse enough to work on other people's machines, which means they must manually input the directory for all 3 areas. Here's where my problem comes in: what happens if OSError is raised (and handled) twice?
At the current moment, when I test the program, I input an invalid path for the first of the 3 and the program prints a message telling me it's invalid (thus OSError has been handled); however, when I do the same for the 2nd of 3 paths, the message is not printed. I'm not sure why, though. Actually I'm not sure if I tested inputting two invalid paths in a row for the 1st of the 3 paths to see if it prints the message each time, but I think I tested that and it did. Is there any way I can ensure that message is always printed, for all 3 options, regardless of how many times it's been raised already?
EDIT: Just realised I got rid of the "OSError" part of my except statements (but I don't think that's too important to this question?), and forgot to mention, in case it's useful, that I'm on a Windows 7 PC
My code (the useful parts):
while True:
try:
SteamDirec = raw_input("Please enter the directory for steam KAG logs (ends in \Logs).\nEnter 'none' if you do not have steam KAG: ")
if SteamDirec.lower() == 'none':
break
else:
#SteamKAG(SteamDirec) commented out, not useful here
break
except:
print "\nInvalid directory. Ensure the directory is correct and try again.\n"
while True:
try:
NonSteamDirec = raw_input("\nPlease enter the directory for non-steam KAG (release) logs (ends in \Logs).\nEnter 'none' if you do not have non-steam KAG (release): ")
if NonSteamDirec.lower() == 'none':
break
else:
#NonSteamKAG(NonSteamDirec) commented out, not useful here
break
except:
print "\nInvalid directory. Ensure the directory is correct and try again.\n"
while True:
try:
ClassicDirec = raw_input("\nPlease enter the directory for Classic KAG logs (ends in \Logs).\nEnter 'none' if you do not have Classic KAG: ")
if ClassicDirec.lower() == 'none':
break
else:
#ClassicKAG(ClassicDirec) commented out, not useful here
break
except:
print "\nInvalid directory. Ensure the directory is correct and try again.\n"

Simple reading/writing from/to a USB HID device in Python?

I've got a fairly simple USB HID device that I've been trying to figure out how to read from and write to using Python. I've been able to read from it using PyWinUSB, but the problem comes in when I try to write to it. Trying to write to it makes things explode.
For example:
device = hid.HidDeviceFilter(vendor_id = 0x0003, product_id = 0x1001).get_devices()[0]
This works fine. Then for reading raw data, which is all that I care about right now (I'll work with that once I can figure out how to write to the cursed thing):
def readData(data):
print(data)
return None
This works fine (in fact, it was quite exciting when I got to see it work). So I would assign the data handler like so:
device.set_raw_data_handler(readData)
And every time I hit a button, it's fine. The data comes through as you would expect. Which is great!
The problem comes when I want to write to the device.
Following the sample simple_send file as a template (which was probably not the best choice), I would do the following:
report = device.find_output_reports()[0]
Which would return a report object with a dictionary holding 4 entries. Is that correct? Do you write to a device using the output_reports object? Trying to do so by setting the report value to ANYTHING:
report[<key>] = "pneumonoultramicroscopicvolcanoconiosis"
report.send()
This would keep returning some obnoxious error that I can't interpret:
Traceback (most recent call last):
File "<pyshell#21>", line 1, in <module>
report.send()
File "C:\Python27\lib\site-packages\pywinusb-0.3.1-py2.7.egg\pywinusb\hid\core.py", line 1446, in send
self.__prepare_raw_data()
File "C:\Python27\lib\site-packages\pywinusb-0.3.1-py2.7.egg\pywinusb\hid\core.py", line 1401, in __prepare_raw_data
byref(self.__raw_data), self.__raw_report_size) )
File "C:\Python27\lib\site-packages\pywinusb-0.3.1-py2.7.egg\pywinusb\hid\winapi.py", line 382, in __init__
raise helpers.HIDError("hidP error: %s" % self.error_message_dict[error_code])
HIDError: hidP error: data index not found
I'm using Windows 7. I've managed to find (finally) a reference for the HID DLL exported functions, and I don't HAVE to (or, for that matter even really WANT to) use the PyWinUSB library. I just want to make this work, and it didn't seem like it would be that tough, but it has been.
Can someone tell me what it is I've been doing wrong here?
Thanks.
Also, I tried tracing the error call, and made it so far before the program just closed which was kind of disheartening.
i made it work with this
buffer= [0xFF]*33 # 33 = report size + 1 byte (report id)
buffer[0]=0x0 # report id
buffer[1]=0xFE
buffer[2]=0x00
buffer[3]=0xFF
out_report.set_raw_data(buffer)
out_report.send()
dev.close()
For me worked only this:
report.send([0x70, ..., 0x73 ])
The function call sequence with set_raw_data([0x70, ..., 0x73) and subsequent send() didn't work for me.

TypeError: abspath() takes exactly 1 argument (2 given)

I get this error while running a python script (called by ./waf --run):
TypeError: abspath() takes exactly 1 argument (2 given)
The problem is that it is indeed called with: obj.path.abspath(env).
This is not a python issue, because that code worked perfectly before, and it's part of a huge project (ns3) so I doubt this is broken.
However something must have changed in my settings, because this code worked before, and now it doesn't.
Can you help me to figure out why I get this error ?
Here is the python code: http://pastebin.com/EbJ50BBt. The error occurs line 61.
The documentation of the method Node.abspath() states it does not take an additional env parameter, and I confirmed that it never did by checking the git history. I suggest replacing
if not (obj.path.abspath().startswith(launch_dir)
or obj.path.abspath(env).startswith(launch_dir)):
continue
with
if not obj.path.abspath().startswith(launch_dir):
continue
If this code worked before, this is probably due to the fact that the first operator of the or expression happened to always be True, so the second operator was never executed. It seems to be a bug in your code anyway.
You should have a file name and line number in the traceback. Go to that file and line and find out was "obj" and "obj.path.abspath" are. A simple solution would be to put the offending line in a try/except block to print (or log) more informations, ie:
# your code here
try:
whatever = obj.path.abspath(env)
except Exception, e:
# if you have a logger
logger.exception("oops : obj is '%s' (%s)" % (obj, type(obj)))
# else
import sys
print >> sys.stderr, "oops, got %s on '%s' (%s)" % (e, obj, type(obj))
# if you can run this code directly from a shell,
# this will send you in the interactive debugger so you can
# inspect the offending objet and the whole call stack.
# else comment out this line
import pdb; pdb.set_trace()
# and re-raise the exception
raise
My bet is that "obj.path" is NOT the python 'os.path' module, and that "obj.path.abspath" is a an instance method that only takes "self" as argument.
The problem came from the fact that apparently waf doesn't like symlinks, the python code must not be prepared for such cases.
Problem solved, thanks for your help everybody