2

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)
4
  • accessKey and secretKey would not be assigned if the given tenant is not found in CSV file. Commented Apr 29, 2021 at 6:52
  • They are local variables so they would remain unassigned when keys is called in the second iteration. Commented Apr 29, 2021 at 6:57
  • ah I see it, correct. Do you know why it goes wrong in the second iteration? Commented Apr 29, 2021 at 7:02
  • Like I said, it must be that the second tenant does not exist in the CSV file. You simply need to decide how you want to handle such a situation, usually by either raising a custom exception or returning None for both values by initializing accessKey and secretKey as None first. Commented Apr 29, 2021 at 7:21

2 Answers 2

2

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
Sign up to request clarification or add additional context in comments.

1 Comment

Adding the return into the "if" block did the trick, Thanks. raising the value error is a nice addition.
1

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

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.