I've been trying to use the python builtin inspect.getclosurevars to identify global variables that sneak into functions, with the goal of providing users a way to guard against this behavior in jupyter notebooks, where scope management can be very difficult.
However, I noticed that object attributes are classified either as unbound or globals, depending on whether another object has been declared with the attribute's name or not.
In both of the following cases, I did not expect 'name' or 'age' to show up in any of these lists. It's not what I understand to be an unbound variable:
In [1]: import inspect
In [2]: def get_persons_name(person):
...: return person.name
...:
In [3]: inspect.getclosurevars(get_persons_name)
Out[3]: ClosureVars(nonlocals={}, globals={}, builtins={}, unbound={'name'})
And it's certainly not a global:
In [4]: age = 4
...:
...: def get_persons_age(person):
...: return person.age
...:
In [5]: inspect.getclosurevars(get_persons_age)
Out[5]: ClosureVars(nonlocals={}, globals={'age': 4}, builtins={}, unbound=set())
Is this a bug in inspect? Or am I misunderstanding something fundamental? If so, is there a better way to distinguish between attributes and true global/unbound variables?
I'm using python 3.7.6, tested on a jupyter notebook and ipython & python prompts, macos & ubuntu.