2

Is there anyway to do something like this:

class A:
 def foo(self):
 if isinstance(caller, B):
 print "B can't call methods in A"
 else:
 print "Foobar"
class B:
 def foo(self, ref): ref.foo()
class C:
 def foo(self, ref): ref.foo()
a = A();
B().foo(a) # Outputs "B can't call methods in A"
C().foo(a) # Outputs "Foobar"

Where caller in A uses some form of introspection to determine the class of the calling method's object?

EDIT:

In the end, I put this together based on some of the suggestions:

import inspect
...
def check_caller(self, klass):
 frame = inspect.currentframe()
 current = lambda : frame.f_locals.get('self')
 while not current() is None:
 if isinstance(current(), klass): return True
 frame = frame.f_back
 return False

It's not perfect for all the reasons supplied, but thanks for the responses: they were a big help.

ekhumoro
122k23 gold badges272 silver badges400 bronze badges
asked Sep 30, 2009 at 11:58
1
  • 2
    You mention you have loads of classes calling each other. You seem to try to insert some sort of "security" where you restrict which classes are allowed to do what. This is in general a bad idea. Please explain why you want to do this, and maybe somebody can come up with a better solution to the usecase. Commented Sep 30, 2009 at 12:41

3 Answers 3

6

Assuming the caller is a method, then yes you can, by looking in the previous frame, and picking out self from the locals.

class Reciever:
 def themethod(self):
 frame = sys._getframe(1)
 arguments = frame.f_code.co_argcount
 if arguments == 0:
 print "Not called from a method"
 return
 caller_calls_self = frame.f_code.co_varnames[0]
 thecaller = frame.f_locals[caller_calls_self]
 print "Called from a", thecaller.__class__.__name__, "instance"

Üglŷ as heck, but it works. Now why you would want to do this is another question altogether, I suspect that there is a better way. The whole concept of A isn't allowed to call B is likely to be a mistake.

answered Sep 30, 2009 at 12:20
Sign up to request clarification or add additional context in comments.

5 Comments

fails in a lot of situations: calling the method top-level, calling the method in a free function, using another name for self (use of self is just a convention), etc
Yup. I agree that passing the caller in is a better solution.
You can see whether caller has arguments from frame.f_code.co_argcount and see the name of the first argument in frame.f_code.co_varnames[0].
Good idea! I updated the method to do that. It got even uglier. :) I still think passing the caller is a better solution though. In the ZCA similar things to this is done all the time between different components, and you always get the adapted component as context. Works fine.
This is what I was looking for. It's not beautiful, but interesting.
4

The caller is always an instance of A. The fact that you're calling it inside a B method doesn't change that. In other words: Insiode B.foo, ref is an instance of A, so calling ref.foo() is a call on A, B is not involved on that call (it could happen top-level).

The only sane way is to pass a reference to self so A can check if it is B or not.

class A(object):
 def foo(self, caller=None):
 if isinstance(caller, B):
 print "B can't call methods in A"
 else:
 print "Foobar"
class B(object):
 def foo(self, ref): ref.foo(self)
class C(object):
 def foo(self, ref): ref.foo(self)
a = A();
B().foo(a) # Outputs "B can't call methods in A"
C().foo(a) # Outputs "Foobar"
a.foo() # Outputs "Foobar"
answered Sep 30, 2009 at 12:02

3 Comments

Looking to do this transparently. It's unmanageable when the number of methods increases significantly.
@blakef: I've edited my question to why this is a bad idea. The call could happen top-level. Trying to detect where the call happened signals misdesign. You should instead share why on earth you want to prevent methods of one class to call methods on another.
The question became academic in the end. Your solution is the better approach, but I was interested in how it could be done without involving the caller's code.
-1

Something like this may meet your needs better:

class A(object):
 def foo(self):
 # do stuff
class B(A):
 def foo(self):
 raise NotImplementedError
class C(A):
 pass

...but it's difficult to say without knowing exactly what you're trying to do.

answered Sep 30, 2009 at 13:58

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.