I don't understand why this loop terminates the way it does - python

checkSql = 'SELECT userid FROM bs_members WHERE userid = :checkUser'
doesUserExist = False
while True:
doesUserExist = False
newUser.userID = ga.getInput('Enter userID: ', "\w+$")
checkUserID = ds.execute(checkSql,checkUser=newUser.userID)
for row in ds:
if row == checkUserID:
doesUserExist = True
print 'That user name is already in use. Please enter a new username.'
break
if doesUserExist == False:
break
else:
continue
I am using the cx_Oracle module with Python 2.7. I am trying to prompt the user to enter a userID. The program will then check if the userID already exists and if it does prompt the user for a different userID. The execute method is a helper method that uses the execute method from cx_Oracle to interact with the Oracle database. The getInput method prompts the user for input that is then checked against the regular expression.
I know I have this wrong but I believe the while loop starts the first action that is taken is the user is prompted for a userID. Then the userID is checked against the database. The for loop starts and checks if the row returned by ds.execute() is the same as the userID provided by the user. If it is the user is told to use another user name and the break exits the for loop. The if statement then checks if a user exists and if it doesn't it breaks the while loop. If not then the while loop iterates so the user is prompted to enter a non-existent userID.
What happens is the user is prompted for the userID then none of the checking ever appears to happen to the user and the program moves on to the next piece of code. What am I missing here? I have included a link to the docs for execute(). The execute method in the above code is part of the following helper method:
def execute(self, statement, **parameters):
if parameters is None:
self._curs.execute(statement)
else:
self._curs.execute(statement,parameters)
If I need to provide more information let me know.
edit: I forgot the line doesUserExist = False immediately after the beginning of the while loop so I added that.

Your custom execute method doesn't return anything meaning that checkUserID in your code will be None.
Furthermore, what you're interested in is if there's at least one row returned by the query. If there are none then the userID should be available.
The docs say that calling .fetchone() returns None if no more rows are available. You can use that.
checkSql = 'SELECT userid FROM bs_members WHERE userid = :checkUser'
while True:
newUser.userID = ga.getInput('Enter userID: ', "\w+$")
ds.execute(checkSql,checkUser=newUser.userID)
if ds.fetchone() is None:
# This userID is available.
break
else:
print 'That user name is already in use. Please enter a new username.'
I'm assuming here that ds is an instance of Cursor, or subclass thereof.

At least you should have line doesUserExist = False at the beginning of the while loop. Otherwise, if user enters an existing ID once then it will keep looping forever.

Related

A Loop I can never return from

So I have been tasked with creating a Login function using an intake from a pickle file, my only issue (that I have noticed) so far is that I can never get out of the loop.
for counter in range(len(Users)):
UserN = input("Username: ")
if UserN in Users[counter]:
PassW = input("Password: ")
if PassW in Users[counter]:
print("User Authenticated")
break
else:
attempt = 1
while attempt != 4:
print("Password Incorrect | Attempt", attempt)
PassW = input("Password: ")
if PassW in Users[1]:
print("User Authenticated")
MainMenu()
else:
attempt = attempt + 1
if attempt == 4:
print("\nToo many attempts, you are locked out")
exit()
else:
print("\nUser not found!\n")
If the user is authenticated, the count of the attempt will stop increasing, but the condition for the while-loop is stop is attempt == 4, so it will be stuck until the user has typed in the wrong password 4 times.
To fix it, either add a variable authenticated before the while-loop and initialize it as False, then set it to True once the user has successfully been authenticated.
attempt = 1
authenticated = False
while attempt != 4 and !authenticated:
...
if PassW in Users[1]:
...
authenticated = True
Or if you don't want a new variable, simply break by
if PassW in Users[1]:
...
break
to break the loop
If you the password is found, you call another function but you never exit from the loop. You should add a break statement.
You can add a break statement under
MainMenu()
break;
The logic in your code seems quite wonky.
This code assumes that the username is the first appearing in Users; if the second user logs in, they need to enter their name twice, the third user 3 times, and so on. If you mistype your name on your "turn", you won't be able to log in at all.
I would suggest the following structure:
Turn your variable "Users" into a dictionary, mapping user name to password.
Ask the user name.
If the user name does not appear, either stop or loop (you might want to prompt to ask whether they want to try again or not, or just let them press ctrl-D to stop (and catch the ensuing EOFError or KeyboardInterrupt (which happens if they press ctrl-C instead).
Some other issues, that are not as crucial for this question but good guidelines:
Use proper style conventions. Variable names should not start with upper case characters. Same for goes for functions. It should be users, passw, main_menu().
You really should not store passwords in a pickle file; that is obfuscation rather than security. Have a look at https://www.geeksforgeeks.org/getpass-and-getuser-in-python-password-without-echo/ for better practices.
When you say the user is locked out, nothing actually happens. They are not really locked out; they could just try again. That means you can just create a bit that brute-forces password guesses
Let them enter the password, with a maximum number of guesses.
A good rule is to test for attempt > 3 instead of attempt == 4. This does not make a difference here, but in larger functions, it's often good practice to make the test a bit "wider".

SQLAlchemy with_for_update lock not working? [duplicate]

There is a student whose type attribute is 4 and the minimum value for type attribute can be 1.
In postgres
In session 1, I exclusively lock and update a row in the student table:
BEGIN;
LOCK TABLE students IN ROW EXCLUSIVE MODE;
SELECT * FROM students WHERE id = 122 FOR UPDATE;
UPDATE students SET type = 1 WHERE id = 122;
END;
In session 2 I concurrently run:
UPDATE students SET type = type - 1 WHERE id = 122;
The result I get is an exception, i.e student's type can't be lower than 1 in session 2 since I already set the same student's type to value of 1 and since session was in exclusive lock for that student, session 2 had to wait.
In Flask-SQLAlchemy
I tried to recreate the same result with user, type attribute set to 4 by default.
In session 1:
user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type = 1
db.session.commit()
In session 2:
user = Student.query.filter(Student.id == 122).first()
user.type -= 1
db.session.commit()
The result I get is that user's type equals 3, whereas I should get an exception.
Transaction changes in session 1 are overridden by those in session 2, even though after db.session.commit() in transaction in session 2 it waits till transaction in session 1 is over.
But in session 2 when I run this with session 1 concurrently:
user = Student.query.filter(Student.id == 122).update({"type": Student.type - 1})
db.session.commit()
I get the right output, i.e integrity error showing an attempt to set student 122s type attribute to 0 (not overriding session 1 result).
Would like to know why this happens.
The 2 sessions should look like this:
user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type = 1
db.session.commit()
and
user = Student.query.with_for_update(of=Student, nowait=True).filter(Student.id == 122).first()
user.type -= 1
db.session.commit()
In order for FOR UPDATE to work properly, all involved transactions which intend to update the row need to use it.
In your example, session 2 is not using with_for_update. Since you didn't tell it to use FOR UPDATE, it is free to read the old value of the row (since the new value has not yet been committed, and locks do not block pure readers), then modify it that in-memory value, then write it back.
If you don't want to use FOR UPDATE everywhere that you read row with the intention of changing it, you could instead use isolation level serializable everywhere. However if you do, things might not block, but rather will appear to succeed until the commit, then throw serialization errors that will need to be caught and dealt with.
Note: Your pre-edit example should have worked as both sessions were labelled with with_for_update.

username input and matching using python-(Stuck in while loop)

I want to create a class based user registration portal and for that I wanted to add some usernames. But if they are already taken, then the code should prompt the
user to add another username and I tried to do so with this code.
a=0
User=['name123']
username=raw_input("Enter username : ")
while a==0:
for i in User:
if i==username:
a=0
break
else:
a=1
if a==0:
usernarme=raw_input("Username already taken.\nEnter another username :")
But it gets stuck in the loop and displays the following message repeatedly
even after entering a valid username. What I am doing wrong?
"Username already taken"
That will fix all your issues with loop.
User=['name123']
username=raw_input("Enter username : ")
while username in User:
username=raw_input("Username already taken.\nEnter another username :")
P.S. I'm strongly recommend you to read Dive Into Python and The Zen of Python
Here's a more pythonic version of your code, that's easier to understand and to fix:
users = ['name123']
username = raw_input("Enter username : ")
while username in users:
username = raw_input("Username already taken.\nEnter another username :")
Notes:
Always use lower case for variable names.
Use in instead of looping over users explicitly in the loop.
Avoid break in a while loop and rather change the value of
your loop condition.

Using a condition with an event

This is the code I want to implement. Simply stated, it is a small chat bot that connects to a server and joins room testroom1 with credentials user and pass. Using a library, an event is called every time somebody enters a message into the chatroom, and this bot will print their message, with formatting:
import ch
class bot(ch.RoomManager):
for x in range(0, 3):
def onMessage(self, room, user, message):
print("[{0}] {1}: {2}".format(room.name, user.name.title(), message.body))
rooms = ["testroom1"]
username = "user"
password = "pass"
bot.easy_start(rooms,username,password)
However, the key problem is this:
for x in range(0, 3):
I only want the "onMessage" function to be accessed 3 times then to not print any more messages. This doesn't seem to work, and it continues to print messages until I exit it.
If possible, I would like to limit the onMessage function with while loops further in my program, and to use it in several different cases to parse messages at different times.
The source of the ch library and basis for my simple code is here
Any insight on this issue is appreciated.
I think you have misunderstood the Python syntax slightly. The for loop in your class will define the same onMessagemethod three times, with each new iteration replacing the old one. This would be apparent if you tried to use the x (or rather self.x) within your function.
What you want, is a counter within the method or class, that is incremented with each call and then, when the max count is reached you could just have the onMessagemethod do nothing.
Try the following code, which I think does what you want:
import ch
class bot(ch.RoomManager):
messages_left = 3
def onMessage(self, room, user, message):
if self.messages_left > 0:
print("[{0}] {1}: {2}".format(room.name, user.name.title(), message.body))
self.messages_left -=1
rooms = ["testroom1"]
username = "user"
password = "pass"
bot.easy_start(rooms,username,password)
What I have done here, is to add an initializing function that defines an object variable messages_left that is decremented in the onMessagecall, until it hits zero. Further, I have added a class parameter, which allows you to control how many times you want onMessage to format the message.

How do I add a retry option to a function in Python?

Pretty new to Python, trying to create a simple login system here (has to be done this way). I've already defined a user() function which asks for the username and checks its validity. This function starts by calling the user function. Here is the main part:
user()
if user in userlist:
while True:
pass = raw_input("Enter password, or X to retry: ")
if pass == 'X':
break
if userlist[user] == pass:
break
else:
print "Invalid password."
I want the function to loop back to asking for username input if X is entered. The rest of it works fine but as it stands, entering X just ends the function and doesn't loop it back to the start. Is this just not possible or can I rewrite it to work? I assume I'd need to include user() into the loop but I've encountered various errors while trying.
You intentionally say to exit the loop if the user enters X by using the break statement. That's why the loop is exiting. Instead of break use continue.
if password == 'X':
continue
This will start the loop over again at the top.
As another user notes, you can't use pass as a variable name. The code you posted clearly isn't the code you're actually running. Anyway, I've assumed that you really used the name password.
Don't use pass as a variable name because it clobbers the builtin pass. What you want is
if passwd == 'X':
continue #restart at top of loop
You'll have to do:
if password == 'X':
continue #restarts loop at while

Categories

Resources