Make comm manager (mostly) independent of InteractiveShell

This makes it possible to use comms from wrapper kernels, without instantiating the full IPython shell machinery.

The one remaining place where we need a reference to shell is to fire pre_execute and post_execute hooks (which are needed to get mpl figures right). This is a pure IPythonism, that it should be safe to ignore if shell is not set.

Ping @dsblank - you were asking about this in #6314.
...which is to say, I think it's fine for `open` to raise, but I might catch the RuntimeError in `__init__`.