commit 41e2ed4680f14f2140dce2a93a43c027aac58786
Author: Shikhar Bhushan <shikhar@schmizz.net>
Date:   Wed Apr 29 23:29:24 2009 +0000

    rpc's coming along, changes to logging
    
    git-svn-id: http://ncclient.googlecode.com/svn/trunk@87 6dbcf712-26ac-11de-a2f3-1373824ab735

diff --git a/ncclient/glue.py b/ncclient/glue.py
index 7500fc9..d361118 100644
--- a/ncclient/glue.py
+++ b/ncclient/glue.py
@@ -46,32 +46,38 @@ class Subject(Thread):
         self._listeners = set() # TODO(?) weakref
         self._lock = Lock()
     
-    def _dispatch_received(self, raw):
+    def _dispatch_message(self, raw):
         "TODO: docstring"
         root = parse_root(raw)
         with self._lock:
             listeners = list(self._listeners)
         for l in listeners:
-            logger.debug('[dispatching] message to %s' % l)
-            l.callback(root, raw)
+            logger.debug('dispatching message to %r' % l)
+            try:
+                l.callback(root, raw)
+            except Exception as e:
+                logger.warning('[error] %r' % e)
     
     def _dispatch_error(self, err):
         "TODO: docstring"
         with self._lock:
             listeners = list(self._listeners)
         for l in listeners:
-            logger.debug('[dispatching] error to %s' % l)
-            l.errback(err)
+            logger.debug('dispatching error to %r' % l)
+            try:
+                l.errback(err)
+            except Exception as e:
+                logger.warning('error %r' % e)
     
     def add_listener(self, listener):
         "TODO: docstring"
-        logger.debug('[installing listener] %r' % listener)
+        logger.debug('installing listener %r' % listener)
         with self._lock:
             self._listeners.add(listener)
     
     def remove_listener(self, listener):
         "TODO: docstring"
-        logger.debug('[discarding listener] %r' % listener)
+        logger.debug('discarding listener %r' % listener)
         with self._lock:
             self._listeners.discard(listener)
     
@@ -86,7 +92,7 @@ class Subject(Thread):
     
     def send(self, message):
         "TODO: docstring"
-        logger.debug('[queueing] %s' % message)
+        logger.debug('queueing %s' % message)
         self._q.put(message)
 
 
diff --git a/ncclient/operations/reply.py b/ncclient/operations/reply.py
index 7bf81cb..ceaedf5 100644
--- a/ncclient/operations/reply.py
+++ b/ncclient/operations/reply.py
@@ -17,10 +17,13 @@ from xml.etree import cElementTree as ET
 from ncclient.content import multiqualify as _
 from ncclient.content import unqualify as __
 
+import logging
+logger = logging.getLogger('ncclient.operations.reply')
+
 class RPCReply:
     
     def __init__(self, raw):
-        self._raw = None
+        self._raw = raw
         self._parsed = False
         self._errors = []
     
@@ -28,12 +31,14 @@ class RPCReply:
         return self._raw
     
     def parse(self):
-        root = ET.fromstring(raw) # <rpc-reply> element
+        root = ET.fromstring(self._raw) # <rpc-reply> element
         
         # per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
         oktags = _('ok')
         for oktag in oktags:
             if root.find(oktag) is not None:
+                logger.debug('found %s' % oktag)
+                self._parsed = True
                 return
         
         # create RPCError objects from <rpc-error> elements
@@ -46,6 +51,7 @@ class RPCReply:
                 self._errors.append(RPCError(d))
             if self._errors:
                 break
+        self._parsed = True
     
     @property
     def raw(self):
@@ -53,15 +59,13 @@ class RPCReply:
     
     @property
     def ok(self):
-        if not self._parsed:
-            self.parse()
-        return bool(self._errors) # empty list = false
+        if not self._parsed: self.parse()
+        return not bool(self._errors) # empty list = false
     
     @property
     def errors(self):
         'List of RPCError objects. Will be empty if no <rpc-error> elements in reply.'
-        if not self._parsed:
-            self.parse()
+        if not self._parsed: self.parse()
         return self._errors
 
 
diff --git a/ncclient/operations/rpc.py b/ncclient/operations/rpc.py
index 1687198..fe69530 100644
--- a/ncclient/operations/rpc.py
+++ b/ncclient/operations/rpc.py
@@ -46,14 +46,27 @@ class RPC(object):
         req = self._build(op)
         self._session.send(req)
         if self._async:
+            return self._reply_event
+        else:
             self._reply_event.wait()
             self._reply.parse()
             return self._reply
     
-    def deliver(self, raw):
+    def _set_reply(self, raw):
         self._reply = RPCReply(raw)
+    
+    def _set_reply_event(self):
         self._reply_event.set()
     
+    def _delivery_hook(self):
+        'For subclasses'
+        pass
+    
+    def deliver(self, raw):
+        self._set_reply(raw)
+        self._delivery_hook()
+        self._set_reply_event()
+    
     @property
     def has_reply(self):
         return self._reply_event.isSet()
@@ -63,6 +76,10 @@ class RPC(object):
         return self._reply
     
     @property
+    def is_async(self):
+        return self._async
+    
+    @property
     def id(self):
         return self._id
     
@@ -113,8 +130,8 @@ class RPCReplyListener(Listener):
     def set_errback(self, errback):
         self._errback = errback
 
-    def register(self, msgid, rpc):
-        self._id2rpc[msgid] = rpc
+    def register(self, id, rpc):
+        self._id2rpc[id] = rpc
     
     def callback(self, root, raw):
         tag, attrs = root
@@ -126,14 +143,17 @@ class RPCReplyListener(Listener):
                 try:
                     rpc = self._id2rpc[id]
                     rpc.deliver(raw)
-                except:
-                    logger.warning('RPCReplyListener.callback: no RPC '
+                except KeyError:
+                    logger.warning('[RPCReplyListener.callback] no RPC '
                                    + 'registered for message-id: [%s]' % id)
+                    logger.debug('[RPCReplyListener.callback] registered: %r '
+                                 % dict(self._id2rpc))
+                except Exception as e:
+                    logger.debug('[RPCReplyListener.callback] error - %r' % e)
                 break
         else:
             logger.warning('<rpc-reply> without message-id received: %s' % raw)
     
     def errback(self, err):
-        logger.error('RPCReplyListener.errback: %r' % err)
         if self._errback is not None:
             self._errback(err)
diff --git a/ncclient/operations/session.py b/ncclient/operations/session.py
index b92533c..0d225cb 100644
--- a/ncclient/operations/session.py
+++ b/ncclient/operations/session.py
@@ -16,22 +16,20 @@
 
 from rpc import RPC
 
-
 class CloseSession(RPC):
     
+    'CloseSession is always synchronous'
+    
     def __init__(self, *args, **kwds):
         RPC.__init__(self, *args, **kwds)
         self.spec = { 'tag': 'close-session' }
     
-    def deliver(self, reply):
-        RPC.deliver(self, reply)
-        # can't be too huge, should be ok to parse in callback
-        if self._reply.ok: # (implicitly parse)
-            self._session.expect_close()
-        self._session.close()
+    def _delivery_hook(self):
+        if self.reply.ok:
+            self.session.expect_close()
     
     def request(self):
-        self._request(self.spec)
+        return self._request(self.spec)
 
 
 class KillSession(RPC):
@@ -47,4 +45,4 @@ class KillSession(RPC):
         if not isinstance(session_id, basestring): # just make sure...
             session_id = str(session_id)
         self.spec['children'][0]['text'] = session_id
-        self._request(self.spec)
+        return self._request(self.spec)
diff --git a/ncclient/transport/__init__.py b/ncclient/transport/__init__.py
index 138f0c0..1e3fa8c 100644
--- a/ncclient/transport/__init__.py
+++ b/ncclient/transport/__init__.py
@@ -14,5 +14,6 @@
 
 "TODO: docstring"
 
-import logging
-logger = logging.getLogger('ncclient.transport')
+from ssh import SSHSession
+
+__all__ = ['SSHSession']
\ No newline at end of file
diff --git a/ncclient/transport/hello.py b/ncclient/transport/hello.py
index ee8693f..576be48 100644
--- a/ncclient/transport/hello.py
+++ b/ncclient/transport/hello.py
@@ -24,7 +24,8 @@ from ncclient.content import unqualify as __
 class HelloHandler(Listener):
     
     def __init__(self, init_cb, error_cb):
-        self._init_cb, self._error_cb = init_cb, error_cb
+        self._init_cb = init_cb
+        self._error_cb = error_cb
     
     def __str__(self):
         return 'HelloListener'
diff --git a/ncclient/transport/session.py b/ncclient/transport/session.py
index ca488cd..05e74e1 100644
--- a/ncclient/transport/session.py
+++ b/ncclient/transport/session.py
@@ -17,27 +17,28 @@ from threading import Event
 from ncclient.capabilities import Capabilities, CAPABILITIES
 from ncclient.glue import Subject
 
-from . import logger
 from hello import HelloHandler
 
+import logging
+logger = logging.getLogger('ncclient.transport.session')
+
 class Session(Subject):
     
     "TODO: docstring"
     
     def __init__(self):
-        "TODO: docstring"
+        "Subclass constructor should call this"
         Subject.__init__(self)
         self.setName('session')
-        self.setDaemon(True) #hmm
         self._client_capabilities = CAPABILITIES
         self._server_capabilities = None # yet
         self._id = None # session-id
         self._connected = False # to be set/cleared by subclass implementation
-        logger.debug('[session object created] client_capabilities=%r' %
-                     self._client_capabilities)
+        logger.debug('%r created: client_capabilities=%r' %
+                     (self, self._client_capabilities))
     
     def _post_connect(self):
-        "TODO: docstring"
+        "Greeting stuff"
         init_event = Event()
         error = [None] # so that err_cb can bind error[0]. just how it is.
         # callbacks
@@ -51,7 +52,7 @@ class Session(Subject):
         listener = HelloHandler(ok_cb, err_cb)
         self.add_listener(listener)
         self.send(HelloHandler.build(self._client_capabilities))
-        logger.debug('[starting main loop]')
+        logger.debug('starting main loop')
         self.start()
         # we expect server's hello message
         init_event.wait()
@@ -63,10 +64,11 @@ class Session(Subject):
                      (self._id, self._server_capabilities))
     
     def connect(self, *args, **kwds):
-        "TODO: docstring"
+        "Subclass implements"
         raise NotImplementedError
 
     def run(self):
+        "Subclass implements"
         raise NotImplementedError
     
     ### Properties
diff --git a/ncclient/transport/ssh.py b/ncclient/transport/ssh.py
index dcc0be0..1e6d508 100644
--- a/ncclient/transport/ssh.py
+++ b/ncclient/transport/ssh.py
@@ -20,10 +20,12 @@ from select import select
 
 import paramiko
 
-from . import logger
 from errors import AuthenticationError, SessionCloseError, SSHError, SSHUnknownHostError
 from session import Session
 
+import logging
+logger = logging.getLogger('ncclient.transport.ssh')
+
 BUF_SIZE = 4096
 MSG_DELIM = ']]>]]>'
 TICK = 0.1
@@ -42,7 +44,6 @@ class SSHSession(Session):
         # parsing-related, see _parse()
         self._parsing_state = 0 
         self._parsing_pos = 0
-        logger.debug('[SSHSession object created]')
     
     def _parse(self):
         '''Messages ae delimited by MSG_DELIM. The buffer could have grown by a
@@ -76,7 +77,8 @@ class SSHSession(Session):
             else: # if we didn't break out of the loop, full delim was parsed
                 msg_till = buf.tell() - n
                 buf.seek(0)
-                self._dispatch_received(buf.read(msg_till).strip())
+                logger.debug('parsed new message')
+                self._dispatch_message(buf.read(msg_till).strip())
                 buf.seek(n+1, os.SEEK_CUR)
                 rest = buf.read()
                 buf = StringIO()
@@ -170,8 +172,8 @@ class SSHSession(Session):
         self._connected = True # there was no error authenticating
         
         c = self._channel = self._transport.open_session()
-        c.invoke_subsystem('netconf')
         c.set_name('netconf')
+        c.invoke_subsystem('netconf')
         
         self._post_connect()
     
@@ -264,6 +266,7 @@ class SSHSession(Session):
                     else:
                         raise SessionCloseError(self._buffer.getvalue())
                 if not q.empty() and chan.send_ready():
+                    logger.debug('sending message')
                     data = q.get() + MSG_DELIM
                     while data:
                         n = chan.send(data)
@@ -271,7 +274,7 @@ class SSHSession(Session):
                             raise SessionCloseError(self._buffer.getvalue(), data)
                         data = data[n:]
         except Exception as e:
-            logger.debug('*** broke out of main loop ***')
+            logger.debug('broke out of main loop')
             self.close()
             if not (isinstance(e, SessionCloseError) and self._expecting_close):
                 self._dispatch_error(e)
diff --git a/ncclient/transport/util.py b/ncclient/transport/util.py
deleted file mode 100644
index e47c5c6..0000000
--- a/ncclient/transport/util.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2009 Shikhar Bhushan
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-#    http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from ncclient.glue import Listener
-
-import logging
-logger = logging.getLogger('DebugListener')
-
-class DebugListener(Listener):
-    
-    def __str__(self):
-        return 'DebugListener'
-    
-    def received(self, raw):
-        logger.debug('[received]:||%s||' % raw)
-    
-    def errback(self, err):
-        logger.debug('[error]:%r' % err)
diff --git a/ncclient/util.py b/ncclient/util.py
new file mode 100644
index 0000000..414aef9
--- /dev/null
+++ b/ncclient/util.py
@@ -0,0 +1,27 @@
+# Copyright 2009 Shikhar Bhushan
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#    http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from ncclient.glue import Listener
+
+import logging
+logger = logging.getLogger('PrintListener')
+
+class PrintListener(Listener):
+    
+    def callback(self, root, raw):
+        tag, attrs = root
+        print '\n$ RECEIVED MESSAGE with root=[tag=%r, attrs=%r]:\n%r\n' % (tag, attrs, raw)
+    
+    def errback(self, err):
+        print '\n$ RECEIVED ERROR:\n%r\n' % err
