Context locals

Want desired context’s locals

From a module function, get the locals and return value

When testing and an Exception occurs, the locals are only available in the context of where the error occurred, not necessarily where need to debug.

As you read this, keep in mind

To understand our motivation, how badly we want to see those locals, background on those locals:

  • param_a – Satoshi Nakamoto’s private key to the genesis block

  • param_b – the passphrase to that private key

^^ is exactly how it feels when don’t have access to those locals and then suddenly do!

—Betty White <– everything is attributed to her

Limited to:

  • module function; not a: class method, Generator, Iterator, or Iterable

  • function must end in a single return statement; not yield, yield from, or raise

And wait! There’s more

In return, value can be: normal or tuple (packed values)

Wow!

Great!

Yea!

One return value example

Example function found in logging_strict.tech_niques.context_locals. Lets pretend this is the module level function would like to see the locals

def _func(param_a: str, param_b: Optional[int] = 10) -> str:
    param_a = f"Hey {param_a}"  # Local only
    param_b += 20  # Local only
    return "bar"

So there are two locals we’d really really like to see:

  • param_a

  • param_b

Returns "bar"

from logging_strict.tech_niques.context_locals import get_locals_dynamic, _func


def main():
    # If in same script file try, f"{__name__}._func"
    func_path = f"logging_strict.tech_niques.context_locals._func"

    args = ("A",)
    kwargs = {}
    t_ret = get_locals_dynamic(_func, *args, **kwargs)
    ret, d_locals = t_ret
    assert ret == "bar"
    assert "param_a" in d_locals.keys()
    assert "param_b" in d_locals.keys()
    print(d_locals)


main()
{'full_name': '_func', 'param_a': 'Hey A', 'param_b': 30}

Woooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooo- oooooooooooooooooooooooooooooooooooooooooooooooooooooooooooooah!

Note

pretty print is your friend

For better readability, pprint.pprint() is better than print()

Note

__name__ is also your friend

This technique requires the absolute dotted path to the module function. if in the same (python script) file, use f"{__name__}.myfunc" instead.

Useful knowhow, if the module function is in the same file as a unittest

Caution

Especially applies to JSON

tl;dr;

In a str, preserve escape characters, use raw string, e.g. r”\\n”

“\\n” –> “\n”

Unpreserved, ^^ may happen

Escape characters need to be preserved. JSON str is an example where a raw string would preserve the escaped characters and remain valid JSON.

See also

Credit

Module private variables

logging_strict.tech_niques.context_locals.__all__: tuple[str, str, str] = ("get_locals", "get_locals_dynamic", "FuncWrapper")

This modules exports

logging_strict.tech_niques.context_locals._T: TypeVar = typing.TypeVar("_T")

Equivalent to Any

logging_strict.tech_niques.context_locals._P: ParamSpec = typing_extensions.ParamSpec('_P')

Equivalent to Any

Module objects

class logging_strict.tech_niques.context_locals.FuncWrapper(func)

Bases: object

Wraps a function to provide basic info about it.

Variables:

func ((types.FunctionType | types.MethodType | types.BuiltinFunctionType | types.BuiltinMethodType | types.WrapperDescriptorType | types.MethodWrapperType | types.MethodDescriptorType | types.ClassMethodDescriptorType)) – the function to wrap

static _get_method_parent(meth)

Returns the class of the parent of the specified method.

Parameters:

meth – the method to check

Returns:

the class of the parent of the specified method

Return type:

type | None

property cls

Returns the function’s parent class.

Returns:

the parent class

Return type:

type | None

property cls_name

Returns the function’s parent class name.

Returns:

the parent class name

Return type:

str | None

property full_name

Returns the function’s full name with the class included.

Returns:

the full name

Return type:

str

get_dotted_path()

Returns the function’s full path with class and package name.

Returns:

the full path

Return type:

str

Raises:

property module

Returns the function’s parent module.

Returns:

the parent module

Return type:

types.ModuleType | None

property module_filename

Returns the function’s module filename.

Returns:

the module filename

Return type:

str | None

property module_name

Returns the function’s parent module name.

Returns:

the parent module name

Return type:

str | None

property name

Returns the function’s name.

Returns:

the function’s name

Return type:

str

property package_name

Returns the function’s package name.

Returns:

the package name

Return type:

str

property root_package_name

Returns the function’s root package name.

Returns:

the root package name

Return type:

str

logging_strict.tech_niques.context_locals.get_locals(func_path, func, /, *args, **kwargs)

Uses patch to retrieve the tested functions locals and return value!

See this module docs for example

Limitation: the function must end with a single return, not yield or raise.

Parameters:
Returns:

Tuple containing return value and the locals

Return type:

tuple[logging_strict.tech_niques.context_locals._T, dict[str, Any]]

Deprecated since version 1.6.0: get_locals_dynamic does not need func_path and has support for class methods.

Removal not planned. Very popular, widely used, transition low priority and will take time.

logging_strict.tech_niques.context_locals.get_locals_dynamic(func, /, *args, **kwargs)

Uses patch to retrieve the tested functions locals and return value!

See this module docs for example

Limitation: the function must end with a single return, not yield or raise.

Parameters:
Returns:

Tuple containing return value and the locals

Return type:

tuple[logging_strict.tech_niques.context_locals._T, dict[str, Any]]