How can I make my function handle an exception? - python

I have a function which looks like this
def read_data(input_file_name):
""" (str) -> list, list
Read data from the path specified by the string input_file.
Data looks like:
18, 120
20, 110
22, 120
25, 135
Output two lists, one for each column of data.
"""
try:
with open(input_file_name) as input_file:
lines = input_file.readlines()
n = len(lines)
x = list(range(n))
for i in range(n):
x[i] = lines[i].strip()
a = []
b = []
if x is None:
raise TypeError
a = None
b = None
else:
for i in x:
c,d = i.split(",")
a.append(float(c))
b.append(float(d))
return a,b
except FileNotFoundError:
print("Unable to open and read '{}'".format(input_file_name))
except ValueError:
print("Unable to open and read '{}'".format(input_file_name))
When I make this function to handle the exception it gives me a Type Error.
For example:
e,f = read_data('lol')
Unable to open and read 'lol'
Traceback (most recent call last):
File "<pyshell#7>", line 1, in <module>
e,f = read_data('lol')
TypeError: cannot unpack non-iterable NoneType object
How can I make this function handle an exception if a file doesn't exist and make the function return a tuple of two Nones and output: 'Unable to open and read'?

after you catch the exception, you didn't return anything(it means you return NoneType). But you try to unpack return values(NoneType). So it throws exception.
so avoid this exception, you raise exception again or return a, b again.
except (FileNotFoundError, ValueError) as e:
print("Unable to open and read '{}'".format(input_file_name))
# throw exception again
raise e
or
# outside of try-except
a, b = [], []
try:
# ...
except (FileNotFoundError, ValueError) as e:
print("Unable to open and read '{}'".format(input_file_name))
# return a tuple of empty lists
return a, b

Related

try/except block catching multiple exceptions

I am trying to create a try except that catches 3 errors:
the first one if the user enters the name if the file which is not a txt file,
the second is if the file is empty then an error Is thrown and
the third is for if the file entered does not exist then an error is thrown as well.
I did try to do the first one where an error messages thrown if the file is not a txt file but I am also getting an error for that one saying 'str' object has no attribute 'contains'
try:
file_name = input("Enter file name: ")
assert not file_name.contains(".txt")
except AssertionError:
("Error, must be a txt file")
file_fh = open(file_name)
counter = 0
avg = 0
for line in file_fh:
if not line.startswith("X-DSPAM-Confidence:"): continue
avg += float(line[20:-1].strip())
counter = counter + 1
print("Average spam confidence:", round(avg / counter, 4))
If you want to be able to catch an exception anywhere in a large block of code, the entire thing needs to be within the same try block:
try:
file_name = input("Enter file name: ")
assert ".txt" in file_name, "Error, must be a txt file"
file_fh = open(file_name) # may raise FileNotFoundError
counter = 0
avg = 0
for line in file_fh:
if not line.startswith("X-DSPAM-Confidence:"): continue
avg += float(line[20:-1].strip())
counter = counter + 1
assert counter, "File is empty!"
print("Average spam confidence:", round(avg / counter, 4))
except (AssertionError, FileNotFoundError) as e:
print(e)
In this example, the assert statements will obviously raise an AssertionError with the given message if the condition is not met. The open call will raise FileNotFoundError if the file is missing, so to handle that case all you need to do is make sure your except block includes FileNotFoundError.

Reading a file and converting it into a dictionary

I have this text file:
this is name_of_liquid(string)=amount(int)
liquid1=200
liquid2=20
liquid_X_= empty
liquid_3= 3000
now, the name does not really matter however the amount does. It has to be an int.
If it is any other type beside int the program would raise an exception
Here is my code/ pseudocode:
#opening the file
d={}
try:
dic = {}
with open('accounts.txt') as f:
for line in f:
(key , val) = line.split()
d[key] = int(val)
#except ValueError:
# print('The value for', key,'is', value,' which is not a number!')
the except block is commented because that is my pseudocode and how I planed in handling the
error, but when I run this code without using exception handling, I get an error of 'not enough values to unpack'
Can anyone please help me?
Try this
f = open("acounts.txt", "r")
dict = {}
try:
for line in f:
line = line.split("=")
dict[line[0]] = int(line[1])
except:
print("Invalid value for key.")
You should split the lines with = as delimiter and strip the list to get rid of extra whitespaces.
I personally think that the try catch block should be used while adding elements to the dictionary.
The following code should work for your problem.
d = {}
with open('accounts.txt', 'r') as f:
for line in f:
(key , val) = map(str.strip,line.split("="))
try:
d[key] = int(val)
except ValueError:
print('The value for', key,'is', val,' which is not a number!')

Why ValueError is not displayed

so basically my code is :
def process_contacts(contacts_file):
f = None
sections = []
ret_dic = {}
try:
f = open(contacts_file, 'r')
content = f.readlines()
for line in content:
if "#" in line:
continue
line = line.rstrip("\n")
sections = line.split(',') # section = categories of information on origin file
if sections[1] == '' or len(sections) != 4:
raise ValueError('Invalid input file')
if sections[3] in ret_dic:
if sections[1] not in ret_dic[sections[3]]:
ret_dic[sections[3]].append(sections[1])
else:
ret_dic[sections[3]] = [sections[1]]
except IOError:
print 'Cannot process due to an IO error'
except ValueError:
raise ValueError('Invalid input file')
finally:
if f != None:
f.close()
if sections != []:
return ret_dic
I'm having problem with ValueError raising. I need the ValueError to be raised as an error, but my code just skips over it and I don't understand why, I searched the whole internet about it, but perhaps I just don't get it.
Anyways the csv file that I open is :
Avi,Levi,Kushnir 7,Jerusalem
Moshe,,Hamakabim 4,Tel Aviv
Michael,Cohen,Herzel 70
Eli,Cohen,Haroe 6,Jerusalem
Moti,Cohen,shalom 5,Tel Aviv
It's because of your finally block. You raise your ValueError in the except and then say "oh never mind" and finish out with the finally. finally blocks will always be the last thing that runs if an error is encountered.

how to write an exception in python for giving a none value

say I am reading a json file.
save_list = []
json_path = '../business.json'
js = [json.loads(line) for line in open(json_path)]
for item in js:
save_dict = {}
try:
save_dict['name'] = item['name']
save_dict['neighborhood'] = item['neighborhood']
save_dict['city'] = item['city']
The problem is in json file, some lines have no value for 'city' or 'name', which are noise data, but I don't want to remove them. I want to write a exception that can handle this none value, if there is no value, put a none to the value, the save_dict['city'] = none , same as save_dict['name'] = none. How do I write try: exception:?
In Python, you could write:
for item in js:
save_dict = {}
try:
save_dict['name'] = item['name']
except KeyError:
save_dict['name'] = None
try:
save_dict['city'] = item['city']
except KeyError:
save_dict['city'] = None
save_dict['neighborhood'] = item['neighborhood']
Note, when doing exception handling, it is good practice to wrap the smallest part of the code that could produce the exception in the try-except, and you should always try to explicitely catch an exception, i.e. use
except SomeException:
instead of a bare
except:
However, in this case, you could use the .get method, on your dict object. This will, by default, return None if the key is not present in your dictionary:
for item in js:
save_dict = {}
save_dict['name'] = item.get('name')
save_dict['neighborhood'] = item['neighborhood']
save_dict['city'] = item.get('city')
or even use a dict literal:
for item in js:
save_dict = {'name': item.get('name')
'neighborhood': item['neighborhood']
'city': item.get('city')}
Or perhaps more succinctly, in general you can do:
fields = 'key1','key2','key3','key4','key4','key6'
for item in js:
save_dict = {k:item.get(k) for k in fields}
You could instead make use of an if in case of a none value and raise a TypeError:
if condition is None:
raise TypeError
This may solve the problem.
Please try this:
save_list = []
json_path = '../business.json'
js = [json.loads(line) for line in open(json_path)]
for item in js:
save_dict = {}
try:
save_dict['name'] = item['name']
except Exception as e:
save_dict['name'] = None
try:
save_dict['neighborhood'] = item['neighborhood']
except Exception as e:
save_dict['neighborhood'] = None
try:
save_dict['city'] = item['city']
except Exception as e:
save_dict['city'] = None

How to decode bencoded torrent data

I'm trying to extract size and name from a torrent file with decoding the content of a torrent file with bencode.
I did pip install bencode then I tested with one of the line of a torrent file as you can see there.
import bencode
blabla = 'd8:announce70:http://tracker.t411.io:56969/c5faa6720249d33ff6ba2af48640af89/announce7:comment29:https://www.t411.io/t/524280210:created by19:https://www.t411.io13:creation datei1431685353e4:infod6:lengthi14634059e4:name22:Charlie-Hebdo-1178.pdf12:piece lengthi262144e6:pieces1120:'
myprint = bencode.decode_string(blabla,1)
print myprint
This is the file that pip install put in the python lib:
from BTL import BTFailure
def decode_int(x, f):
f += 1
newf = x.index('e', f)
n = int(x[f:newf])
if x[f] == '-':
if x[f + 1] == '0':
raise ValueError
elif x[f] == '0' and newf != f+1:
raise ValueError
return (n, newf+1)
def decode_string(x, f):
colon = x.index(':', f)
n = int(x[f:colon])
if x[f] == '0' and colon != f+1:
raise ValueError
colon += 1
return (x[colon:colon+n], colon+n)
def decode_list(x, f):
r, f = [], f+1
while x[f] != 'e':
v, f = decode_func[x[f]](x, f)
r.append(v)
return (r, f + 1)
def decode_dict(x, f):
r, f = {}, f+1
while x[f] != 'e':
k, f = decode_string(x, f)
r[k], f = decode_func[x[f]](x, f)
return (r, f + 1)
decode_func = {}
decode_func['l'] = decode_list
decode_func['d'] = decode_dict
decode_func['i'] = decode_int
decode_func['0'] = decode_string
decode_func['1'] = decode_string
decode_func['2'] = decode_string
decode_func['3'] = decode_string
decode_func['4'] = decode_string
decode_func['5'] = decode_string
decode_func['6'] = decode_string
decode_func['7'] = decode_string
decode_func['8'] = decode_string
decode_func['9'] = decode_string
def bdecode(x):
try:
r, l = decode_func[x[0]](x, 0)
except (IndexError, KeyError, ValueError):
raise BTFailure("not a valid bencoded string")
if l != len(x):
raise BTFailure("invalid bencoded value (data after valid prefix)")
return r
from types import StringType, IntType, LongType, DictType, ListType, TupleType
class Bencached(object):
__slots__ = ['bencoded']
def __init__(self, s):
self.bencoded = s
def encode_bencached(x,r):
r.append(x.bencoded)
def encode_int(x, r):
r.extend(('i', str(x), 'e'))
def encode_bool(x, r):
if x:
encode_int(1, r)
else:
encode_int(0, r)
def encode_string(x, r):
r.extend((str(len(x)), ':', x))
def encode_list(x, r):
r.append('l')
for i in x:
encode_func[type(i)](i, r)
r.append('e')
def encode_dict(x,r):
r.append('d')
ilist = x.items()
ilist.sort()
for k, v in ilist:
r.extend((str(len(k)), ':', k))
encode_func[type(v)](v, r)
r.append('e')
encode_func = {}
encode_func[Bencached] = encode_bencached
encode_func[IntType] = encode_int
encode_func[LongType] = encode_int
encode_func[StringType] = encode_string
encode_func[ListType] = encode_list
encode_func[TupleType] = encode_list
encode_func[DictType] = encode_dict
try:
from types import BooleanType
encode_func[BooleanType] = encode_bool
except ImportError:
pass
def bencode(x):
r = []
encode_func[type(x)](x, r)
return ''.join(r)
The fact is that I don't really understand how can I decode my line with this bencode.
I already tried the def bdecode but this is the output:
root#debian:/home/florian/Téléchargements# python decript.py
Traceback (most recent call last):
File "decript.py", line 4, in <module>
myprint = bencode.bdecode(blabla)
File "/usr/local/lib/python2.7/dist-packages/bencode/__init__.py", line 68, in bdecode
raise BTFailure("not a valid bencoded string")
bencode.BTL.BTFailure: not a valid bencoded string
So I tried with the def decode_string but with decode_string(blabla, 1) it decode only the first word:
root#debian:/home/florian/Téléchargements# python decript.py
('announce', 11)
and the number like 2, 3, 4 don't work and display error like:
root#debian:/home/florian/Téléchargements# python decript.py
Traceback (most recent call last):
File "decript.py", line 4, in <module>
myprint = bencode.decode_string(blabla,10)
File "/usr/local/lib/python2.7/dist-packages/bencode/__init__.py", line 29, in decode_string
n = int(x[f:colon])
ValueError: invalid literal for int() with base 10: 'e70'
I want to decode all the line and I don't understand how can I do it with this bencode for example.
The string you're trying to decode seems to be truncated. it ends in pieces1120:, indicating that at least 1120 bytes should follow.
BEncoding is a binary format. It's only partially human-readable and not meant to be embedded in charset-sensitive things such as source code files. I suggest you read it straight from a file.
You have an incomplete Bencoded string.
The first part tells you there is a dictionary:
d...
which is supposed to be parsed until there is an e character. There is no such character in your input string.
A manual parse shows you have the keys announce, comment, created by, creation date, and info, where the latter is a nested dictionary with length, name, piece-length and pieces. Then your string stops; there is no value for pieces, and no e to mark the end of either the outer dictionary or the nested info dictionary. All we have is the type and length indicator: 1120.
You could try and use the decoding functions directly, but then take into account that they return the value and the offset:
>>> bencode.decode_string(blabla, 1)
('announce', 11)
11 is the offset for the next value:
>>> bencode.decode_string(blabla, 11)
('http://tracker.t411.io:56969/c5faa6720249d33ff6ba2af48640af89/announce', 84)
and 84 is again the next:
>>> bencode.decode_string(blabla, 84)
('comment', 93)
If you take into account that the string is incomplete and that not all encoded objects are strings, you can still decode what little is there.
The offset also tells you what function to use for decoding:
>>> blabla[1]
'8'
>>> bencode.decode_func[blabla[1]]
<function decode_string at 0x1004632a8>
The number here spells out how many characters to expect. So skipping the failing d dictionary mapping you get:
>>> offset = 1
>>> while True:
... value, offset = bencode.decode_func[blabla[offset]](blabla, offset)
... print value
...
announce
http://tracker.t411.io:56969/c5faa6720249d33ff6ba2af48640af89/announce
comment
https://www.t411.io/t/5242802
created by
https://www.t411.io
creation date
1431685353
info
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
File "/Users/mj/Development/venvs/stackoverflow-2.7/lib/python2.7/site-packages/bencode/__init__.py", line 44, in decode_dict
while x[f] != 'e':
IndexError: string index out of range
which fails because you hit the nested dictionary without e. You could extract those keys too, by adding one to the last offset:
>>> offset
194
>>> blabla[offset]
'd'
>>> offset += 1
>>> while True:
... value, offset = bencode.decode_func[blabla[offset]](blabla, offset)
... print value
...
length
14634059
name
Charlie-Hebdo-1178.pdf
piece length
262144
pieces
Traceback (most recent call last):
File "<stdin>", line 2, in <module>
IndexError: string index out of range
Or you could just read the data as binary data and not truncate it:
with open(torrentfilename, 'rb') as torrentfile:
torrent = bencode.bdecode(torrentfile.read())
# now you have a dictionary.

Categories