kitetsu
kitetsu

Reputation: 23

UnboundLocalError: local variable <var> referenced before assignment

I am getting an unbound local error in a function that is being looped. I am not getting the error in the first iteration, only in the second (and most probably the one after as well).

I have tried:

  1. initializing the accessKey and secretKey as a global variable and putting "global" keyword inside the function.
  2. clearing/deleting these variables after the first iteration.

Neither seem to work

# key_file[0] is a csv-file with columns: tenant, api_key, secret_key
def keys(tenant):
    for line in key_file[0]:
        if tenant == line.get('tenant'):
            accessKey = line.get('api_key')
            secretKey = line.get('secret_key')
    return  accessKey, secretKey

# tenant_list is a list with the names of the tenant

for tenant in tenant_list:
    keypair = keys(tenant) 
    print(keypair)

Upvotes: 2

Views: 1881

Answers (2)

Blckknght
Blckknght

Reputation: 104712

I suspect there are two issues with your code. Together they combine to give the exception you're seeing, but even if you could mitigate the immediately exception, the errors would still need to be fixed eventually.

The first error is a bit speculative, but I suspect that key_file[0] is an iterator (probably a csv.DictReader, or something similar). When you iterate over it the first time keys is called, it works as expected, with the items from the iterator being used as they are needed. The trouble comes the second time keys gets called, as then the iterator at that point is already exhausted, and so iterating over it doesn't do anything at all. The loop just ends immediately.

The fix for this issue is probably to dump the iterator into a sequence that you can iterate upon as many times as you want. This can be done with the equivalent of key_file[0] = list(key_file[0]) somewhere (you might be able to just put the list call in to whatever existing code assigns that value the first time around, rather than assigning the iterator first and then later converting it into a list). An alternative might be to move the whole file-opening and CSV parsing logic into keys (or into a function that keys can call) so you can recreate the iterator whenever you need it.

The other issue is that your function raises an odd exception any time you ask for the keys of a tenant that's not in your file. Now, raising some exception may be the right thing to do, but the UnboundLocalError exception you're getting now is a lot less clear than something like ValueError(f"Unknown tenant {tenant}") would be.

The most natural way to fix this second issue is probably to move the return call into the if block inside the loop, so that you immediately return the keys if you have found them. Then you can raise an appropriate exception if you make it out the end of the loop without having found anything. If you really do need to keep the logic of searching the whole file and returning the keys for the last matching line, you could initialize your fooKey values to some sentinel value like None and then check for it after the loop to see if they've changed (raising an exception they have not).

Anyway, here's my general fix:

key_file[0] = list(csv.DictReader(...))  # this is somewhere above, probably


def keys(tenant):
    for line in key_file[0]:
        if tenant == line.get('tenant'):
            accessKey = line.get('api_key')
            secretKey = line.get('secret_key')
            return accessKey, secretKey           # return immediately if a match is found

    raise ValueError(f"Unknown tenant: {tenant}")  # it's an error if you reach here

Upvotes: 2

nobleknight
nobleknight

Reputation: 797

I think you are searching for line where tenant == "tenant" and corresponding api_key and secret_key. Assuming there is only one tenant == "tenant" you can directly return accessKey, secretKey when it is found.

def keys(tenant):
    for line in key_file[0]:
        if tenant == line.get('tenant'):
            accessKey = line.get('api_key')
            secretKey = line.get('secret_key')
            return accessKey, secretKey

I recreated your error in following code, which proves that variables declared inside conditional statements (like if, elif) are not accessible outside scope of that conditional statements.

# code 1
def func(a):
    for i in a:
        if i == '5':
            n = 5

    return n

a = [1, 2, 3, 4, 5]
print(func(a))
# code 2
def func(a):
    for i in a:
        if i == '5':
            n = 5

        return n

a = [1, 2, 3, 4, 5]
print(func(a))

Both code 1 and code 2 gives following error, because we are accessing n outside if statement.

UnboundLocalError: local variable 'n' referenced before assignment

Upvotes: 1

Related Questions