Repository: ipython/ipython

Issue:
Contents Manager does not get passed config ?
I can't seem to have my custom contents manager receive a config.

In sessionmanager we seem to have `contents_manager = Instance('IPython.html.services.contents.manager.ContentsManager', args=())` but nowhere is it passed parent or config. Is that normal ?



Discussion:
actually it seams that because SessionManager does not have constructor, so when you passs it aroung in notebook_app it is not set. 

These objects are constructed in `NotebookApp.init_configurables`, so that default init in session_manager shouldn't ever be used. `parent` is passed. If config weren't passed, then you wouldn't be able to specify the notebook_dir, and that's not true. What config are you trying to pass?

Yeah, they are contructed there, but we do : 

```
 self.session_manager = self.session_manager_class(
            parent=self,
            log=self.log,
            kernel_manager=self.kernel_manager,
            contents_manager=self.contents_manager,
        )
```

Though, sessionmanager has no `__init__` to it, so as far as I can tell it reconstruct new ones without passing config or parents. 

I'm mostly trying to get a custom manager with custom config, and that does not work.

Even this config : 

```
{
  "nbformat": 1,
  "NotebookApp": {
    "tornado_settings": {
      "contents_manager_class": "jupyterdrive.mixednbmanager.MixedContentsManager",
      "contents_js_source": "nbextensions/gdrive/mixed-contents"
    }
  },
  "ContentsManager":{
      "filesystem_scheme": [
            {
                "root":"HD",
                "contents":"IPython.html.services.contents.filemanager.FileContentsManager"
            },
            {
                "root": "gdrive",
                "contents": "jupyterdrive.clientsidenbmanager.ClientSideContentsManager"
            }
        ]
  }

}
```

yield this log (with extra print statement):

```
Contents ::  () {'log': ..., 'parent': <IPython.html.notebookapp.NotebookApp object at 0x102a8a320>}
Contents.config ::  {..., 'ContentsManager': {....}}
=================
Contents ::  () {}
Contents.config ::  {}
```

`(){}` being `*args, **kwargs`
Which show that we are creating 2 instances one of them without configs. 

> Though, sessionmanager has no **init** to it,

HasTraits objects take keyword arguments to the constructor and set the traitlet attributes from those. Configurable objects additionally deal with `parent`. So it shouldn't need an explicit `__init__`.

Hum, when would these contents without config be created then ? Should I just ignore them ?

> Hum, when would these contents without config be created then ? Should I just ignore them ?

Should I just suppose that my custom content manager might get instanciated without config at some point and would be instanciated ok at a later point ?

Which one is it using? I think we should fix it so it's only instantiated once, but if the copy with the config is getting used, it's not so urgent.

I would guess that it's an ordering thing that something is referencing `self.contents_manager` before it gets set, and traitlets generates the default value.

> when would these contents without config be created then ?

probably created by the default_value generator, and never used. The default value stuff is really annoying.

> Should I just ignore them ?

yes

What's your class's name? `ContentsManager` doesn't have a `filesystem_scheme` trait. You need to use _your_ class's name.

> probably created by the default_value generator, and never used. The default value stuff is really annoying.

Ah, that could be it.

> What's your class's name? ContentsManager doesn't have a filesystem_scheme trait. You need to use your class's name.

Yes, but mine derive from it, so using `ContentsManager` does work too. 

I am failing early in mine  if config is not given, and did the same in my for of IPython to check if it was true with default `ContentsManager` (Hence the key) 

Not failing early seem to work, but it was very unintuitive that you could not be passed the config.
That prevent to raise an exception telling the user "Hey you should configure me".

We should just get rid of the `args=()` in SessionManager that causes it to be instantiated.


Resolving patch:
diff --git a/IPython/html/services/sessions/sessionmanager.py b/IPython/html/services/sessions/sessionmanager.py
--- a/IPython/html/services/sessions/sessionmanager.py
+++ b/IPython/html/services/sessions/sessionmanager.py
@@ -16,7 +16,7 @@
 class SessionManager(LoggingConfigurable):
 
     kernel_manager = Instance('IPython.html.services.kernels.kernelmanager.MappingKernelManager')
-    contents_manager = Instance('IPython.html.services.contents.manager.ContentsManager', args=())
+    contents_manager = Instance('IPython.html.services.contents.manager.ContentsManager')
     
     # Session database initialized below
     _cursor = None


Test patch:
diff --git a/IPython/html/services/sessions/tests/test_sessionmanager.py b/IPython/html/services/sessions/tests/test_sessionmanager.py
--- a/IPython/html/services/sessions/tests/test_sessionmanager.py
+++ b/IPython/html/services/sessions/tests/test_sessionmanager.py
@@ -6,6 +6,7 @@
 
 from ..sessionmanager import SessionManager
 from IPython.html.services.kernels.kernelmanager import MappingKernelManager
+from IPython.html.services.contents.manager import ContentsManager
 
 class DummyKernel(object):
     def __init__(self, kernel_name='python'):
@@ -28,10 +29,17 @@ def start_kernel(self, kernel_id=None, path=None, kernel_name='python', **kwargs
     def shutdown_kernel(self, kernel_id, now=False):
         del self._kernels[kernel_id]
 
+
 class TestSessionManager(TestCase):
     
+    def setUp(self):
+        self.sm = SessionManager(
+            kernel_manager=DummyMKM(),
+            contents_manager=ContentsManager(),
+        )
+
     def test_get_session(self):
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         session_id = sm.create_session(path='/path/to/test.ipynb',
                                        kernel_name='bar')['id']
         model = sm.get_session(session_id=session_id)
@@ -42,13 +50,13 @@ def test_get_session(self):
 
     def test_bad_get_session(self):
         # Should raise error if a bad key is passed to the database.
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         session_id = sm.create_session(path='/path/to/test.ipynb',
                                        kernel_name='foo')['id']
         self.assertRaises(TypeError, sm.get_session, bad_id=session_id) # Bad keyword
 
     def test_get_session_dead_kernel(self):
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         session = sm.create_session(path='/path/to/1/test1.ipynb', kernel_name='python')
         # kill the kernel
         sm.kernel_manager.shutdown_kernel(session['kernel']['id'])
@@ -59,7 +67,7 @@ def test_get_session_dead_kernel(self):
         self.assertEqual(listed, [])
 
     def test_list_sessions(self):
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         sessions = [
             sm.create_session(path='/path/to/1/test1.ipynb', kernel_name='python'),
             sm.create_session(path='/path/to/2/test2.ipynb', kernel_name='python'),
@@ -84,7 +92,7 @@ def test_list_sessions(self):
         self.assertEqual(sessions, expected)
 
     def test_list_sessions_dead_kernel(self):
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         sessions = [
             sm.create_session(path='/path/to/1/test1.ipynb', kernel_name='python'),
             sm.create_session(path='/path/to/2/test2.ipynb', kernel_name='python'),
@@ -107,7 +115,7 @@ def test_list_sessions_dead_kernel(self):
         self.assertEqual(listed, expected)
 
     def test_update_session(self):
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         session_id = sm.create_session(path='/path/to/test.ipynb',
                                        kernel_name='julia')['id']
         sm.update_session(session_id, path='/path/to/new_name.ipynb')
@@ -119,13 +127,13 @@ def test_update_session(self):
     
     def test_bad_update_session(self):
         # try to update a session with a bad keyword ~ raise error
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         session_id = sm.create_session(path='/path/to/test.ipynb',
                                        kernel_name='ir')['id']
         self.assertRaises(TypeError, sm.update_session, session_id=session_id, bad_kw='test.ipynb') # Bad keyword
 
     def test_delete_session(self):
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         sessions = [
             sm.create_session(path='/path/to/1/test1.ipynb', kernel_name='python'),
             sm.create_session(path='/path/to/2/test2.ipynb', kernel_name='python'),
@@ -147,7 +155,7 @@ def test_delete_session(self):
 
     def test_bad_delete_session(self):
         # try to delete a session that doesn't exist ~ raise error
-        sm = SessionManager(kernel_manager=DummyMKM())
+        sm = self.sm
         sm.create_session(path='/path/to/test.ipynb', kernel_name='python')
         self.assertRaises(TypeError, sm.delete_session, bad_kwarg='23424') # Bad keyword
         self.assertRaises(web.HTTPError, sm.delete_session, session_id='23424') # nonexistant