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

Viewed 101

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.

2 Answers

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.

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()
Related