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.
Since widgets do open a comm on `__init__`, I think code using them will expect that a widget has a connected comm. So I wouldn't catch the error. At least until someone has a need to use the widget classes when there's nothing for them to talk to. Which someone probably will...