Search code examples
pythonjsonunicodematchraw-input

Matching raw_input with JSON key value pair


import getpass
import json

class LogInNow(object):

    def __init__(self, file):
        self.file = json.load(open("password.txt"))

    def authenticate(self):
        self.username = raw_input("Enter Username> ")
        self.password = getpass.getpass("Enter Password> ")
        for k, v in self.file:
            if k == self.username and v == self.password:
                print "It worked"
            else:
                print "Fail"

go = LogInNow("password.txt")
go.authenticate()

A separate module uses json.dump to write both inputs into password.txt

When I:

>>> import login

I get:

{u'go': u'go'}

I read the 'u' is for unicode, is good, and must be kept.

I could concatenate the u before self.username and split the values to add the quotes, but that defeats the purpose.

Any ideas?


Solution

  • The u is not part of the string, any more than the quotes are. The u'go' is just how Python represents a Unicode string whose value is go. You do not need to "concatenate the u" or anything like that.

    The raw_input will return 'go', rather than u'go', because it's reading in an encoded byte string. But in Python 2.x, if you compare those two strings, they're still equal. Try it:

    >>> 'go' == u'go'
    True
    

    So, there is no problem here.

    However, as soon as you start dealing with non-ASCII usernames or passwords, then you are going to have a problem. You will need to call decode on the values you got from the user, using the input's encoding, like this:

    >>> self.username = raw_input("Enter Username> ").decode(sys.stdin.encoding)
    

    This is a bit clumsy, but hey, Unicode is clumsy in Python 2.x, that's why Python 3.x was invented.


    There is, however, a bug in your code that may be causing whatever problem you're seeing:

    for k, v in self.file:
    

    When you loop over a dictionary, you loop over its keys, not its key-value pairs. So, each username will be unpacked into k and v. If you have any username that aren't exactly 2 characters long, you will get ValueError: too many values to unpack. But because you happen to have only one username, and it happens to be exactly 2 characters long, that u'go' gets unpacked into u'g' and u'o'. So, instead of comparing the username to go and the password to go, you end up comparing the username to g and the password to o, which doesn't match.

    If you want to iterate over key-value pairs, use for k, v in self.file.items():.


    But you usually don't want to iterate through a dict's items to search it either. The whole point of a dict is that you can look things up instantly. Instead of this:

    for k, v in self.file:
        if k == self.username and v == self.password:
            print "It worked"
        else:
            print "Fail"
    

    … just do this:

    if self.password == self.file.get(self.username):
        print "It worked"
    else:
        print "Fail"
    

    Or, if you want to distinguish "wrong password" from "unknown user":

    try:
        if self.password == self.file[self.username]:
            print "It worked"
        else:
            print "That's the wrong password, you evil hacker"
    except KeyError:
        print "I've never heard of you"