ammt
ammt

Reputation: 149

How do I display the code of a Python function?

I defined a function in a Python interactive console (not idle, not ipython) and I needed to see it again many screenfuls later.

Example:

>>> def myf():  
...    print 1
...    print 2
...    print 3
...
>>>
>>> # a lot of scrolling
>>>  

Fake example output below (instead of "name 'print_function' is not defined") to demonstrate what I want:

>>> print_function(myf)
myf()
    print 1
    print 2
    print 3

Is there anything like print_function in Python? If not, how would you implement it?

Upvotes: 3

Views: 6154

Answers (8)

Steve
Steve

Reputation: 551

This is how I do it:

    import inspect as i
    import sys
    sys.stdout.write(i.getsource(MyFunction))

Prints it out nicely...

Upvotes: 0

alexisdevarennes
alexisdevarennes

Reputation: 5642

There are many ways to do this:

Without any imports:

print myf.func_code.co_code 

But you will most probably just end up with bytecode. This can be solved with: http://www.crazy-compilers.com/decompyle/

You can also go the inspect way:

import inspect
import mymodule
print inspect.getsource(mymodule.myf)

Or you can use dis:

import dis

dis.dis(myf)

Lastly, although not recommendable (Security wise) there is this:

funcstring = """def myf():  
    print 1
    print 2
    print 3"""
func = eval(funcstring)

func.source = funcstring

print func.source

Upvotes: 0

Mike McKerns
Mike McKerns

Reputation: 35247

While I'd generally agree that inspect is a good answer (as Martijn Pieters mentions), I'd disagree that you can't get the source code of objects defined in the interpreter. If you use dill.source.getsource from dill, you can get the source of functions and lambdas, even if they are defined interactively. It also can get the code for from bound or unbound class methods and functions defined in curries... however, you might not be able to compile that code without the enclosing object's code.

>>> from dill.source import getsource
>>> 
>>> def add(x,y):
...   return x+y
... 
>>> squared = lambda x:x**2
>>> 
>>> print getsource(add)
def add(x,y):
  return x+y

>>> print getsource(squared)
squared = lambda x:x**2

>>> 
>>> class Foo(object):
...   def bar(self, x):
...     return x*x+x
... 
>>> f = Foo()
>>> 
>>> print getsource(f.bar)
def bar(self, x):
    return x*x+x

>>> 

Upvotes: 2

user626998
user626998

Reputation:

It's a bit hackish but if this is something that you will be doing a lot, you can use the readline module and a function decorator.

class PrintableFunction(object):
    """A class that contains a function and its start and end points 
    in the readline history"""

    def __init__(self, func, start, end):
        self.start = start
        self.end = end
        self.func = func

    def __call__(self, *args):
        self.func(*args)

    def __str__(self):
        """Just get all the readline history lines from start to end and
        return them"""

        lines = []
        for i in range(self.start, self.end + 1):
            lines.append(readline.get_history_item(i))
        return "\n".join(lines)


class SavedFunction(object):
    """The actual function decorator. It takes one argument, the history 
    line that at which the function definition will begin."""

    def __init__(self, start):
        """Take the starting line as an argument to the decorator. The end
        line is grabbed from the current history after the function has 
        been entered"""

        self.start = start
        self.end = readline.get_current_history_length()

    def __call__(self, func):
        return PrintableFunction(func, self.start, self.end)

You can add these classes to your PYTHONSTARTUP file, so that every time you load an interpreter, you have them available.

>>> @SavedFunction(readline.get_current_history_length() + 1)
... def foo(bar):
...     print(bar)
>>> foo(5)
5
>>> print(foo)
def foo(bar):
    print(bar)

I've created a custom PS1 for myself (in my PYTHONSTARTUP file as well) that shows the current readline history number, meaning I can just quickly add it to the @saved_function argument list, which is easier than fetching it with the readline.get_current_history_length function as above:

[508] @SavedFunction(509)
(509) def foo(bar):
(510)     print(bar)
[511] print(foo)
def foo(bar):
    print(bar)

Upvotes: 2

inspectorG4dget
inspectorG4dget

Reputation: 114025

IPython has this lovely history lookup. Therefore, if you know the name of your function, you should be able to type in def myFunc( and press the up arrow key. This will make IPython recall the buffer and display to you the last inputted code that matches this starting. In your case, it will be the definition of your function, including the full function body. So that should do it for you!

Hope this helps

Upvotes: 0

Pawel Miech
Pawel Miech

Reputation: 7822

I was suprised when I've learned this but each function has a code method, so you can try to write something like:

>>> def f():
print "hello world" 
>>> f.__code__
<code object f at 02C57188, file "<pyshell#5>", line 1>

As you see it just gives you a reference to an object stored in memory, not an exact code of the function. However you can do something like:

>>> def roman():
print "Oh no!"
>>> roman.__code__ = f.__code__
>>> roman()
hello world
>>> 

I am not sure about that, but I think that you can also write the code to file and read it from this file later on. You will need to do some research on your own and tinker around with it, hope this helps somehow.

Upvotes: 0

en_Knight
en_Knight

Reputation: 5381

How formal do you want to be about this? If your response was something to the effect of "not very", then you could just copy the code of a function into the docstring and then print the docstring using doc

For example:

def foo():
""" Prints a sequence of numbers to the terminal:
    print 1
    print 2
"""
print 1
print 2

print foo()
--> 1
--> 2

print foo.__doc__
--> Prints a sequence of numbers to the terminal:
--> print 1
--> print 2

Upvotes: 0

Martijn Pieters
Martijn Pieters

Reputation: 1123420

You cannot do this in the regular console. iPython keeps a copy of the source in case you want to see it again later on, but the standard Python console does not.

Had you imported the function from a file, you could have used inspect.getsource():

>>> import os.path
>>> import inspect
>>> print inspect.getsource(os.path.join)
def join(a, *p):
    """Join two or more pathname components, inserting '/' as needed.
    If any component is an absolute path, all previous path components
    will be discarded.  An empty last part will result in a path that
    ends with a separator."""
    path = a
    for b in p:
        if b.startswith('/'):
            path = b
        elif path == '' or path.endswith('/'):
            path +=  b
        else:
            path += '/' + b
    return path

but, just to be explicit, inspect.getsource() will fail for functions entered in the interactive console:

>>> def foo(): pass
... 
>>> print inspect.getsource(foo)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 701, in getsource
    lines, lnum = getsourcelines(object)
  File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 690, in getsourcelines
    lines, lnum = findsource(object)
  File "/Users/mj/Development/Libraries/buildout.python/parts/opt/lib/python2.7/inspect.py", line 538, in findsource
    raise IOError('could not get source code')
IOError: could not get source code

because nothing in the interpreter retains the input (other than the readline library, which might save input history, just not in a format directly usable by inspect.getsource()).

Upvotes: 6

Related Questions