Python 3 UnboundLocalError: local variable referenced before assignment in try statement

Question:

I’m trying to make a program that handles RSA encryption and decryption for sending messages using the rsa library. Currently I’m implementing it to use only 1 key pair, but it’ll be changed later on. I’m having some trouble with the logic to check whether the files that store the keys exist or not. The logic for these functions looks like so:

import rsa

################################################
keySize = 256
################################################


def genKeys():
    pubkey, privkey = rsa.newkeys(keySize)

    with open("public.pem", "w") as pub:
        pub.write(str(pubkey))

    with open("private.pem", "w") as priv:
        priv.write(str(privkey))

    return pubkey, privkey

def readKeys():

      with open("private.pem", "r") as priv:
          keydata = priv.read()
          privkey = rsa.PrivateKey.load_pkcs1(keydata)
          print(privkey)

      with open("public.pem", "r") as pub:
          keydata = pub.read()
          pubkey = rsa.PublicKey.load_pkcs1(keydata)
          print(pubkey)
      return pubkey, privkey




def send(message, pubkey):
    message = message.encode('utf-8')
    crypto = rsa.encrypt(message, pubkey)
    return crypto

def recv(crypto, privkey):
    message = rsa.decrypt(crypto, privkey)
    message = message.decode('utf-8')
    return message

def main():
    global pubkey, privkey
    try:
        if (open("private.pem", "r") and open("public.pem", "r")) == True:

            pubkey, privkey = readKeys()

    except:
        (pubkey, privkey) = genKeys()

    cryptMessage = send('hello world', pubkey)
    print(cryptMessage)
    print(recv(cryptMessage, privkey))

if __name__ == "__main__":
    main()

The main() function, specifically the try: except: statement is where my problem resides. The error I’m getting is builtins.NameError: name 'pubkey' is not defined. I’ve tried declaring the variables as global, which is what other answers suggested, but that didn’t work for me, or I’m doing it wrong.

Thanks for the help. Sorry for the noob question.

EDIT:
So, that solved my first problem. However, now when I use the try/except statement, it tries to call the variables, which have a value of None, even though I’m assigning them a value using the functions. The stack trace is

File "", line 61, in <module>
  main()
File "", line 56, in main
  cryptMessage = send('hello world', pubkey)
File "", line 37, in send
  crypto = rsa.encrypt(message, pubkey)
File "/usr/local/lib/python3.6/dist-packages/rsa/pkcs1.py", line 169, in encrypt
  keylength = common.byte_size(pub_key.n)

builtins.AttributeError: 'NoneType' object has no attribute 'n'
Asked By: GarrukApex

||

Answers:

Even if you declare a variable as global inside a function, you still need to initialize it, or in the global scope or while declaring them.

import rsa

################################################
keySize = 256
################################################

# DECLARATION IN GLOBAL SCOPE
pubkey = None
privkey = None

def genKeys():
    pubkey, privkey = rsa.newkeys(keySize)

    with open("public.pem", "w") as pub:
        pub.write(str(pubkey))

    with open("private.pem", "w") as priv:
        priv.write(str(privkey))

    return pubkey, privkey

def readKeys():

      with open("private.pem", "r") as priv:
          keydata = priv.read()
          privkey = rsa.PrivateKey.load_pkcs1(keydata)
          print(privkey)

      with open("public.pem", "r") as pub:
          keydata = pub.read()
          pubkey = rsa.PublicKey.load_pkcs1(keydata)
          print(pubkey)
      return pubkey, privkey




def send(message, pubkey):
    message = message.encode('utf-8')
    crypto = rsa.encrypt(message, pubkey)
    return crypto

def recv(crypto, privkey):
    message = rsa.decrypt(crypto, privkey)
    message = message.decode('utf-8')
    return message

def main():
    # DECLARATION INSIDE THE FUNCTION
    global pubkey, privkey; pubkey = privkey = None
    try:
        if (open("private.pem", "r") and open("public.pem", "r")) == True:

            pubkey, privkey = readKeys()

    except:
        (pubkey, privkey) = genKeys()

    cryptMessage = send('hello world', pubkey)
    print(cryptMessage)
    print(recv(cryptMessage, privkey))

if __name__ == "__main__":
    main()
Answered By: Ami Hollander

You should declare the variables pubkey and privkey outside the function genKeys()and then the error should vanish.
This is my first post, I hope to be helpful.
Regards.

Answered By: Half_Bit

Your problem is here:

if (open("private.pem", "r") and open("public.pem", "r")) == True:

Python logical operators return the last inspected value, not True or False. That value was a file object from the open command is not equal to True. You could check the files for existence with os.path.isfile and skip the try/except block completely. Or you could just try the actual read the in the try block, like so

(slimmed down for demo)

# dummys for test...

def readKeys():
    print('try read')
    return open('public.pem').read(), open('private.pem').read()

def genKeys():
    print('try write')
    open('public.pem','w').write('foo')
    open('private.pem','w').write('bar')
    return 'foo', 'bar'


def main():
    global pubkey, privkey
    try:
        pubkey, privkey = readKeys()
    except:
        (pubkey, privkey) = genKeys()


if __name__ == "__main__":
    main()
Answered By: tdelaney

Copying the code of the other answer and with the change to check for globals() instead of overwriting None variables at every function call.

import rsa

################################################
keySize = 256
################################################

# DECLARATION IN GLOBAL SCOPE
pubkey = None
privkey = None

def genKeys():
    pubkey, privkey = rsa.newkeys(keySize)

    with open("public.pem", "w") as pub:
        pub.write(str(pubkey))

    with open("private.pem", "w") as priv:
        priv.write(str(privkey))

    return pubkey, privkey

def readKeys():

      with open("private.pem", "r") as priv:
          keydata = priv.read()
          privkey = rsa.PrivateKey.load_pkcs1(keydata)
          print(privkey)

      with open("public.pem", "r") as pub:
          keydata = pub.read()
          pubkey = rsa.PublicKey.load_pkcs1(keydata)
          print(pubkey)
      return pubkey, privkey




def send(message, pubkey):
    message = message.encode('utf-8')
    crypto = rsa.encrypt(message, pubkey)
    return crypto

def recv(crypto, privkey):
    message = rsa.decrypt(crypto, privkey)
    message = message.decode('utf-8')
    return message

def main():
    # DECLARATION INSIDE THE FUNCTION
    global pubkey, privkey;
    if 'pubkey' not in globals() or 'privkey' not in in globals():
        pubkey = privkey = None
    try:
        if (open("private.pem", "r") and open("public.pem", "r")) == True:
            if 'pubkey' in globals() and 'privkey' in globals():
                pubkey, privkey = readKeys()
            
    except:
        if 'pubkey' in globals() and 'privkey' in globals():
            (pubkey, privkey) = genKeys()


    if 'pubkey' in globals() and 'privkey' in globals():
        cryptMessage = send('hello world', pubkey)
        print(cryptMessage)
        print(recv(cryptMessage, privkey))

if __name__ == "__main__":
    main()

This is untested. I did that for local variables instead where a local connection object was closed in the finally block. For that, I did not even have to set the objects as None at any time since I checked for the connection object to be in globals() before closing it. The error was gone.

In this code, however, you do not just close objects, you overwrite them instead, both in the try and in the except block.
Therefore, I just guess that you need to make the None objects at first loading time.
If this trick works for global variables as well, this will be the only code that does not overwrite global objects with None every time the function gets called, but only if the variables are touched the first time.

Categories: questions Tags: , ,
Answers are sorted by their score. The answer accepted by the question owner as the best is marked with
at the top-right corner.