Search code examples
pythonpython-3.xcallbackglobal-variablesglobal

Access dynamically changing variable across modules


TL;DR:

  • A separate process calls a function whenever data is available and provides that data as the input to the called function

  • In the function I process the data, and want to make results of it globally available (i.e., a globally available variable changes its value dynamically)

  • What is a clean method to achieve this across modules, when within a module, this job would be performed well through a global variable?

Background

I use an eyetracking device (Tobii 4C) that can provide gaze data of a human via a Python API

The typical flow would be to:

  1. initialize an eyetracker object, pointing to the eyetracking device
  2. "subscribe" the eyetracker object to the actual data: eyetracker.subscribe_to(DATA, callback)

The callback is a function handle that gets called everytime the eyetracking device has new data available. Typically it would be used to save data, e.g.:

# `gaze_data` is what gets returned from the eyetracking device that we subscribed to
def my_callback(gaze_data):
    # fname is defined globally in this case
    with open(fname, 'a') as f:
        f.write(gaze_data)

The data recording would then run "in the background", until we cann eyetracker.unsubscribe()

However, to make use of "live" gaze data, one would have to make gaze_data a global variable, e.g.,

def my_callback(gaze_data):
    global gaze
    gaze = gaze_data['gaze_point']

... now we can use gaze in our script, which gets continuously "updated" whenever the callback function is run (which happens at the sampling rate of the eyetracking device)

Problem

I have a larger project, where I organize 2 modules:

  1. eyetracker.py where I define my callback functions and other eyetracking functions
  2. test_eyetracker.py where I import functions from eyetracker.py and test them

However, when I import the functions from eyetracker.py into test_eyetracker.py, the global variable gaze is not being updated.

After a lot of search, I realized that this is because global variables are not cross-module. So when in test_eyetracker.py the gaze should be updated, this change is not available within the scope of test_eyetracker.py and as such, calls like assert gaze == 'value_i_know_it_should_be' evaluate as False.

Current solution

My current solution is, to define the eyetracking functions in the script, where I also use them. However, this does not feel clean , especially, because I would like to use the eyetracking functions in several other modules (not just the test module) = code duplication.

Is there a better way to organize this?

I have come across this post that shows how builtins are the only truly cross-module globals ... but it is discouraged to use those for a tweak that allows me a cross-module global variable.


Solution

  • A workaround to this problem is to use for example a global dict. I will show you a code snippet based on the linked question that demonstrates the idea:

    file1.py:

    global_dict = {'x': 5}
    x = 5 # for comparison
    

    file2.py:

    from file1 import *
    
    def update_x():
        global x
        global global_dict
        x += 1
        global_dict['x'] += 1
    

    main.py:

    from file2 import *
    
    update_x()
    print(x) # 5
    print(global_dict['x']) # 6