Sunday, October 9, 2022

Python - Exceptions and JSON (Day 30)

This is a 100 Days challenge to learn a new language (Python). 100 Days of Code - The Complete Python Pro Bootcamp 

I will post some notes to motivate myself to finish this challenge.


Exceptions



When exceptions occur, python program will stop and show some error messages.

There are some normal exceptions below.


FileNotFound



Ex: 

with open("no_such_file.txt") as file:
    print(file.read())

Result:
FileNotFoundError:
[Errno 2] No such file or directory: 'no_such_file.txt'


KeyError



Ex: 
my_dict = {"key1": "value1", "key2": "value2"}
print(my_dict["no_such_key"])

Result:
KeyError: 'no_such_key'


IndexError



Ex: 
my_list = [1, 2, 3]
print(my_list[4])

Result:
IndexError: list index out of range


TypeError



Ex: 
my_str = "hello"
print(my_str + 123)

Result:
TypeError: can only concatenate str (not "int") to str


How to catch Exceptions



Using the following keywords:

        try:
            Something that might cause an exception.

        except:
            Do this if there was an exception.

        else:
            Do this if there were no exceptions.

        finally:
            Do this no matter what happens.


Ex: 
try:
    file = open("no_such_file.txt")
except:
    print("except")
    file = open("no_such_file.txt", "w")
    file.write("new created file")
else:
    print("else")
    print(file.read())
finally:
    print("finally")
    file.close()

Result (first run):
except
finally

Result (second run):
else
new created file
finally


Our own exceptions?



We can utilize 'raise' statement and some built-in exception to create our own exceptions.

Take the following exp as an example, there is no syntax errors or exceptions if users enter their height larger than 300. But it does not make sense to be an input parameter as human's height.

Ex:

height = float(input("Your height in cm: "))
weight = float(input("Your weight in kg: "))

# Normal Human Being cannot be higher than 300cm
if height > 300:
    raise ValueError("Height should not be larger than 300")

bmi = weight / (height / 100) ** 2

print(f"Your bmi is {weight / (height / 100) ** 2}")


Challenging - Revise NATO Alphabet Project



Catch exceptions if users enter non-alphabet characters.


Ex:
import pandas

# Using pandas to read csv file
nato_phonetic_alphabet_data_frame =
pandas.read_csv("nato_phonetic_alphabet.csv")

# Generate a dictionary to use alphabet as a key and relating word
as a value
# {"A": "Alfa", "B": "Bravo"}
nato_phonetic_alphabet_dict = {
    row["letter"]: row["code"]
    for (index, row) in nato_phonetic_alphabet_data_frame.iterrows()
}


""" Generate phonetic alphabet result based on the user input """
def generate_phonetic_alphabet_result():

    # Ask users to enter a word
    user_input = input("Enter a word? ")

    try:
        # Use List Comprehension to create a result list
        # Loop through all letters of user input and get
the mapping word by dictionary
        result = [nato_phonetic_alphabet_dict[letter] for letter in
user_input.upper()]
    except KeyError:
        # If users enter non-alphabet characters, then we cannot find it
from the nato_phonetic_alphabet_dict dictionary
        # And program will throw KeyError Exception
        print("Sorry, only letters in the alphabet please")

        # In this case, we need to call the self function again
        generate_phonetic_alphabet_result()
    else:
        print(result)


# Call this function to ask user input and then get relating results
generate_phonetic_alphabet_result()



JSON



JSON, JavaScript Object Notation.

Format: (look like python dictionary)
  {
      key: value,
      key: value,
  }

Ex (Write):

import json

# Write
with open("data.json", "w") as file:
    json.dump({"key1": "value1"}, file, indent=4)


Result:
// data.json
  {
      "key1": "value1"
  }

Ex (Read):

import json

# Load
with open("data.json", "r") as file:
    print(json.load(file))

Result:
{'key1': 'value1'}

Ex (Update):

import json

# Update
# Read it frist
with open("data.json", "r") as file:
    # Load JSON file first
    data = json.load(file)
    # Use update to add more data
    data.update(
        {
            "key2": {
                "inner_key1": "innver_value1",
                "inner_key2": "innver_value2",
            }
        }
    )
# Write it back
with open("data.json", "w") as file:
    # Use dump function to write the updated data back to the file
    json.dump(data, file, indent=4)

Result:
// data.json
  {
     "key1": "value1",
          "key2": {
              "inner_key1": "innver_value1",
              "inner_key2": "innver_value2"
      }
  }


Challenging - Revise Password Manager





#1 Save file from txt to json.
#2 Catch Exceptions
#3 Add Search function

Ex:
import random
import tkinter as tk
from tkinter import messagebox
import pyperclip
import json


def find_password():
    """Find password from data.json"""
    website = website_entry.get()

    # Validation
    if len(website) == 0:
        messagebox.showerror(
            title="Info",
message="Please make sure to enter website field"
        )

        return

    try:
        # Load data from data.json
        with open("data.json", encoding="utf-8", mode="r") as file:
            data = json.load(file)
    except FileNotFoundError:
        messagebox.showerror(title="Info", message="No Data File Found")
    else:
        # Continue from try clause
        # If we can find website from data.json
        if website in data:
            websiteObj = data[website]
            messagebox.showinfo(
                title=website,
                message=f"Email:{websiteObj['email']}\nPassword:
{websiteObj['pwd']}",
            )
        else:
            messagebox.showerror(
                title="Info",
message=f"No details for the {website} exists"
            )


def copy_to_clipboard(message):
    """Use pyperclip library to copy message to clipboard"""
    pyperclip.copy(message)


def generate_password():
    """Generate random password and show it to GUI"""
    letters = [
        "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
        "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
    ]
    numbers = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
    symbols = ["!", "#", "$", "%", "&", "(", ")", "*", "+"]

    password_letter_list = [
        random.choice(letters) for _ in range(random.randint(8, 10))
    ]
    password_symbols_list = [
        random.choice(symbols) for _ in range(random.randint(2, 4))
    ]
    password_numbers_list = [
        random.choice(numbers) for _ in range(random.randint(2, 4))
    ]
    password_list = password_letter_list + password_symbols_list +
password_numbers_list
    random.shuffle(password_list)

    password = "".join(password_list)
    pwd_entry.insert(0, password)
    copy_to_clipboard(password)


def save():
    """Save user info to the file system"""
    website = website_entry.get()
    username = username_entry.get()
    pwd = pwd_entry.get()

    # Form Validation
    if len(website) == 0 or len(username) == 0 or len(pwd) == 0:
        messagebox.showerror(
            title="Info", message="Please make sure to enter all fields"
        )

        return

    # Pop-up confirmation
    is_ok = messagebox.askokcancel(
        title=website,
        message=f"Is that ok to save {username} / {pwd} ?",
    )

    if is_ok:
        try:
            # Open a file with r mode
            # And load it
            with open("data.json", encoding="utf-8", mode="r") as file:
                data = json.load(file)
        except FileNotFoundError:
            # If there is a file not found exception
            # Then, open a file with w mode
            # And dump to json file directly since it will be the
# first record
            with open("data.json", encoding="utf-8", mode="w") as file:
                json.dump({website: {"email": username, "pwd": pwd}},
file, indent=4)
        else:
            # Continue from try clause
            # Update python object data with the new record
            data.update({website: {"email": username, "pwd": pwd}})

            # Open a file with w mode
            # And write updated python object back to json file
            with open("data.json", encoding="utf-8", mode="w") as file:
                json.dump(data, file, indent=4)
        finally:
            # Clear User Input
            website_entry.delete(0, tk.END)
            pwd_entry.delete(0, tk.END)


# Init
root = tk.Tk()
root.title("Password Manager")
root.config(padx=50, pady=50)

# Init canvas
canvas = tk.Canvas(width=200, height=200)
logo_image = tk.PhotoImage(file="logo.png")
canvas.create_image(100, 100, image=logo_image)
canvas.grid(column=1, row=0)

# Website
website_label = tk.Label(text="Website:")
website_label.grid(column=0, row=1)
website_entry = tk.Entry()
website_entry.grid(sticky="ew", padx=[0, 5], column=1, row=1)
# Focus entry
website_entry.focus()

# Search
search_btn = tk.Button(text="Search", command=find_password)
search_btn.grid(sticky="ew", padx=[5, 0], column=2, row=1)

# Email/Username
username_lable = tk.Label(text="Email/Username:")
username_lable.grid(column=0, row=2)
username_entry = tk.Entry()
username_entry.grid(sticky="ew", column=1, row=2, columnspan=2)
username_entry.insert(0, "frank@demo.com")

# Password
pwd_lable = tk.Label(text="Password:")
pwd_lable.grid(column=0, row=3)
pwd_entry = tk.Entry()
pwd_entry.grid(sticky="ew", padx=[0, 5], column=1, row=3)

# PWD Generation Btn
generate_pwd_btn = tk.Button(text="Generate Password",
command=generate_password)
generate_pwd_btn.grid(sticky="ew", padx=[5, 0], column=2, row=3)

# Add Btn
add_btn = tk.Button(text="Add", command=save)
add_btn.grid(sticky="ew", column=1, row=4, columnspan=2)


# Start the Event Loop
root.mainloop()


No comments:

Post a Comment