'Add a new background job and start it in a separate thread.
There are two types of jobs which can be created:
1. Jobs based on expressions which can be passed to an eval() call.
The expression must be given as a string.  For example:
job_manager.new(\'myfunc(x,y,z=1)\'[,glob[,loc]])
The given expression is passed to eval(), along with the optional
global/local dicts provided.  If no dicts are given, they are
extracted automatically from the caller\'s frame.
A Python statement is NOT a valid eval() expression.  Basically, you
can only use as an eval() argument something which can go on the right
of an \'=\' sign and be assigned to a variable.
For example,"print \'hello\'" is not valid, but \'2+3\' is.
2. Jobs given a function object, optionally passing additional
positional arguments:
job_manager.new(myfunc, x, y)
The function is called with the given arguments.
If you need to pass keyword arguments to your function, you must
supply them as a dict named kw:
job_manager.new(myfunc, x, y, kw=dict(z=1))
The reason for this assymmetry is that the new() method needs to
maintain access to its own keywords, and this prevents name collisions
between arguments to new() and arguments to your own functions.
In both cases, the result is stored in the job.result field of the
background job object.
You can set `daemon` attribute of the thread by giving the keyword
argument `daemon`.
Notes and caveats:
1. All threads running share the same standard output.  Thus, if your
background jobs generate output, it will come out on top of whatever
you are currently writing.  For this reason, background jobs are best
used with silent functions which simply return their output.
2. Threads also all work within the same global namespace, and this
system does not lock interactive variables.  So if you send job to the
background which operates on a mutable object for a long time, and
start modifying that same mutable object interactively (or in another
backgrounded job), all sorts of bizarre behaviour will occur.
3. If a background job is spending a lot of time inside a C extension
module which does not release the Python Global Interpreter Lock
(GIL), this will block the IPython prompt.  This is simply because the
Python interpreter can only switch between threads at Python
bytecodes.  While the execution is inside C code, the interpreter must
simply wait unless the extension module releases the GIL.
4. There is no way, due to limitations in the Python threads library,
to kill a thread once it has started.'

def new(self, func_or_exp, *args, **kwargs):

    if callable(func_or_exp):
        kw = kwargs.get('kw', {})
        job = BackgroundJobFunc(func_or_exp, *args, **kw)
    elif isinstance(func_or_exp, str):
        if (not args):
            frame = sys._getframe(1)
            (glob, loc) = (frame.f_globals, frame.f_locals)
        elif (len(args) == 1):
            glob = loc = args[0]
        elif (len(args) == 2):
            (glob, loc) = args
        else:
            raise ValueError('Expression    jobs    take    at    most    2    args    (globals,locals)')
        job = BackgroundJobExpr(func_or_exp, glob, loc)
    else:
        raise TypeError('invalid    args    for    new    job')
    if kwargs.get('daemon', False):
        job.daemon = True
    job.num = ((len(self.all) + 1) if self.all else 0)
    self.running.append(job)
    self.all[job.num] = job
    print ('Starting    job    #    %s    in    a    separate    thread.' % job.num)
    job.start()
    return job
