Search code examples
pythonfunctionvariables

best practice for global variable in python


I have the following code across different files. I want to understand what is the best practice to define the global variable so that I don't have to run the function again to get the variable value. File structure as below:

00.py

def f_token():
    global v_Token, v_session
    auth_context = adal.AuthenticationContext("https://login.microsoftonline.com/common")
    token_response = auth_context.acquire_token_with_username_password("https://xxx.xxx.dynamics.com/", username, password, client_id)
    v_Token= token_response['accessToken']
    return v_Token

01.py

from 00 import*
A_Token = f_token()
def f_meta():
    code here

02.py

from 00 import*
A_Token = f_token()
def f_anotherfunction():
    code here

03.py

from 00 import*
A_Token = f_token()
def f_function():
    code here

I only want to execute f_token once and reuse the value of v_Token across file 01,02, and 03. These 3 functions are not dependant on each other. They are being executed separately. What would be the best practice for this problem?


Solution

  • Assuming this code could be multithreaded, you can control access to the global variable with a lock. The global resource starts out None and the first caller who finds that None acquires the token. Future callers notice it is no longer None and skip the extra work.

    import threading
    
    v_lock = threading.Lock()
    v_Token = v_session = None
    
    def f_token():
        global v_Token, v_session
        # short circuit when token already acquired 
        if not v_Token:
            # lock, verify no race condition, get token
            with v_lock.lock():
                if v_Token is None:    
                    auth_context = adal.AuthenticationContext("https://login.microsoftonline.com/common")
                    token_response = auth_context.acquire_token_with_username_password("https://xxx.xxx.dynamics.com/", username, password, client_id)
                    v_Token = token_response['accessToken']
        return v_Token
    

    You probably don't want those module level A_Token = f_token() calls in other modules. Simple import generally shouldn't do something as dramatic as network authentication. It makes things hard to test and code is generally less reusable. Just call f_token() every time you need the token.