#
Embedding IPython

It is possible to start an IPython instance inside your own Python programs. This allows you to evaluate dynamically the state of your code, operate with your variables, analyze them, etc. Note however that any changes you make to values while in the shell do not propagate back to the running code, so it is safe to modify your values because you won't break your code in bizarre ways by doing so.

This feature allows you to easily have a fully functional python environment for doing object introspection anywhere in your code with a simple function call. In some cases a simple print statement is enough, but if you need to do more detailed analysis of a code fragment this feature can be very valuable.

It can also be useful in scientific computing situations where it is common to need to do some automatic, computationally intensive part and then stop to look at data, plots, etc5. Opening an IPython instance will give you full access to your data and functions, and you can resume program execution once you are done with the interactive part (perhaps to stop again later, as many times as needed).

The following code snippet is the bare minimum you need to include in your Python programs for this to work (detailed examples follow later):

  from IPython.Shell import IPShellEmbed

  ipshell = IPShellEmbed()

  ipshell() # this call anywhere in your program will start IPython

You can run embedded instances even in code which is itself being run at the IPython interactive prompt with '`%run <filename>`'. Since it's easy to get lost as to where you are (in your top-level IPython or in your embedded one), it's a good idea in such cases to set the in/out prompts to something different for the embedded instances. The code examples below illustrate this.

You can also have multiple IPython instances in your program and open them separately, for example with different options for data presentation. If you close and open the same instance multiple times, its prompt counters simply continue from each execution to the next.

Please look at the docstrings in the `Shell.py` module for more details on the use of this system.

The following sample file illustrating how to use the embedding functionality is provided in the examples directory as `example-embed.py`. It should be fairly self-explanatory:

    #!/usr/bin/env python

    """An example of how to embed an IPython shell into a running program.

    Please see the documentation in the IPython.Shell module for more details.

    The accompanying file example-embed-short.py has quick code fragments for
    embedding which you can cut and paste in your code once you understand how
    things work.

    The code in this file is deliberately extra-verbose, meant for learning."""

    # The basics to get you going:

    # IPython sets the __IPYTHON__ variable so you can know if you have nested
    # copies running.

    # Try running this code both at the command line and from inside IPython (with
    # %run example-embed.py)
    try:
        __IPYTHON__
    except NameError:
        nested = 0
        args = ['']
    else:
        print "Running nested copies of IPython."
        print "The prompts for the nested copy have been modified"
        nested = 1
        # what the embedded instance will see as sys.argv:
        args = ['-pi1','In <\\#>: ','-pi2','   .\\D.: ',
                '-po','Out<\\#>: ','-nosep']

    # First import the embeddable shell class
    from IPython.Shell import IPShellEmbed

    # Now create an instance of the embeddable shell. The first argument is a
    # string with options exactly as you would type them if you were starting
    # IPython at the system command line. Any parameters you want to define for
    # configuration can thus be specified here.
    ipshell = IPShellEmbed(args,
                           banner = 'Dropping into IPython',
                           exit_msg = 'Leaving Interpreter, back to program.')

    # Make a second instance, you can have as many as you want.
    if nested:
        args[1] = 'In2<\\#>'
    else:
        args = ['-pi1','In2<\\#>: ','-pi2','   .\\D.: ',
                '-po','Out<\\#>: ','-nosep']
    ipshell2 = IPShellEmbed(args,banner = 'Second IPython instance.')

    print '\nHello. This is printed from the main controller program.\n'

    # You can then call ipshell() anywhere you need it (with an optional
    # message):
    ipshell('***Called from top level. '
            'Hit Ctrl-D to exit interpreter and continue program.')

    print '\nBack in caller program, moving along...\n'

    #---------------------------------------------------------------------------
    # More details:

    # IPShellEmbed instances don't print the standard system banner and
    # messages. The IPython banner (which actually may contain initialization
    # messages) is available as <instance>.IP.BANNER in case you want it.

    # IPShellEmbed instances print the following information everytime they
    # start:

    # - A global startup banner.

    # - A call-specific header string, which you can use to indicate where in the
    # execution flow the shell is starting.

    # They also print an exit message every time they exit.

    # Both the startup banner and the exit message default to None, and can be set
    # either at the instance constructor or at any other time with the
    # set_banner() and set_exit_msg() methods.

    # The shell instance can be also put in 'dummy' mode globally or on a per-call
    # basis. This gives you fine control for debugging without having to change
    # code all over the place.

    # The code below illustrates all this.

    # This is how the global banner and exit_msg can be reset at any point
    ipshell.set_banner('Entering interpreter - New Banner')
    ipshell.set_exit_msg('Leaving interpreter - New exit_msg')

    def foo(m):
        s = 'spam'
        ipshell('***In foo(). Try @whos, or print s or m:')
        print 'foo says m = ',m

    def bar(n):
        s = 'eggs'
        ipshell('***In bar(). Try @whos, or print s or n:')
        print 'bar says n = ',n

    # Some calls to the above functions which will trigger IPython:
    print 'Main program calling foo("eggs")\n'
    foo('eggs')

    # The shell can be put in 'dummy' mode where calls to it silently return. This
    # allows you, for example, to globally turn off debugging for a program with a
    # single call.
    ipshell.set_dummy_mode(1)
    print '\nTrying to call IPython which is now "dummy":'
    ipshell()
    print 'Nothing happened...'
    # The global 'dummy' mode can still be overridden for a single call
    print '\nOverriding dummy mode manually:'
    ipshell(dummy=0)

    # Reactivate the IPython shell
    ipshell.set_dummy_mode(0)

    print 'You can even have multiple embedded instances:'
    ipshell2()

    print '\nMain program calling bar("spam")\n'
    bar('spam')

    print 'Main program finished. Bye!'

    #********************** End of file <example-embed.py> ***********************

Once you understand how the system functions, you can use the following code fragments in your programs which are ready for cut and paste:

    """Quick code snippets for embedding IPython into other programs.

    See example-embed.py for full details, this file has the bare minimum code for
    cut and paste use once you understand how to use the system."""

    #---------------------------------------------------------------------------
    # This code loads IPython but modifies a few things if it detects it's running
    # embedded in another IPython session (helps avoid confusion)

    try:
        __IPYTHON__
    except NameError:
        argv = ['']
        banner = exit_msg = ''
    else:
        # Command-line options for IPython (a list like sys.argv)
        argv = ['-pi1','In <\\#>:','-pi2','   .\\D.:','-po','Out<\\#>:']
        banner = '*** Nested interpreter ***'
        exit_msg = '*** Back in main IPython ***'

    # First import the embeddable shell class
    from IPython.Shell import IPShellEmbed
    # Now create the IPython shell instance. Put ipshell() anywhere in your code
    # where you want it to open.
    ipshell = IPShellEmbed(argv,banner=banner,exit_msg=exit_msg)

    #---------------------------------------------------------------------------
    # This code will load an embeddable IPython shell always with no changes for
    # nested embededings.

    from IPython.Shell import IPShellEmbed
    ipshell = IPShellEmbed()
    # Now ipshell() will open IPython anywhere in the code.

    #---------------------------------------------------------------------------
    # This code loads an embeddable shell only if NOT running inside
    # IPython. Inside IPython, the embeddable shell variable ipshell is just a
    # dummy function.

    try:
        __IPYTHON__
    except NameError:
        from IPython.Shell import IPShellEmbed
        ipshell = IPShellEmbed()
        # Now ipshell() will open IPython anywhere in the code
    else:
        # Define a dummy ipshell() so the same code doesn't crash inside an
        # interactive IPython
        def ipshell(): pass

    #******************* End of file <example-embed-short.py> ********************