vogelier
vogelier

Reputation: 51

Fetching Context Variables by identifier

I'm trying to use the new contextvars library (https://docs.python.org/3/library/contextvars.html) to make certain values available across modules in an async context, similar to ContextVars across modules, and was wondering if there was a way of fetching context variables by name or identifier from within a given execution context without importing the context variable directly.

Ideally, I would like to do something like the following:

from _contextvars import ContextVar

language = ContextVar('language')
language.set('en-US')

And in a separate module, which may serve a web request or fetch a translation based on the language that was set:

from _contextvars import copy_context

ctx = copy_context()

if 'language' in ctx:
  lang_context_var = ctx.get('language') # get the context variable
  lang_value = lang_context_var.get() # get the value

I realise the API for the contextvars library has a mapping between a ContextVar object and value and not string to value so this lookup can't be done, but I find that needing to do a from module_with_context_var import language a bit messy and prone to being spread across a codebase rather than a single wrapper for the current context , which seems like what copy_context() should be.

Any pointers in a good way to structure context variable fetching and setting in a central location which also works with co-routines/async programming would be much appreciated!

Upvotes: 5

Views: 603

Answers (1)

Eugene Yarmash
Eugene Yarmash

Reputation: 150081

If you don't want to import anything from the module where vars are declared, you could just do a linear search on the context mapping, e.g.:

from contextvars import ContextVar, copy_context


def get_var_by_name(var_name: str) -> ContextVar | None:
    ctx = copy_context()
    return next((var for var in ctx if var.name == var_name), None)

lang_context_var = get_var_by_name('language')

if lang_context_var is not None:
    print(f"Language set to {lang_context_var.get()}")

Alternatively, you could put the context vars in a dictionary keyed by name and import that:

CONTEXT_VARS: dict[str, ContextVar] = {
    "language": ContextVar('language'),
    "country": ContextVar('country'),
    # etc
}

# in another module

from context_vars import CONTEXT_VARS

if "language" in CONTEXT_VARS:
    language = CONTEXT_VARS["language"].get(None)
    if language is not None:
        print(f"Language set to {language}")

Of course, in this case you should set values for the variables via the dictionary as well.

Upvotes: 1

Related Questions