Refactor two similar functions into one? [closed] - python

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I have this very simple sign up/log in program for learning purpose, and it works. But it feels like I have code that repeats itself. The must obvious is the check functions.
My question is, should I refactor those two so they become one or is it better to keep them seperate?
def signUp():
username = input("Give me a username: ")
if checkUser(username) == True:
print("You are already registrered, please log in with your password.")
else:
password = input("Also give me a password: ")
with open("sign-up.csv", "a", newline="") as file:
writer = csv.writer(
file, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
)
writer.writerow([username, password])
print("You are now signed up. Please log in with your credentials.")
def logIn():
username = input("Give me your username: ")
password = input("Also give me your password: ")
if checkPassword(username, password) == True:
print("Welcome, you are now logged in.")
else:
print("Username or password is incorrect please try again.")
def checkUser(username):
with open("sign-up.csv", "r") as file:
reader = csv.reader(file)
myList = dict(reader)
if username in myList:
return True
else:
return False
def checkPassword(username, password):
with open("sign-up.csv", "r") as file:
reader = csv.reader(file)
myList = dict(reader)
if username in myList and password == myList[username]:
return True
else:
return False
def get_user_choice():
print("\n[1] Sign up")
print("[2] Log in")
print("[q] Quit")
return input("What would you like to do? ")
choice = ""
while choice != "q":
choice = get_user_choice()
if choice == "1":
signUp()
elif choice == "2":
logIn()
elif choice == "q":
print("Welcome back some other day")
else:
print("That choice doesn't exists")

Function checkUser is checking if a username is already present in the csv file. This would happen at signup. The function checkPassword is used when the user is signing in. These functions should stay seperate since they do dramaticly different things with different levels of security concerns. They also expect input based on where the user is in the procces of signup/login. Meaning when you write a function that does both like doBoth(username, password) you have to call this function with a null when you wanna use it at the signup fase in the application doBoth(username, null) since password is never known at signup.

The first obvious factorisation is the common part of both functions - the part tha reads the csv file into a dict:
def read_users():
with open("sign-up.csv", "r") as file:
reader = csv.reader(file)
return dict(reader)
Then you can rewrite check_user and check_password with this function:
def check_user(username):
users = read_users()
return username in users
def check_password(username, password):
users = read_users()
# make sure we work correctly even if
# someone passes `None` as password
_notfound = object()
return users.get(username, _notfound) == password
FWIW, those functions would be better named as (resp.) 'user_exists' and 'authenticate'
Also, you may want to factor out the part that's writing to the csv file - not to reduce code duplication, but to better separate the UI / domain / persistance layers.
def add_user(username, password):
with open("sign-up.csv", "a", newline="") as file:
writer = csv.writer(
file, delimiter=",", quotechar='"', quoting=csv.QUOTE_MINIMAL
writer.writerow([username, password])
def sign_up():
username = input("Give me a username: ")
# note how good naming makes code much more explicit
if user_exists(username):
print("You are already registrered, please log in with your password.")
return # no need to go further
password = input("Also give me a password: ")
add_user(username, password)
def log_in():
username = input("Give me your username: ")
password = input("Also give me your password: ")
if authenticate(username, password):
print("Welcome, you are now logged in.")
return
# oops...
print("Username or password is incorrect please try again.")
Next step would be to replace the input() calls by dedicated ask_username() and ask_password() functions that will validate the user's input. First write them as simply as possible, then find out the common part(s) and see if you can factor them out.
Note that I renamed your functions in all_lower - this is the official coding convention, and Python users tend to strongly adhere to the official coding conventions.
Also note that I removed the useless == True tests. In Python, any expression resolves to an object (in the case of a function call, to the object returned by the function), and every object has a boolean value, so if someexpression == True: is redundant at best. FWIW this is also part of pep8 (official coding conventions). And finally, when you find yourself writing something like:
if someexperession:
return True
else:
return False
You can just simplify it to
return someexpression

Try this:
def checkBoth(username, password):
with open("sign-up.csv", "r") as file:
reader = csv.reader(file)
myList = dict(reader)
if username in myList:
u = True
if password == myList[username]:
p = True
else:
p = False
else:
u = False
p = False
return (u,p)

def checkPassword(username, password):
out = [False, False]
with open("sign-up.csv", "r") as file:
reader = csv.reader(file)
myList = dict(reader)
if username in myList:
out[0] = True
if password == myList[username]:
out[1] = True
return out
and then having a check what is true on the out.

Related

python json keeps appending same key and value

import json
def write_json(data, file='users.json'):
with open(file, 'w') as f:
json.dump(data, f, indent=4)
while True:
user = {'name':[], 'password':[]}
choice = int(input('1) Register, 2) Login\n>> '))
if choice == 1:
username = input('Enter username: ')
password = input('Enter password: ')
user['name'] = username
user['password'] = password
print('Registered successfully')
with open('users.json') as json_file:
data = json.load(json_file)
users = data['users']
for user in users:
if user['name'] == username:
print(f'User "{username}" already exists')
break
new_user = user
users.append(new_user)
write_json(data)
if choice == 2:
username = input('Enter username: ')
password = input('Enter password: ')
with open('users.json', 'r') as f:
data = json.load(f)
for user in data['users']:
if user['name'] == username and user['password'] == password:
print('Logged in succesfully')
I am trying to make a simple login/register system, but when the user registers for the 2nd time, its gets overridden by the 1st key/value every time, I tried user.clear() but it doesnt seem to have an effect
The issue is that you are using a single dict item for all your users. The way you've set it up only allows for one user to exist.
You need to restructure your dict. You could do a list of dict items, but I would suggest using the username as the key in your dict. Since usernames are supposed to be unique, this makes sense IMHO.
In case you want to use the simpler list of dict items metioned above, you would structure it as follows:
[
{'Edo': 'mypassword'},
{'Iso': 'yourpassword'}
]
I've added some comments on the adjusted code below...
import json
def write_json(data, file="users.json"):
with open(file, "w") as outfile:
json.dump(data, outfile, indent=4)
def load_json(file="users.json"):
# try block in case file doesn't exist
try:
with open(file) as infile:
result = json.load(infile)
return result
except Exception as e:
# just printing out the error
print(e)
# should only be file not found error
# returning an empty dict
return {}
while True:
# you need to load before actually doing anything.
# if you don't you might overwrite the file
userlist = load_json()
# newlines for each option
choice = int(input("1) Register\n2) Login\n>> "))
if choice == 1:
username = input("Enter username: ")
# check if user already exists before requesting password
# since usernames are supposed to be unique, you can just
# create a dict with the key being username.
# you could use the value directly for password, but
# if you need to store more values for a user, I advice
# you use another dict as the value.
if username in userlist:
print(f"User {username} already exists")
# do some other magic here to handle this scenario
# continue makes the while loop go to the next iteration
continue
password = input("Enter password: ")
userlist[username] = {"password": password, "someotheruserdate": "dunno?"}
write_json(userlist)
# only print the success **after** you've actually
# completed all relevant logic.
print("Registered successfully")
# change this to elif instead of a second if statement
elif choice == 2:
username = input("Enter username: ")
password = input("Enter password: ")
if username in userlist and userlist[username]["password"] == password:
print("Logged in succesfully")
else:
# handle wrong username/password
# here you need to check after getting both username&password
print("Incorrect username/password combination")

Python program to register accounts an log in

So I'm making a program where I need a user to log in or register. The registered account goes to a .txt file from which I'm supposed to read the data to log in again.
I managed to get the basics working. I can register a new account to the file and I can log in with every account I've created, but I can't seem to get 2 important elements working. The first one is for when the user inserts an inexistent username/ password (in this case the program just does nothing as I can't figure out a condition to make it go back to asking the username and password), and the second one is for when I insert a username and password that don't match. Here the program goes back and asks for them again but then keeps asking, even if I put them correctly.
Here's my function if anyone's interested in having a look at it:
def ent():
util = False
ppass = False
login = False
while not login:
n_util = input("Introduce your username: ")
password = input("Introduce your password: ")
with open("dadoscontas.txt", "r") as f:
while not util:
vski = 0
for line in f:
vski += 1
if vski == 1:
if line.strip() == n_util:
util = True
else:
break
if vski == 2:
if line.strip() == password and user:
ppass = True
if user and ppass:
login = True
print("Logged in")
I've spent my whole afternoon trying different things to see if I can get these 2 things to work, but I can't. As I said, the function above is the part that kinda works, and if anyone could give any suggestions / point me in the right direction it would be really helpful. Thank you in advance.
Does this code cover your needs?
def ent():
util = False
login = False
while not login:
n_util = input("Introduce your username: ")
password = input("Introduce your password: ")
with open("some_test.txt", "r") as f:
vski = 0
for line in f:
vski += 1
if vski%2:
if line.strip() == n_util:
util = True
elif util:
if line.strip() == password:
login = True
else:
util = False
print("Logged in")
Or you even could exit the function with return in if line.strip() == password: block.
But i would recommend you to store the file content to dictionaries (user_name:passwor),
because you are parsing the whole file again and again while login=False:
def ent():
login = False
name=""
my_data = {}
with open("some_test.txt", "r") as f:
index = 0
for line in f:
index += 1
if index%2:
name = line.strip()
else:
my_data[name] = line.strip()
while not login:
n_util = input("Introduce your username: ")
password = input("Introduce your password: ")
if n_util in my_data and my_data[n_util] == password:
login = True
print("Logged in")
If you use python2 you can use .get() or try instead of n_util in my_data for better performance.

How to append to csv file before script is done running?

I am currently making a log in system which stores usernames and passwords in a text file.
This is my code:
import csv
Brugere = open("D:\Filer\Programmering/Profiles.txt","r+",newline="\n")
writer = csv.writer(Brugere, delimiter=",")
print("Welcome to the chat app!")
Brugernavn = "" //means username
Kodeord = "" // means password
def Signup(j,k):
print("Welcome to sign up!\n Please enter your Username")
j = input("")
if j not in Brugere:
print("Hello, " + j + ", please enter a password:")
k = input("")
line1 = [j,k]
writer.writerow(line1)
print("Great! Now you can sign in")
Signin(Brugernavn, Kodeord)
else:
print("Username already taken! try again")
Signup(Brugernavn, Kodeord)
def Signin(U,P):
print("Welcome to sign in!")
print("Do you already have an account?[y/n]")
ans = input("")
if ans == "n":
print("You will now be redirected to sign up")
Signup(Brugernavn, Kodeord)
elif ans == "y":
U = input("Username: ")
if U in Brugere:
P = input("Password: ")
print("WELCOME")
else:
print("Invalid username, try again")
Signin(Brugernavn, Kodeord)
else:
print("Please write 'y' or 'n'")
Signin(Brugernavn, Kodeord)
Signin(Brugernavn, Kodeord)
Brugere.close()
When I run it, the signup function works as it should, but when the signin function is called, it can't find the username and password from the text file. I think it's because they only get appended after the script is done running. However, 'Im not sure.
I've been struggling for a long time with this csv file thing. I want to have it like
this where each line is a list where I can find the username and password
I've heard people calling it "comma seperated values", however I have no idea how to do it.
TL;DR:
I suspect the simplest solution is:
...
if j not in brugere:
...
writer.writerow(line1)
brugere.flush() # Flush pending changes to file.
...
...
However, I've noticed a few things worthy of consideration:
Python help. You can get help on Python things within an interactive Python session:
python
> import csv
> help(csv)
CSV for this sort of usecase isn't a good idea. If it's production grade use a proper DB, if you're just playing around locally to get a feel for Python use a dict() and store as JSON. I.e.:
import json
username = "myuser"
password = "password"
filename = "data.json"
# Reads data. Requires file `data.json` to exist, with content `{}`.
with open(filename, "r") as data_stream:
data = json.load(data_stream)
if username in data:
print(f"User {username} exists.")
else:
data[username] = password # You'd usually hash and salt a password. It's a separate topic.
# Writes data to disk.
with open(filename, "w") as data_stream:
data_stream.write(json.dumps(data, indent=2)) # Makes it look pretty on disk.
Note that open() returns a stream wrapped in class io.TextIOWrapper (see python -c "import io; help(io.TextIOWrapper)"). It's basically an interface to get the data you want, not the data itself. Try open("test.txt").readlines() to actually read data from stream, for example. Note that streams are consumables.
When you call in on the stream Brugere you're calling the __contains__() dunder method on class io.TextIOWrapper which isn't implemented. But Python being Python it returns False (it's not meaningful).
The CSV interfaces are pretty straightforward:
import csv
filename = "test.txt"
f = open(filename, "w")
writer = csv.writer(f)
writer.write(["myuser", "password"])
# Flushes lines to file.
f.flush()
...
f.close()
Creating a CSV is as simple as writing a string with each value separated by a designated character, often a comma as the name implies. You don't really need the csv library for that.
Your Signup method would then look as follows:
def Signup(j,k):
print("Welcome to sign up!\n Please enter your Username")
j = input("")
if j not in Brugere:
print("Hello, " + j + ", please enter a password:")
k = input("")
line1 = [j,k]
# Write a string with a comma between the values
Brugere.write("{}, {}\n".format(j, k))
print("Great! Now you can sign in")
Signin(Brugernavn, Kodeord)
else:
print("Username already taken! try again")
Signup(Brugernavn, Kodeord)
With that being said, using CSVs to store operational data in a an application does not make all that much sense to me. Please see below for a modified version of signup using SQLite, which is much more robust and still simple to use:
import sqlite3
con = sqlite3.connect('brugere.db')
cur = con.cursor()
# Create a user table with bruger and kodeord
# I added an extra field for when the user was created as well
# which defaults to the date and time the user was created
cur.execute("""create table if not exists brugere
(brugernavn text,
kodeord text,
dato_oprettet TIMESTAMP DEFAULT CURRENT_TIMESTAMP)""")
con.close()
def signup():
con = sqlite3.connect('brugere.db')
cur = con.cursor()
with con:
print("Please enter your Username")
bruger = input()
cur.execute("select brugernavn from brugere where brugernavn =?", [bruger])
bruger_eksisterer = cur.fetchone()
if bruger_eksisterer:
print("Username already taken! Try again")
# Signup(Brugernavn, Kodeord)
else:
print("Hello, " + bruger + ", please enter a password:")
kodeord = input()
try:
cur.execute("insert into brugere (brugernavn, kodeord) values (?, ?)", (bruger, kodeord))
print("Great! Now you can sign in")
except sqlite3.Error as Err:
print("Couldn't add user.\nError: {}".format(Err))
# Signin(Brugernavn, Kodeord)
con.close()
signup()
Modifying the signin should be straighforward, but I'll leave that part up to you.

Python Login and Register System using text files

Hey I am trying to create a system using text files where a user can sign up and log in. All the data will be stored in plain text in a text file called User_Data.txt. My code works but I would like to know if there is anything I missed or If I could improve it in any way. Sorry for the Bad code Formatting in advance.
def choices():
print("Please choose what you would like to do.")
choice = int(input("For Sigining Up Type 1 and For Signing in Type 2: "))
if choice == 1:
return getdetails()
elif choice == 2:
return checkdetails()
else:
raise TypeError
def getdetails():
print("Please Provide")
name = str(input("Name: "))
password = str(input("Password: "))
f = open("User_Data.txt",'r')
info = f.read()
if name in info:
return "Name Unavailable. Please Try Again"
f.close()
f = open("User_Data.txt",'w')
info = info + " " +name + " " + password
f.write(info)
def checkdetails():
print("Please Provide")
name = str(input("Name: "))
password = str(input("Password: "))
f = open("User_Data.txt",'r')
info = f.read()
info = info.split()
if name in info:
index = info.index(name) + 1
usr_password = info[index]
if usr_password == password:
return "Welcome Back, " + name
else:
return "Password entered is wrong"
else:
return "Name not found. Please Sign Up."
print(choices())
There is a lot of improvements You could do.
First of all, split functionality to smaller function.
PASSWORD_FNAME = "User_Data.txt"
def get_existing_users():
with open("r", PASSWORD_FNAME ) as fp:
for line in fp.readlines():
# This expects each line of a file to be (name, pass) seperated by whitespace
username, password = line.split()
yield username, password
def is_authorized(username, password):
return any((user == (username, password) for user in get_existing_users())
def user_exists(username):
return any((usr_name == username) for usr_name, _ in get_existing_users())
# above is equivalent of:
#
# for usr_name, _ in get_existing_users():
# if usr_name == username:
# return True
# return False
def ask_user_credentials():
print("Please Provide")
name = str(input("Name: "))
password = str(input("Password: "))
return name, password
def checkdetails():
name, password = ask_user_credentials()
if is_authorized(name, password):
return "Welcome Back, " + name
if user_exists(name):
return "Password entered is wrong"
return "Name not found. Please Sign Up."
def getdetails():
name, password = ask_user_credentials()
if not user_exists(name):
return "Name Unavailable. Please Try Again"
# Not sure tho what would You like to do here
It's always good to remember to always close your file if you read it.
So if you do something like:
f = open("r", "file.txt") remember to always call f.close() later.
If you use context manager and do it like:
with open("r", "file.txt") as fp:
print(fp.read())
it will automatically close the file for you at the end.
Firstly, fix the spelling error at int(input("For Sigining Up Type 1") Other than that I would add some kind of purpose, for example storing secret numbers or something.
For example you can extend your script with a simple password recovery system.
I think it could be useful to learn...
You can implement a sort of a simple hashing system in order to avoid saving the password as plain text.
If you want to add a GUI, please consider using Tkinter.
https://docs.python.org/3/library/tkinter.html
Let we know.
Good Luck and Keep Coding with <3

csv - Creating a login system, python not properly reading from .csv file?

I am trying to make a login system that is looped basically and whenever I try to enter the correct details that are even stored in the .csv file, it outputs as incorrect username/password no matter what I put. This code works for python 3.6 but I need it to work for python 3.2.3.
loop1 = False #for this bit of code (logging in)
loop2 = False #for next bit of code
while loop1 == False:
choice = input("Login/SignUp [TYPE 'L' OR 'S']: ").lower()
if choice == "l":
username = input("Username: ")
password = input("Password: ")
f = open("usernamepassword.csv","r")
for line in f:
details = line.split(",")
if username == details[0] and password == details[1]:
print("Welcome")
break
#this whole bit of code is meant to read from the csv and check if the login details are correct
else:
print("Username/Password [INCORRECT]")
Allow me to refactor your code:
def login(username, password):
with open("usernamepassword.csv", "r") as csv:
all_details =
[[attr.strip() for attr in line.split(",")]
for line in csv]
return any(
username == details[0]
and password == details[1]
for details in all_details)
def login_action():
username = input("Username: ")
password = input("Password: ")
if not login(username, password):
raise ValueError("Username/Password [INCORRECT]")
return True
_USER_ACTIONS = {
'l': login_action
}
def main():
while True:
choice = input("Login/SignUp [TYPE 'L' or 'S']: ").lower()
action = _USER_ACTIONS[choice]
try:
if action():
break
except Exception as err:
print(err.message)
I think your unexpected behavior comes from not stripping the values you get after splitting by ,
Solved by replacing:
if username == details[0] and password == details[1]:
With:
if username == details[0] and (password+"\n") == details[1]:
You may have a bug in line.split(','), try line.strip().split(',')
TL; DR: posted a proper solution there : https://github.com/cgte/stackoverflow-issues/tree/master/47207293-csv-dict
I'll stuff up my answer later if needed.
Furthermore you have a poor code design here, and find yourself debugging in the middle of a loop.
So first of all : load the data file, store content to a dict.
f = open("usernamepassword.csv","r")
for line in f:
details = line.split(",")
if username == details[0] and password == details[1]:
print("Welcome")
break
Should become
user_pass = {}
f = open("usernamepassword.csv","r")
for line in f:
user, password = line.strip().split(",")
user_pass[user] = password
f.close()
or better
with open("usernamepassword.csv","r") as f:
for line in f.readlines():
user, password = line.split().split(",")
user_pass[user] = password
eventually run python -i yourfile.py and type "user_pass" to see what is actually stored when correct go on further code.
Think of using the csv module : https://docs.python.org/3/library/csv.html
Then get username and password from input and check:
if login in user_pass and user_pass[login] = password:
# or better `if user_pass.get(login, None) == password:`
do_stuff()

Categories