The __qualname__ in contrast with name includes the information about enclosing classes or functions:
In [13]: class Symbol(type):
...: def __repr__(cls):
...: return cls.__qualname__
...:
...:
In [14]: class Dog(metaclass=Symbol): pass
In [15]: Dog
Out[15]: Dog
In [16]: class Animal:
...: class Dog(metaclass=Symbol): pass
...:
In [17]: Animal.Dog
Out[17]: Animal.Dog
Of course, if you really want to use Enums, Python's enum.Enumcan cover a wide number of use cases. Sometimes I just find it overkill, and use some simpler ad-hoc things, but once you are resorting to a custom metaclass, that is no longer "simpler", and you likely will have some corner cases ahead that enum has already solved.
Now, regardless of Enums, if one wants to use a metaclass and have information about the enclosing class body - as in the title of this question, the thing is trickier. For inside a class body definition, the variables in the class body itself are the local variables, and the global variables point to the module globals: there is no equivalent of nonlocal as when the enclosing scope is a function.
If an object that is set as a class attribute needs information about its owner class, Python implements the powerful descriptor procotol, used by the language built-in property. All you need is to have a __get__ method defined in your object's class, and take note of the owners attributes: no metaclasses needed:
In [56]: class Symbol: # not a metaclass
...: def __get__(self, instance, owner):
...: self.owner = owner
...: return self
...: def __set_name__(self, owner, name):
...: self.name = name
...: def __repr__(self):
...: return f"{self.owner.__name__}.{self.name}"
...:
...:
In [57]: class Animal:
...: Dog = Symbol()
...:
In [58]: Animal.Dog
Out[58]: Animal.Dog
Now, if one wants information on the enclosing scope at a nested class creation time, it is feasible, but one needs to introspect the stack in the metaclass' __new__ and find if locals in the enclosing scope is the body of another class. A strong hint of it is if it will have __module__ and __qualname__ keys defined. Then you have access to the enclosing class namespace, but...the enclosing class itself does not exist yet:
In [40]: import inspect
In [41]: class M(type):
...: def __new__(mcls, name, bases, ns, **kw):
...: enclosing_frame = inspect.stack()[1].frame
...: print (enclosing_frame.f_locals)
...: return super().__new__(mcls, name, bases, ns, **kw)
...:
In [42]: class A:
...: class B(metaclass=M): pass
...:
{'__module__': '__main__', '__qualname__': 'A'}
So, if all that is wanted is the enclosing class __name__, one can just use __qualname__ and be done with it. But if all one wanted was the name, __qualname__ itself is already set on the nested class namespace.
If one wants a valid reference to the __class__ itself, it is not possible when the nested class itself is created, because the outer class does not exist at this point: it will only be created at the end of the definition of its own body, of course.
Fortunately, the descriptor mechanism will work if the attribute is a class, and the __set_name__ special method is defined in its metaclass:
In [51]: class Symbol(type):
...: def __set_name__(cls, owner, name):
...: assert name == cls.__name__
...: cls.owner = owner
# it turns out __get__ is not needed for __set_name__ to work
...: #def __get__(cls, instance, owner):
...: # needed so the attribute is recognized as a descriptor (? - actually: to be checked)
...: # return cls
...: def __repr__(cls):
...: return f"{cls.owner.__name__}.{cls.__name__}"
...:
...:
In [52]: class Animal:
...: class Dog(metaclass=Symbol): pass
...:
In [53]: Animal.Dog
Out[53]: Animal.Dog
Again: this last part of the question is just here for people wanting to get a reference to the outer class for other purposes than getting the name. Now, check that the descriptor protocol, with the __get__ method can provide such information without the need for a metaclass at all.