Repository: ipython/ipython

Issue:
BackgroundJobManager wrong job.num's are assigned after flush call
Hello, there is a problem with how `job.num` is calculated.

### What I do:
I try to run the following code
```python
from IPython.lib import backgroundjobs as bg

def forever():
    while True:
        pass
    return 'Done'

def once():
    return 'Done'

jm = bg.BackgroundJobManager()
jm.new(forever)
jm.new(once)
jm.new(once)
jm.new(forever)
jm.flush()
jm.new(forever)
jm.new(once)
jm.new(once)
jm.new(once)
jm.flush()
```
### What is wrong
I got an exception when calling `jm.flush()` second time
```
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-35-a03db7172b42> in <module>
     19 jm.new(once)
     20 jm.new(once)
---> 21 jm.flush()

~/.virtualenvs/ipython3/lib/python3.7/site-packages/IPython/lib/backgroundjobs.py in flush(self)
    320         alljobs = self.all
    321         for job in self.completed+self.dead:
--> 322             del(alljobs[job.num])
    323 
    324         # Now flush these lists completely

KeyError: 4
```

### My guess
It is connected to the way job.num is calculated here: https://github.com/ipython/ipython/blob/master/IPython/lib/backgroundjobs.py#L190
```python
# when we call flush first time in the example above len(self.all) becomes 2 
job.num = len(self.all)+1 if self.all else 0 
self.running.append(job)
 # and now we count from 2 despite that we have running job with id=4 already
self.all[job.num] = job 
```

Also, there is second bug in the calculation:
we could never create a job with `id=1` as it will be 0 if `self.all` is empty or 1 + 1 if there is one job in `self.all`

### How to fix
My suggestion is to create new instance field `self._current_job_id = 0`, and increment it on every new job creation.
With that, we'll get a counter that will be tied to BackgroundJobManager instance only and not to the current jobs count

If this issue will be confirmed I probably will create a fix myself.


Discussion:
@meeseeksdev tag bug, lib/backgroundjobs
Aww sintell, I was not able to apply the following label(s): `lib/backgroundjobs`,`bug`. Either because they are not existing labels on this repository or because you do not have the permission to apply these.I tried my best to guess by looking at the casing, but was unable to find matching labels.

Resolving patch:
diff --git a/IPython/lib/backgroundjobs.py b/IPython/lib/backgroundjobs.py
--- a/IPython/lib/backgroundjobs.py
+++ b/IPython/lib/backgroundjobs.py
@@ -86,6 +86,7 @@ def __init__(self):
         self._s_running   = BackgroundJobBase.stat_running_c
         self._s_completed = BackgroundJobBase.stat_completed_c
         self._s_dead      = BackgroundJobBase.stat_dead_c
+        self._current_job_id = 0
 
     @property
     def running(self):
@@ -187,7 +188,8 @@ def new(self, func_or_exp, *args, **kwargs):
 
         if kwargs.get('daemon', False):
             job.daemon = True
-        job.num = len(self.all)+1 if self.all else 0
+        job.num = self._current_job_id
+        self._current_job_id += 1
         self.running.append(job)
         self.all[job.num] = job
         debug('Starting job # %s in a separate thread.' % job.num)


Test patch: