JoshJohnson
JoshJohnson

Reputation: 113

How to properly handle errors while working with files?

Program description:

Program creates a .txt file with the given name from the first input. After that it accepts text lines for the file from each input, until the input only consists of a string "end" (this end line should not be included). The program should also handle all possible errors.

My solution:

def writef(f, st):
    try:
        assert st == "end", "* End of the file (not included)"
        assert not(f.endswith(".txt")), "txt only"
    except IOError:
        print("Unexpected error")
    except AssertionError as sterr:
        print(sterr)
    f.write(st + "\n")

t = input("* Beggining of the file (.txt supported only): ")
f = open(t, "w+")
while True:
    exec_st = input("> ")
    writef(f, exec_st)

Problems:

Traceback (most recent call last):
 File "C:\Users\1\Desktop\IT\pycharm\em_1.py", line 15, in <module>
   writef(f, exec_st)
 File "C:\Users\1\Desktop\IT\pycharm\em_1.py", line 4, in writef
   assert not(f.endswith(".txt")), "txt only"
AttributeError: '_io.TextIOWrapper' object has no attribute 'endswith'

I will appreciate any help, thanks in advance.

Upvotes: 0

Views: 192

Answers (3)

hostingutilities.com
hostingutilities.com

Reputation: 9509

The way assert statements work is that when you say

assert st == "end", "* End of the file (not included)"

You are saying that you assume st is equal to end. If for some reason this isn't true, raise an error. Using != would make the program work as you explained, however, you shouldn't even be using an assert statement here. Assert statements are only meant for sanity checks, and they get stripped out in production. What I mean by that is most companies will run Python in a special optimized mode that skips over assert statements. See this question for more info. Instead raise an error like so:

if st == "end":
    raise RuntimeError("* End of the file (not included)")

That'll take care of making sure your error gets raised when it should, but we still need to take care of the '_io.TextIOWrapper' object has no attribute 'endswith' error. You're checking if f does not end with ".txt". f is whatever open() returns, and if you lookup the documentation to see what that function returns, you'll find that it does not return a string, but the endswith function can only operate on strings, hence the error. What you can do instead is pass t to your writef function and check if that ends with "txt", or you can do as PIG208 mentioned and check if f.name ends with ".txt".


Some other things to consider:

  • You should get in the habit of using more descriptive names. It helps you when you come back to your code later on. I have no idea what t and st stand for, and future you won't know what they stand for either.

  • You should avoid printing out "Unexpected error" whenever you can in favor of a more specific error message. You'll just annoy the user by not telling them what is going on, you'll annoy yourself when your users complain about this very generic error message.

  • Your try catch block is around some assert statements that aren't doing any IO work, but your catching an IO error anyways. This isn't necessary. If you get an IO error, it's going to come from f.write or open or f.close.

Upvotes: 1

PIG208
PIG208

Reputation: 2370

The endswith function only works with strings. If you want to check whether the input file is legal, you may do it by checking f.name.

The file extension will not be checked until writef is called, which doesn't make sense because the file name is already given in t = input("* Beggining of the file (.txt supported only): ")

t = input("* Beggining of the file (.txt supported only): ")
f = open(t, "w+")
assert f.name.endswith(".txt"), "txt only"

The code above raises an AssertionError if the file name doesn't end with ".txt".

assert st == "end", "* End of the file (not included)" in the try block raises an exception to output the AssertionError every time the user types in anything other than "end". Instead of checking it in writef, you may want to do it in the while loop.

while True:
    exec_st = input("> ")
    if exec_st == "end":
        f.close()
        break
    writef(f, exec_st)

The code above breaks the loop as soon as a string containing only "end" is entered, and it will not be written into the output file.

Here is the complete solution:

def writef(f, st):
    try:
        f.write(st + "\n")
    except IOError:
        print("Unexpected error")

t = input("* Beggining of the file (.txt supported only): ")
f = open(t, "w+")
assert f.name.endswith(".txt"), "txt only"
while True:
    exec_st = input("> ")
    if exec_st == "end":
        f.close()
        break
    writef(f, exec_st)

Upvotes: 1

Harvey
Harvey

Reputation: 114

In order to check the file name, i would recommend you use regex, i'd recommend reading up on it because it makes no sense until you do.

for the "end" case, i believe you want to have f.close() rather than f.close

if you don't want to use regex, you can check if the filename string contains ".txt", which will fix the majority of cases.

Upvotes: 0

Related Questions