Repository: holoviz/panel

Issue:
Bogus error message about VTK
Users get a bizarre error message if they try to make a pane out of None:

![image](https://user-images.githubusercontent.com/1695496/57634822-ad3e4300-756b-11e9-89b7-13df0c065dc0.png)

I encountered it because of a bug elsewhere in my code that resulted in a parameter returning None rather than an actual value, but the error message was _not_ very helpful in tracking that down...



Discussion:
I'd expect to see just the string "None", as a representation of None, just as strings, integers, and other Python types display themselves.
And note that in some cases, depending on out of order execution, it looks like it actually _is_ trying to display None using VTK, even though I have never run `pn.extension("vtk")`:

![image](https://user-images.githubusercontent.com/1695496/57635396-a82dc380-756c-11e9-84d5-d6303453cbbc.png)

I've seen this error a lot since the VTK merge. It has generally happened to me when I had something coded wrong and panel was trying to make sense of it. Seems like it was defaulting back to vtk? I could never come up with a good idea of what was happening so I never made an issue.   
  
edited to add: I have no vtk-related code which makes the error very bizarre
The VTK pane applies check is probably catching None's for some reason, should be an easy fix.
Yeah, this is a pretty bad check:

```python
def applies(cls, obj):
        return (isinstance(obj, getattr(vtk, 'vtkRenderWindow', type(None))) or
                hasattr(obj, 'read') or (isinstance(obj, string_types) and obj.endswith('.vtkjs')))
```
Will catch NoneTypes and pretty much any file-like type.
I imagine you can fix that easily enough. :-)  But regardless of the check, should we be expecting VTK objects to display themselves even without `pn.extension("vtk")`, but only sometimes?  That seems surprising to me as well.
>But regardless of the check, should we be expecting VTK objects to display themselves even without pn.extension("vtk"), but only sometimes?

Yes, they will but you will get a warning. That's expected behavior and less confusing for users than having it display only when you do something very specific (``pn.extension('vtk')``) which they may never know about otherwise.
It's expected that it will sometimes display, and sometimes give a warning, without changing any user code?  I can't quite tell when it displays and when I'll get a warning, but something about re-executing the same cell or the same notebook.  The warning is helpful, but it would be a lot easier to work with if it always warned or always worked, not some seemingly random switch between the two.
Hmm, I guess the problem currently is that the warning is based on whether the thing has been imported and it will have been imported after the first attempt. It should really warn if the model wasn't imported before running the extension, will have to think about that.
Right; that's what I'm thinking.  Something to make the behavior predictable and repeatable will really help the user work with Panel, by keeping the user's mental model simple and straightforward.
Can just keep a list of custom models that were loaded on extension load time and if the current model isn't one of those it will warn.

Resolving patch:
diff --git a/panel/pane/vtk.py b/panel/pane/vtk.py
--- a/panel/pane/vtk.py
+++ b/panel/pane/vtk.py
@@ -28,13 +28,10 @@
 
 import param
 
-from bokeh.util.dependencies import import_optional
 from pyviz_comms import JupyterComm
 
 from .base import PaneBase
 
-vtk = import_optional('vtk')
-
 arrayTypesMapping = '  bBhHiIlLfdL' # last one is idtype
 
 _js_mapping = {
@@ -87,6 +84,8 @@ def _dump_data_array(scDirs, datasetDir, dataDir, array, root={}, compress=True)
     if not array:
         return None
 
+    import vtk
+
     if array.GetDataType() == 12:
         # IdType need to be converted to Uint32
         arraySize = array.GetNumberOfTuples() * array.GetNumberOfComponents()
@@ -342,8 +341,13 @@ class VTK(PaneBase):
 
     @classmethod
     def applies(cls, obj):
-        return (isinstance(obj, getattr(vtk, 'vtkRenderWindow', type(None))) or
-                hasattr(obj, 'read') or (isinstance(obj, string_types) and obj.endswith('.vtkjs')))
+        if isinstance(obj, string_types) and obj.endswith('.vtkjs'):
+            return True
+        elif 'vtk' not in sys.modules:
+            return False
+        else:
+            import vtk
+            return isinstance(obj, vtk.vtkRenderWindow)
 
     def _get_model(self, doc, root=None, parent=None, comm=None):
         """
@@ -389,6 +393,8 @@ def _update(self, model):
         model.data = self._get_vtkjs()
 
     def _vtksjs_from_render_window(self, render_window):
+        import vtk
+
         render_window.OffScreenRenderingOn() # to not pop a vtk windows
         render_window.Render()
         renderers = render_window.GetRenderers()


Test patch: