Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix IPython kernel-related cleanup #188

Merged
merged 42 commits into from
Sep 13, 2019
Merged

Fix IPython kernel-related cleanup #188

merged 42 commits into from
Sep 13, 2019

Conversation

mdickinson
Copy link
Member

@mdickinson mdickinson commented Sep 10, 2019

This PR shows a proof of concept for fixing cleanup issues in the InternalIPKernel and associated plugin. Should eventually address issues #94, #185, #186 and #187.

Currently WIP, so making this a draft PR. Opening a PR now (a) to get CI feedback and (b) to have a place outside the code to record things that are still to do.

Ready for review.

To do:

  • Lots of cleanup and removal of duplicated code.
  • Tests.
    • Test that sys.std*, sys.excepthook and sys.displayhook are all restored as expected
    • Test that IPython.utils.io globals unaltered
    • Test that no ZMQInteractiveShell or IPythonKernel objects remain after cleanup
  • Absorb changes from Redirect IPython's raw_print and raw_print_err to logging. #183 (they need extending to cover rprint and rprinte, which are still used in some versions of the code).
  • zmq Sockets are only being cleaned up through cyclic gc. Can we clean them up better?
  • investigate and fix the ResourceWarning from test_shutdown_closes_console_pipes

Note: commit history is very messy. Please be sure to squash and merge.

@codecov-io
Copy link

codecov-io commented Sep 10, 2019

Codecov Report

❗ No coverage uploaded for pull request base (master@4b8431c). Click here to learn what that means.
The diff coverage is 92.15%.

Impacted file tree graph

@@            Coverage Diff            @@
##             master     #188   +/-   ##
=========================================
  Coverage          ?   45.73%           
=========================================
  Files             ?      147           
  Lines             ?     4476           
  Branches          ?      506           
=========================================
  Hits              ?     2047           
  Misses            ?     2384           
  Partials          ?       45
Impacted Files Coverage Δ
...visage/plugins/ipython_kernel/internal_ipkernel.py 93.18% <100%> (ø)
envisage/plugins/ipython_kernel/heartbeat.py 81.81% <81.81%> (ø)
envisage/plugins/ipython_kernel/kernelapp.py 92.18% <92.18%> (ø)

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 4b8431c...ab5b258. Read the comment docs.

@mdickinson
Copy link
Member Author

I'm getting a ResourceWarning from one of the tests:

test_shutdown_closes_console_pipes (envisage.plugins.ipython_kernel.tests.test_internal_ipkernel.TestInternalIPKernel) ... 
To connect another client to this kernel, use:
    --existing kernel-95701.json
/Users/mdickinson/.edm/envs/envisage-test-3.6-null/lib/python3.6/subprocess.py:766: ResourceWarning: subprocess 95708 is still running
  ResourceWarning, source=self)
ok

@mdickinson
Copy link
Member Author

The ResourceWarning is a result of a semi-bug: when we clean up the consoles, we do a console.term() for each console, but don't then do a console.wait(); I've added that missing wait call, which sets the return code on the Popen object, which in turn allows the subprocess module to recognise that the child process has finished, and prevents the ResourceWarning from being issued.

@rahulporuri
Copy link
Contributor

I have no additional comments on this PR and any questions I had have been addressed.

# the existence and content of those messages, and for hiding the
# logger output during the test run).

class ListHandler(logging.Handler):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels generally useful, and may belong in apptools.logging.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code is now gone; I'll try to remember to open an apptools issue for this and for the output stream capturing before I close this PR.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@corranwebster corranwebster left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally looks good, with the caveat that I haven't done the digging into IPython to ensure that clean up is doing all the right things, but it looks reasonable and the tests seem comprehensive enough.

Furthermore, I think that architecturally it is the right sort of thing.

@mdickinson
Copy link
Member Author

There's something a bit fragile with the current tests and latest ipykernel and friends from PyPI. I have an intermittent exception that looks like this:

test_no_threads_leaked (envisage.plugins.ipython_kernel.tests.test_internal_ipkernel.TestInternalIPKernel) ... Exception in thread Thread-14:
Traceback (most recent call last):
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/Users/mdickinson/Enthought/ETS/envisage/envisage/plugins/ipython_kernel/heartbeat.py", line 37, in run
    super(Heartbeat, self).run()
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/site-packages/ipykernel/heartbeat.py", line 91, in run
    self._bind_socket()
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/site-packages/ipykernel/heartbeat.py", line 74, in _bind_socket
    self._try_bind_socket()
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/site-packages/ipykernel/heartbeat.py", line 61, in _try_bind_socket
    return self.socket.bind('%s://%s' % (self.transport, self.ip) + c + str(self.port))
  File "zmq/backend/cython/socket.pyx", line 550, in zmq.backend.cython.socket.Socket.bind
  File "zmq/backend/cython/checkrc.pxd", line 25, in zmq.backend.cython.checkrc._check_rc
zmq.error.ZMQError: Address already in use

ok

@mdickinson
Copy link
Member Author

Variant on the intermittent failure:

test_io_pub_thread_stopped (envisage.plugins.ipython_kernel.tests.test_internal_ipkernel.TestInternalIPKernel) ... Unhandled exception in thread started by <bound method Thread._bootstrap of <Heartbeat(Thread-4, started daemon 123145440456704)>>
Traceback (most recent call last):
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/threading.py", line 916, in _bootstrap_inner
    self.run()
  File "/Users/mdickinson/Enthought/ETS/envisage/envisage/plugins/ipython_kernel/heartbeat.py", line 30, in run
    super(Heartbeat, self).run()
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/site-packages/ipykernel/heartbeat.py", line 91, in run
    self._bind_socket()
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/site-packages/ipykernel/heartbeat.py", line 74, in _bind_socket
    self._try_bind_socket()
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/site-packages/ipykernel/heartbeat.py", line 61, in _try_bind_socket
    return self.socket.bind('%s://%s' % (self.transport, self.ip) + c + str(self.port))
  File "zmq/backend/cython/socket.pyx", line 550, in zmq.backend.cython.socket.Socket.bind
  File "zmq/backend/cython/checkrc.pxd", line 25, in zmq.backend.cython.checkrc._check_rc
zmq.error.ZMQError: Address already in use

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/threading.py", line 884, in _bootstrap
    self._bootstrap_inner()
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/threading.py", line 926, in _bootstrap_inner
    (self.name, _format_exc()), file=_sys.stderr)
  File "/Users/mdickinson/.edm/envs/envisage/lib/python3.6/site-packages/ipykernel/iostream.py", line 394, in write
    raise ValueError('I/O operation on closed file')
ValueError: I/O operation on closed file
ok

@mdickinson
Copy link
Member Author

@rahulporuri @corranwebster I think this is ready. I'm still investigating the intermittent failure, but it may make most sense for the fix for that to go into a separate PR.

@mdickinson
Copy link
Member Author

I'm still investigating the intermittent failure

This turned out to be an ipykernel bug; see ipython/ipykernel#430. We'll need to figure out how to work around it here, but that can happen in another PR.

@mdickinson
Copy link
Member Author

We'll need to figure out how to work around it here, but that can happen in another PR.

I think the easiest solution is to not towork around it and wait for ipykernel 5.1.3 (or later) to be released.

Merging!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants