I'm writing an incredibly hackish piece of not-quite-production code in Python, and I need some way of detecting whether a _XYZ__foo attribute access was called from a method defined on a class called /_*XYZ/. It's not quite as easy as that, though, since I need to detect the original method access in case anything has overridden __getattribute__ and called super().
I'm bad at explaining, so... the rules are similar to Java's private, except I want to prevent cheating. (Yes, I know that this is counter to the Python philosophy; bear with me here.)
My current plan of attack is:
- Use
re.compile('_(?P<class>.*?)__(?P<name>.*)')to detect the name of the class (with preceding_s stripped). - Climb the
superchain withsys._getframe(n)to find out where the attribute access was. - Detect what class it was on... somehow. I'm stuck here.
I might be able to do this by emulating super's walking of the MRO, but I'd much rather rely on detection because checking what's been called by super and what's been called by user functions is hard.
So, to my actual question. Given a frame, how can I detect which class a method is associated with? If I had access to the function object I could do f.__qualname__[:-1-len(f.__name__)], but I don't (or, at least, I don't think I do). As is, I have no idea how to do this!
Here's a simple example that demonstrates what I want to do:
import sys
import re
import itertools
import builtins
from builtins import __build_class__
def build_class(func, name, *bases, metaclass=None, **kwds):
if bases[-1] is object:
bases = bases[:-1]
bases += HackishClass, object
if metaclass is None:
return __build_class__(func, name, *bases, **kwds)
return __build_class__(func, name, *bases, metaclass=metaclass, **kwds)
private_regex = re.compile('_(?P<class>.*?)__(?P<name>.*)')
class HackishClass:
__slots__ = ()
def __getattribute__(self, key):
match = private_regex.match(key)
if match is not None:
for depth in itertools.count(1):
frame = sys._getframe(depth)
if ...: # snip
# Check for the original attribute access here.
break
class_name = ... # HERE! MAGIC GOES HERE!
if class_name != match['class']:
raise AttributeError("This is private! Keep out.")
return super().__getattribute__(key)
builtins.__build_class__ = build_class
As far as I know, there's no way to obtain the method where the attribute access occurred directly from a frame object. We can, however, obtain that method's code object. We can then search the object's MRO until we find the method to which that code object belongs.
A small demonstration: