Question
Is there a reliable way to get a namespace containing Bar
in the following example?
Ideally I would like a way to get all parent nested namespaces between sys._getframe(1)
and the global namespace.
import sys
class MyBaseClass:
def __init_subclass__(cls, **kwargs):
class_local_frame = sys._getframe(1)
class_local_ns = {
k: v for k, v in class_local_frame.f_locals.items()
if not k.startswith('_')
}
print(class_local_ns)
return super().__init_subclass__(**kwargs)
# easy to get a namespace including Foo as it's global
Foo = 'this is Foo'
def nested1():
# is there a way to get a namespace containing Bar?
Bar = 'this is Bar'
def nested2():
# relatively easy to get a namespace containing Spam
# via sys._getframe(1).f_locals
Spam = 'this is Spam'
class MyClass(MyBaseClass):
b: Foo
return MyClass
return nested2
def calling_it():
Baz = 'this is baz'
return nested1()()
calling_it()
Note: this is NOT possible with class_local_frame.f_back.f_locals
- it would work in some scenarios, but in this case it would be the namespace inside calling_it
, not as desired the namespace the ns inside nested2
.
I’m basically looking for a way to get a namespace that accurately shadows how attributes would be accessible at runtime - e.g. when defining MyClass
I could use Foo
, Bar
and Spam
, but not Baz
.
Context
Some context for my question:
As per PEP 563: “Annotations can only use names present in the module scope…”.
Thus, if I call get_type_hints()
in __init_subclass__
with either from __future__ import annotations
or string annotations, by default it would raise an error if type hints referenced Bar
or Spam
since they’re not in the module scope.
@guido, mentioned here that I could use sys._getframe(1)
to access the frame where the class is defined, and that seems to be working well (implemented in pydantic#4663), thanks so much.
But that leaves a very weird edge case as demonstrated by the example above: Foo
will work fine, Spam
will work fine, but Bar
won’t. Even though at runtime (or with annotations as python objects) using Bar
would work fine.
To be clear: I understand this will come up every rarely, but when it does, it’ll be very unintuitive for users, hence I would love a way to get get_type_hints
to work as near as possible identically whether or not type hints are python objects.
Thanks in advance.