How can i catch any error caused by unset module variable? - python-3.x

So I have this function list and showlist but i need to catch any error caused by an unset module variable. can someone help me whats the first thing i should do?
this is my code at the moment
import csv
filep #filepath
menulist = []
def list():
"""Function to read the csv file, create a nested list
and return the list that is sorted based on calories
in the ascending order."""
global menulist
menulist = [] #store items
with open(filep) as csv_file: #read file
reader = csv.reader (csv_file, delimiter=',')
next(reader, None)
for row in reader:
row[2] = int(row[2].strip())
row[1] = float(row[1].strip())
if row[2]> 100 and row[2] <200:
menulist.append(row)
menulist.sort(key=lambda x: x[-1])
def show_list():#Function to display menu
global menulist
for i in range(len(menulist)):
print ('%-4d%-20s%-15s%-15s' %(i + 1, menulist[i][0], menulist[i][2], menulist[i][1]))
list()
show_list()
for example, if the variable file is not set before the list() is called, the function needs to catch an error and prints an appropriate comment

You are using a built-in function name as your function name. Which is not considered as a good practice in Python. It replaces the built-in function list() which is used to create a list. And you need to define a variable before you can use it.
Here's how you can catch the error and print an appropriate comment, with the variable defined:
import csv
filep = str(input("Enter the file name with path : ")) # filepath
menulist = []
def calorieList():
"""Function to read the csv file, create a nested list
and return the list that is sorted based on calories
in the ascending order."""
global menulist
menulist = [] # store items
with open(filep) as csv_file: # read file
reader = csv.reader(csv_file, delimiter=",")
next(reader, None)
for row in reader:
row[2] = int(row[2].strip())
row[1] = float(row[1].strip())
if row[2] > 100 and row[2] < 200:
menulist.append(row)
menulist.sort(key=lambda x: x[-1])
def show_list(): # Function to display menu
global menulist
for i in range(len(menulist)):
print("%-4d%-20s%-15s%-15s" % (i + 1, menulist[i][0], menulist[i][2], menulist[i][1]))
try:
calorieList()
except FileNotFoundError:
print("Enter a valid file path.")
show_list()

I assume you meant filep by the file variable.
If you try to access filep before it is set, your program should raise NameError: name 'filep' is not defined.
But if you want to raise a custom error message, you can just use a try-except block as follows:
import csv
filep #filepath
menulist = []
def list():
"""Function to read the csv file, create a nested list
and return the list that is sorted based on calories
in the ascending order."""
global menulist
menulist = [] #store items
try:
with open(filep) as csv_file: #read file
reader = csv.reader (csv_file, delimiter=',')
next(reader, None)
for row in reader:
row[2] = int(row[2].strip())
row[1] = float(row[1].strip())
if row[2]> 100 and row[2] <200:
menulist.append(row)
except NameError:
raise ValueError("Your custom message here")
menulist.sort(key=lambda x: x[-1])
def show_list():#Function to display menu
global menulist
for i in range(len(menulist)):
print ('%-4d%-20s%-15s%-15s' %(i + 1, menulist[i][0], menulist[i][2], menulist[i][]))
list()
show_list()

Related

Is there any method to append the value to the list in JSON file (Python)?

My exercise idea is to append new value from the input prompt to the list. Instead of adding new value, my list in JSON file always give the newly inputted value.
import json
file = 'database.json'
namelist = []
name = input("Enter your name:\n")
with open(file, 'w') as file_object:
namelist.append(name)
json.dump(namelist, file_object)
with open('database.json', "r+") as file_object:
data = json.load(file)
# data.update(your_dict) or data.append(new_data)
file.seek(0)
json.dump(data, file)

How to determine the number of columns per row variably in a CSV File with Python?

I am analyzing xml-structured Textfiles about insider dealings. I wrote some code to parse through the XML-structure and write my output in a CSV file. The results of the files are written per line and the analyzed information is written in individual columns. But in some files information is present in multiple times and my code override the information in the cells, in the end only one date is in the cell of my CSV-File.
import csv
import glob
import re
import string
import time
import bs4 as bs
# User defined directory for files to be parsed
TARGET_FILES = r'D:\files\'
# User defined file pointer to LM dictionary
# User defined output file
OUTPUT_FILE = r'D:\ouput\Parser.csv'
# Setup output
OUTPUT_FIELDS = [r'Datei', 'transactionDate', r'transactionsCode', r'Director', r'Officer', r'Titel', r'10-% Eigner', r'sonstiges', r'SignatureDate']
def main():
f_out = open(OUTPUT_FILE, 'w')
wr = csv.writer(f_out, lineterminator='\n', delimiter=';')
wr.writerow(OUTPUT_FIELDS)
file_list = glob.glob(TARGET_FILES)
for file in file_list:
print(file)
with open(file, 'r', encoding='UTF-8', errors='ignore') as f_in:
soup = bs.BeautifulSoup(f_in, 'xml')
output_data = get_data(soup)
output_data[0] = file
wr.writerow(output_data)
def get_data(soup):
# overrides the transactionDate if more than one transactions disclosed on the current form
# the number determine the column for the output
_odata = [0] * 9
try:
for item in soup.find_all('transactionDate'):
_odata[1] = item.find('value').text
except AttributeError:
_odata[1] = ('keine Angabe')
try:
for item in soup.find_all('transactionAcquiredDisposedCode'):
_odata[2] = item.find('value').text
except AttributeError:
_odata[2] = 'ka'
for item in soup.find_all('reportingOwnerRelationship'):
try:
_odata[3] = item.find('isDirector').text
except AttributeError:
_odata[3] = ('ka')
try:
_odata[4] = item.find('isOfficer').text
except AttributeError:
_odata[4] = ('ka')
try:
_odata[5] = item.find('officerTitle').text
except AttributeError:
_odata[5] = 'ka'
try:
_odata[6] = item.find('isTenPercentOwner').text
except AttributeError:
_odata[6] = ('ka')
try:
_odata[7] = item.find('isOther').text
except AttributeError:
_odata[7] = ('ka')
try:
for item in soup.find_all('ownerSignature'):
_odata[8] = item.find('signatureDate').text
except AttributeError:
_odata[8] = ('ka')
return _odata
if __name__ == '__main__':
print('\n' + time.strftime('%c') + '\nGeneric_Parser.py\n')
main()
print('\n' + time.strftime('%c') + '\nNormal termination.')
Actually the code works, but overwrites columns if, for e.g. more than one transacion date is given in the file. So I need a code that automatically uses the next column for each transaction date. How could this work?
I would be glad if someone have a solution for my problem. Thanks a lot!
Your issue is that you are iterating over the result of
soup.find_all()
and every time you are writing to the same value. You need to do something with
_odata in each iteration, otherwise you will only end up with whatever is written to it the last time.
If you can show us what the data you're trying to parse actually looks like, perhaps we could give a more specific answer.

Python object serialization

I am trying to mimic a dict by using that as the base class. The objective is to meet these conditions:
If 2 arguments on the command line, set a key and value in the object's dictionary;
if 1 argument on the command line, treat it as a key and show the value; if no arguments on the command line, show all keys and values.
Here is my code:
import pickle,os,sys
class ConfigDict(dict):
def __init__(self, filename):
self._filename = filename
if not os.path.exists(self._filename):
with open(self._filename,"wb") as fh:
pickle.dump({}, fh)
with open(self._filename,"rb") as fh:
self.update(pickle.load(fh))
def __setitem__(self, key, value):
dict.__setitem__(self,key,value)
with open(self._filename,"wb") as fh:
pickle.dump(self, fh)
def __getitem__(self,key):
return dict.__getitem__(self,key)
cd = ConfigDict('first.pickle')
# if 2 arguments on the command line,
# set a key and value in the object's dictionary
if len(sys.argv) == 3:
key, value = sys.argv[1], sys.argv[2]
print('writing data: {0}, {1}'.format(key, value))
cd[key] = value
# if 1 argument on the command line, treat it as a key and show the value
elif len(sys.argv) == 2:
print('reading a value')
key = sys.argv[1]
print('the value for {0} is {1}'.format(sys.argv[1], cd[key]))
# if no arguments on the command line, show all keys and values
else:
print('keys/values:')
for key in cd.keys():
print(' {0} = {1}'.format(key, cd[key]))
I am able to write to the file, however, when i try to retrive the value for a given key, i hit the error (only the end of stack trace shown):
with open(self._filename,"wb") as fh:
AttributeError: 'ConfigDict' object has no attribute '_filename'
But, i already set the _filename in __init__. What am i missing ?
Well, this is a tricky one - the problem seems to be with
pickle.load(fh) and NOT with self.update(
try this in two lines
...
with open(self._filename,"rb") as fh:
tmp = pickle.load(fh)
self.update(tmp)
...
this would fail at tmp =, so it's the object you're un-pickling that's failing. An easy fix would be to do pickle.dump(dict(self), fh), when serialising your things. Though this whole approach seems "forced" to me. Fully working version:
import pickle,os,sys
class ConfigDict(dict):
def __init__(self, filename):
self._filename = filename
if not os.path.exists(self._filename):
with open(self._filename,"wb") as fh:
pickle.dump({}, fh)
with open(self._filename,"rb") as fh:
self.update(pickle.load(fh))
def __setitem__(self, key, value):
dict.__setitem__(self,key,value)
with open(self._filename,"wb") as fh:
pickle.dump(dict(self), fh)
def __getitem__(self,key):
return dict.__getitem__(self,key)
cd = ConfigDict('first.pickle')
# if 2 arguments on the command line,
# set a key and value in the object's dictionary
if len(sys.argv) == 3:
key, value = sys.argv[1], sys.argv[2]
print('writing data: {0}, {1}'.format(key, value))
cd[key] = value
# if 1 argument on the command line, treat it as a key and show the value
elif len(sys.argv) == 2:
print('reading a value')
key = sys.argv[1]
print('the value for {0} is {1}'.format(sys.argv[1], cd[key]))
# if no arguments on the command line, show all keys and values
else:
print('keys/values:')
for key in cd.keys():
print(' {0} = {1}'.format(key, cd[key]))

AttributeError: 'WebElement' object has no attribute 'extract_first'

I am trying to run the script below to extract the tags from a webpage and save them into a csv file.
In details, I want to extract the tags associated to a class name.
However, I come across this error: AttributeError:
'WebElement' object has no attribute 'extract_first'.
The script is the following:
import csv
from selenium import webdriver
from time import sleep
from parsel import Selector
from selenium.webdriver.common.keys import Keys
from collections import defaultdict
from selenium.webdriver.support.select import Select
####### reading from the input file ##########
columns = defaultdict(list) # each value in each column is appended to a list
# get the list of keywords from the csv file
with open('query.csv', 'r') as csvfile:
reader = csv.DictReader(csvfile) # read rows into a dictionary format
for row in reader: # read a row as {column1: value1, column2: value2,...}
for (k, v) in row.items(): # go over each column name and value
columns[k].append(v) # append the value into the appropriate list
# the list containing all of the keywords
search_query_list = columns['Keyword']
########## start scraping ###############
rb_results = []
# create a driver and let it open google chrome
driver = webdriver.Chrome("chromedriver")
# get website
driver.get('https://www.redbubble.com/')
sleep(0.5)
for i in range(len(search_query_list)):
next_query = search_query_list[i]
# get RB website
driver.get('https://www.redbubble.com/')
# get the search by its id
search_bar = driver.find_element_by_name("query")
sleep(0.5)
# enter the query to the search bar
search_bar.send_keys(next_query)
# press enter
search_bar.send_keys(Keys.RETURN)
sleep(1)
# from parsel's selector get the page source
sel1 = Selector(text=driver.page_source)
sleep(0.5)
# get first shirt //
continue_link = driver.find_element_by_class_name('shared-components-ShopSearchSkeleton-ShopSearchSkeleton__composedComponentWrapper--1s_CI').click()
sleep(1)
sel2 = Selector(text=driver.page_source)
sleep(0.5)
################## get TAGS ###############
# Check tags for all products
try:
# get the tags for the search query
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").extract_first()
tags_rb = str(tags_rb)
# if number of products is found print it and search for the prime
# print the number of products found
if tags_rb == None:
rb_results.append("0")
else:
#rb_results = str(tags_rb)
rb_results.append(tags_rb)
except ValueError:
pass
#except:
#rb_results.append("errore")
###### writing part ########
with open ("rb_results.csv","w", newline='') as resultFile:
writer = csv.DictWriter(resultFile, fieldnames=["Rb Results"],delimiter='\t')
writer.writeheader()
writer.writerows({'Rb Results': item} for item in rb_results)
resultFile.close()
Any ideas about how to fix it and extract the text of shared-components-Tags-Tags__listContent--oLdDf ? Many thanks!!!
If I right understand, you want an element text. So you can do it like this:
replace:
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").extract_first()
with:
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").text
You are getting this error:
'WebElement' object has no attribute 'extract_first'.
because WebElement does not have method .extract_first().
PS: you don't need this:
tags_rb = str(tags_rb)
The code block to replace is:
# Check tags for all products
try:
# get the tags for the search query
tags_rb = driver.find_element_by_class_name("shared-components-Tags-Tags__listContent--oLdDf").text # get text
# tags_rb = str(tags_rb) no need in this line
# if number of products is found print it and search for the prime
# print the number of products found
if tags_rb == None:
rb_results.append("0")
else:
#rb_results = str(tags_rb)
rb_results.append(tags_rb)
except ValueError:
pass

How to print all the highest value places

I cannot figure out how to get python to print all the highest values as it only prints the first one it encounters.
It takes standard input from a file that has on a few lines the following:
89 Michael Dunne (grade name)
I know I can use the zip function but I cannot figure out how only print the name from it
If I add "highstudents = sorted(zip(grade,name),reverse=True)" it sorts from high to low but I do not know how to filter the name out as it prints as "(89, 'Pepe')"
The code below is the following attempt so far.
import sys
def topgrade(x):
s = max(x)
return s
def main():
s = sys.argv[1]
grade=[]
name = []
try:
with open(s,'r') as studata:
for line in studata:
try:
line = line.strip()
grade.append(int(line[0:2]))
name.append(line[3::])
except ValueError:
print("Invalid mark",line[0:2],"encountered. Skipping.")
top = topgrade(grade)
a = grade.index(top)
print("Best students:",name[a])
print("Best mark:",top)
except FileNotFoundError:
print("File not found:",s)
if __name__ == '__main__':
main()
Rather than trying to keep the students and marks in 2 separate lists (with the risk that they get out of step) it is better to use a dictionary - where the key is the mark and the value is a list of the student(s) who obtained that mark.
Then it is a simple task of just printing out the highest key, and the associated list of students. I'm using defaultdict as an easier option than having to create or append to the list for each value.
from collections import defaultdict
import sys
def main():
s = sys.argv[1]
grades = defaultdict(list)
try:
with open(s,'r') as studata:
for line in studata:
try:
line = line.strip()
grades[int(line[0:2])].append(line[3::])
except ValueError:
print("Invalid mark",line[0:2],"encountered. Skipping.")
top_mark = max(grades.keys())
print("Best students:{}".format(','.join(grades[top_mark])))
print("Best mark: {}".format(top_mark))
except FileNotFoundError:
print("File not found:",s)
if __name__ == '__main__':
main()

Resources