tags:

views:

95

answers:

3

I guess the subject sounds pretty stupid, so I'll show some code:

def foo(**kwargs):
    # How can you detect the difference between (**{}) and ()?
    pass
foo(**{})
foo()

Is there any way to detect inside of foo, how the method was called?

Update 1

Because there were some comments why you possible want to do something, I'll try to explain some background.

super(MyClass, self).foo(*args, **kwargs) sucks - a lot of wasteful duplication. I want to write 'self.super()'. In this case, just call the super class and hand over all parameters that the calling method got as well. Works like a charm.

Now the problematic magic part:

I want to say 'self.super(something)' and in this case, only 'something' is passed to the super method. Works for most cases.

This is where it breaks:

def foo(self, fnord=42, *args, **kwargs):
    self.super(*args, **kwargs)

So the super method should get the arguments that the calling method - however if *args, **kwargs are empty, currently the library can not detect this condition and passed all arguments including 'fnord'...

Of course I could use self.super.foo(*args, **kwargs) as an alternative syntax but that's lame :-)

PS: Yes, I know p3k's super, but still not nice and it does not work with Python 2.x...

Update 2

Actually even Python's ast module removes the **{} (ast.parse('foo(**{})')) so it looks like this happens so early in the parsing process that you can not get this information later on...

So in the end I have either to give up on that specific problem (raising an AmbiguousSyntaxError) or to use text parsing as proposed by ~unutbu. Re-thinking my approach, the latter might actually feasable because I only need to know if it is self.super(\s*), self.super(\S+).

A: 

This hack only works with CPython.

import traceback

def foo(**kwargs):
    # stack is a list of 4-tuples: (filename, line number, function name, text)
    # see http://docs.python.org/library/traceback.html#module-traceback

    (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
    print('foo was called: %s'%text)

foo(**{})
# foo was called: foo(**{})
foo()
# foo was called: foo()

As an example of how this might be useful:

def pv(var):
    (filename,line_number,function_name,text)=traceback.extract_stack()[-2]
    print('%s: %s'%(text[text.find('(')+1:-1],var))

x=5
pv(x)
# x: 5

Notice that pv is called with just the value x, but it prints both the "name" of the variable (as it was called), and the value of a variable. Sometimes I use this when debugging and am too lazy to write out the full print statement.

unutbu
yes, however that only gives me the source representation, not the real syntax tree. Building a custom parser for that seems to be awfully error-prone. I hoped that I can get access to the specific syntax tree that Python is using 'right now' (so the 'live' tree, not a similar representation parsed later). Actually it 'should' not be difficult but looks like Python does not provide this level of introspection.
Felix Schwarz
A: 

In your update to your original question you wrote you want to be able to do something like this:

def foo(self, fnord=42, *args, **kwargs):
    self.super(*args, **kwargs)

Does this do what you want?

def foo(self, fnord=42, *args, **kwargs):
    self.super(fnord=fnord, *args, **kwargs)
Bryan Oakley
yes, of course. But suppose the fnord parameter is only used by a subclass so I do not want to pass it to the super class. So the question is how I can detect if `*args, **kwargs` (even if empty) were used in self.super.
Felix Schwarz
A: 

So basically it is not possible to do that with the provided Python modules. I had to fall-back to plain-text regexes which work for me pretty well given the simplicity of my requirements.

Felix Schwarz