commit cc8de4688ece37bd40f622754471af761569610f
Author: Shikhar Bhushan <shikhar@schmizz.net>
Date:   Mon May 4 13:28:39 2009 +0000

    ...
    
    git-svn-id: http://ncclient.googlecode.com/svn/trunk@93 6dbcf712-26ac-11de-a2f3-1373824ab735

diff --git a/ncclient/content.py b/ncclient/content.py
index 0d61f8a..5c87014 100644
--- a/ncclient/content.py
+++ b/ncclient/content.py
@@ -54,7 +54,7 @@ class TreeBuilder:
     def __init__(self, spec):
         "TODO: docstring"
         self._root = TreeBuilder.build(spec)
-        
+    
     def to_string(self, encoding='utf-8'):
         "TODO: docstring"
         xml = ET.tostring(self._root, encoding)
@@ -86,3 +86,14 @@ class TreeBuilder:
             return ET.Comment(spec.get('comment'))
         else:
             raise ValueError('Invalid tree spec')
+
+class Parser:
+    pass
+
+class PartialParser(Parser):
+    
+    pass
+
+class RootParser(Parser):
+    
+    pass
diff --git a/ncclient/operations/notification.py b/ncclient/operations/notification.py
deleted file mode 100644
index 2611586..0000000
--- a/ncclient/operations/notification.py
+++ /dev/null
@@ -1,18 +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.
-
-
-class Notification:
-    
-    pass
diff --git a/ncclient/operations/reply.py b/ncclient/operations/reply.py
deleted file mode 100644
index 49c4ac6..0000000
--- a/ncclient/operations/reply.py
+++ /dev/null
@@ -1,139 +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 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 = raw
-        self._parsed = False
-        self._errors = []
-    
-    def __repr__(self):
-        return self._raw
-    
-    def parse(self):
-        if self._parsed: return
-        root = ET.fromstring(self._raw) # <rpc-reply> element
-        
-        if __(root.tag) != 'rpc-reply':
-            raise ValueError('Root element is not RPC reply')
-        
-        # 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('parsed [%s]' % oktag)
-                self._parsed = True
-                return
-        
-        # create RPCError objects from <rpc-error> elements
-        errtags = _('rpc-error')
-        for errtag in errtags:
-            for err in root.getiterator(errtag): # a particular <rpc-error>
-                logger.debug('parsed [%s]' % errtag)
-                d = {}
-                for err_detail in err.getchildren(): # <error-type> etc..
-                    tag = __(err_detail.tag)
-                    d[tag] = (err_detail.text.strip() if tag != 'error-info'
-                              else ET.tostring(err_detail, 'utf-8'))
-                self._errors.append(RPCError(d))
-            if self._errors:
-                break
-        
-        self._parsed = True
-    
-    @property
-    def raw(self):
-        return self._raw
-    
-    @property
-    def ok(self):
-        if not self._parsed: self.parse()
-        return not 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()
-        return self._errors
-
-
-class RPCError(Exception): # raise it if you like
-    
-    def __init__(self, err_dict):
-        self._dict = err_dict
-        if self.message is not None:
-            Exception.__init__(self, self.message)
-        else:
-            Exception.__init__(self)
-    
-    @property
-    def raw(self):
-        return self._element.tostring()
-    
-    @property
-    def type(self):
-        return self.get('error-type', None)
-    
-    @property
-    def severity(self):
-        return self.get('error-severity', None)
-    
-    @property
-    def tag(self):
-        return self.get('error-tag', None)
-    
-    @property
-    def path(self):
-        return self.get('error-path', None)
-    
-    @property
-    def message(self):
-        return self.get('error-message', None)
-    
-    @property
-    def info(self):
-        return self.get('error-info', None)
-
-    ## dictionary interface
-    
-    __getitem__ = lambda self, key: self._dict.__getitem__(key)
-    
-    __iter__ = lambda self: self._dict.__iter__()
-    
-    __contains__ = lambda self, key: self._dict.__contains__(key)
-    
-    keys = lambda self: self._dict.keys()
-    
-    get = lambda self, key, default: self._dict.get(key, default)
-        
-    iteritems = lambda self: self._dict.iteritems()
-    
-    iterkeys = lambda self: self._dict.iterkeys()
-    
-    itervalues = lambda self: self._dict.itervalues()
-    
-    values = lambda self: self._dict.values()
-    
-    items = lambda self: self._dict.items()
-    
-    __repr__ = lambda self: repr(self._dict)
diff --git a/ncclient/operations/rpc.py b/ncclient/operations/rpc.py
deleted file mode 100644
index 633e85f..0000000
--- a/ncclient/operations/rpc.py
+++ /dev/null
@@ -1,172 +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 threading import Event, Lock
-from uuid import uuid1
-from weakref import WeakValueDictionary
-
-from ncclient.content import TreeBuilder
-from ncclient.content import qualify as _
-from ncclient.content import unqualify as __
-from ncclient.glue import Listener
-
-from . import logger
-from reply import RPCReply
-
-
-# Cisco does not include message-id attribute in <rpc-reply> in case of an error.
-# This is messed up however we have to deal with it.
-# So essentially, there can be only one operation at a time if we are talking to
-# a Cisco device.
-
-def cisco_check(session):
-    try:
-        return session.is_remote_cisco
-    except AttributeError:
-        return False
-
-class RPC(object):
-    
-    def __init__(self, session, async=False):
-        if cisco_check(session) and async:
-            raise UserWarning('Asynchronous mode not supported for Cisco devices')
-        self._session = session
-        self._async = async
-        self._id = uuid1().urn
-        self._listener = RPCReplyListener(session)
-        self._listener.register(self._id, self)
-        self._reply = None
-        self._reply_event = Event()
-    
-    def _build(self, op, encoding='utf-8'):
-        if isinstance(op, dict):
-            return self.build_from_spec(self._id, op, encoding)
-        else:
-            return self.build_from_string(self._id, op, encoding)
-    
-    def _request(self, op):
-        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 _delivery_hook(self):
-        'For subclasses'
-        pass
-    
-    def deliver(self, raw):
-        self._reply = RPCReply(raw)
-        self._delivery_hook()
-        self._reply_event.set()
-    
-    @property
-    def has_reply(self):
-        return self._reply_event.isSet()
-    
-    @property
-    def reply(self):
-        return self._reply
-    
-    @property
-    def is_async(self):
-        return self._async
-    
-    @property
-    def id(self):
-        return self._id
-    
-    @property
-    def session(self):
-        return self._session
-    
-    @property
-    def reply_event(self):
-        return self._reply_event
-    
-    @staticmethod
-    def build_from_spec(msgid, opspec, encoding='utf-8'):
-        "TODO: docstring"
-        spec = {
-            'tag': _('rpc'),
-            'attributes': {'message-id': msgid},
-            'children': opspec
-            }
-        return TreeBuilder(spec).to_string(encoding)
-    
-    @staticmethod
-    def build_from_string(msgid, opstr, encoding='utf-8'):
-        "TODO: docstring"
-        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
-        doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
-               (msgid, BASE_NS, opstr)).encode(encoding)
-        return '%s%s' % (decl, doc)
-
-
-class RPCReplyListener(Listener):
-    
-    # TODO - determine if need locking
-    
-    # one instance per session
-    def __new__(cls, session):
-        instance = session.get_listener_instance(cls)
-        if instance is None:
-            instance = object.__new__(cls)
-            instance._id2rpc = WeakValueDictionary()
-            instance._cisco = cisco_check(session)
-            instance._errback = None
-            session.add_listener(instance)
-        return instance
-    
-    def __str__(self):
-        return 'RPCReplyListener'
-    
-    def set_errback(self, errback):
-        self._errback = errback
-
-    def register(self, id, rpc):
-        self._id2rpc[id] = rpc
-    
-    def callback(self, root, raw):
-        tag, attrs = root
-        if __(tag) != 'rpc-reply':
-            return
-        rpc = None
-        for key in attrs:
-            if __(key) == 'message-id':
-                id = attrs[key]
-                try:
-                    rpc = self._id2rpc.pop(id)
-                except KeyError:
-                    logger.warning('[RPCReplyListener.callback] no object '
-                                   + 'registered for message-id: [%s]' % id)
-                except Exception as e:
-                    logger.debug('[RPCReplyListener.callback] error - %r' % e)
-                break
-        else:
-            if self._cisco:
-                assert(len(self._id2rpc) == 1)
-                rpc = self._id2rpc.values()[0]
-                self._id2rpc.clear()
-            else:
-                logger.warning('<rpc-reply> without message-id received: %s' % raw)
-        logger.debug('[RPCReplyListener.callback] delivering to %r' % rpc)
-        rpc.deliver(raw)
-    
-    def errback(self, err):
-        if self._errback is not None:
-            self._errback(err)
diff --git a/ncclient/operations/subscribe.py b/ncclient/operations/subscribe.py
index e4be0c5..f58fdbe 100644
--- a/ncclient/operations/subscribe.py
+++ b/ncclient/operations/subscribe.py
@@ -12,7 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-# TODO...
+# TODO when can actually test it...
 
 from rpc import RPC
 
@@ -28,7 +28,8 @@ class CreateSubscription(RPC):
         'stream': None
     }
 
+class Notification:
+    pass
 
 class NotificationListener(Listener):
-    
     pass
diff --git a/ncclient/rpc/reply.py b/ncclient/rpc/reply.py
new file mode 100644
index 0000000..49c4ac6
--- /dev/null
+++ b/ncclient/rpc/reply.py
@@ -0,0 +1,139 @@
+# 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 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 = raw
+        self._parsed = False
+        self._errors = []
+    
+    def __repr__(self):
+        return self._raw
+    
+    def parse(self):
+        if self._parsed: return
+        root = ET.fromstring(self._raw) # <rpc-reply> element
+        
+        if __(root.tag) != 'rpc-reply':
+            raise ValueError('Root element is not RPC reply')
+        
+        # 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('parsed [%s]' % oktag)
+                self._parsed = True
+                return
+        
+        # create RPCError objects from <rpc-error> elements
+        errtags = _('rpc-error')
+        for errtag in errtags:
+            for err in root.getiterator(errtag): # a particular <rpc-error>
+                logger.debug('parsed [%s]' % errtag)
+                d = {}
+                for err_detail in err.getchildren(): # <error-type> etc..
+                    tag = __(err_detail.tag)
+                    d[tag] = (err_detail.text.strip() if tag != 'error-info'
+                              else ET.tostring(err_detail, 'utf-8'))
+                self._errors.append(RPCError(d))
+            if self._errors:
+                break
+        
+        self._parsed = True
+    
+    @property
+    def raw(self):
+        return self._raw
+    
+    @property
+    def ok(self):
+        if not self._parsed: self.parse()
+        return not 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()
+        return self._errors
+
+
+class RPCError(Exception): # raise it if you like
+    
+    def __init__(self, err_dict):
+        self._dict = err_dict
+        if self.message is not None:
+            Exception.__init__(self, self.message)
+        else:
+            Exception.__init__(self)
+    
+    @property
+    def raw(self):
+        return self._element.tostring()
+    
+    @property
+    def type(self):
+        return self.get('error-type', None)
+    
+    @property
+    def severity(self):
+        return self.get('error-severity', None)
+    
+    @property
+    def tag(self):
+        return self.get('error-tag', None)
+    
+    @property
+    def path(self):
+        return self.get('error-path', None)
+    
+    @property
+    def message(self):
+        return self.get('error-message', None)
+    
+    @property
+    def info(self):
+        return self.get('error-info', None)
+
+    ## dictionary interface
+    
+    __getitem__ = lambda self, key: self._dict.__getitem__(key)
+    
+    __iter__ = lambda self: self._dict.__iter__()
+    
+    __contains__ = lambda self, key: self._dict.__contains__(key)
+    
+    keys = lambda self: self._dict.keys()
+    
+    get = lambda self, key, default: self._dict.get(key, default)
+        
+    iteritems = lambda self: self._dict.iteritems()
+    
+    iterkeys = lambda self: self._dict.iterkeys()
+    
+    itervalues = lambda self: self._dict.itervalues()
+    
+    values = lambda self: self._dict.values()
+    
+    items = lambda self: self._dict.items()
+    
+    __repr__ = lambda self: repr(self._dict)
diff --git a/ncclient/rpc/rpc.py b/ncclient/rpc/rpc.py
new file mode 100644
index 0000000..633e85f
--- /dev/null
+++ b/ncclient/rpc/rpc.py
@@ -0,0 +1,172 @@
+# 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 threading import Event, Lock
+from uuid import uuid1
+from weakref import WeakValueDictionary
+
+from ncclient.content import TreeBuilder
+from ncclient.content import qualify as _
+from ncclient.content import unqualify as __
+from ncclient.glue import Listener
+
+from . import logger
+from reply import RPCReply
+
+
+# Cisco does not include message-id attribute in <rpc-reply> in case of an error.
+# This is messed up however we have to deal with it.
+# So essentially, there can be only one operation at a time if we are talking to
+# a Cisco device.
+
+def cisco_check(session):
+    try:
+        return session.is_remote_cisco
+    except AttributeError:
+        return False
+
+class RPC(object):
+    
+    def __init__(self, session, async=False):
+        if cisco_check(session) and async:
+            raise UserWarning('Asynchronous mode not supported for Cisco devices')
+        self._session = session
+        self._async = async
+        self._id = uuid1().urn
+        self._listener = RPCReplyListener(session)
+        self._listener.register(self._id, self)
+        self._reply = None
+        self._reply_event = Event()
+    
+    def _build(self, op, encoding='utf-8'):
+        if isinstance(op, dict):
+            return self.build_from_spec(self._id, op, encoding)
+        else:
+            return self.build_from_string(self._id, op, encoding)
+    
+    def _request(self, op):
+        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 _delivery_hook(self):
+        'For subclasses'
+        pass
+    
+    def deliver(self, raw):
+        self._reply = RPCReply(raw)
+        self._delivery_hook()
+        self._reply_event.set()
+    
+    @property
+    def has_reply(self):
+        return self._reply_event.isSet()
+    
+    @property
+    def reply(self):
+        return self._reply
+    
+    @property
+    def is_async(self):
+        return self._async
+    
+    @property
+    def id(self):
+        return self._id
+    
+    @property
+    def session(self):
+        return self._session
+    
+    @property
+    def reply_event(self):
+        return self._reply_event
+    
+    @staticmethod
+    def build_from_spec(msgid, opspec, encoding='utf-8'):
+        "TODO: docstring"
+        spec = {
+            'tag': _('rpc'),
+            'attributes': {'message-id': msgid},
+            'children': opspec
+            }
+        return TreeBuilder(spec).to_string(encoding)
+    
+    @staticmethod
+    def build_from_string(msgid, opstr, encoding='utf-8'):
+        "TODO: docstring"
+        decl = '<?xml version="1.0" encoding="%s"?>' % encoding
+        doc = (u'<rpc message-id="%s" xmlns="%s">%s</rpc>' %
+               (msgid, BASE_NS, opstr)).encode(encoding)
+        return '%s%s' % (decl, doc)
+
+
+class RPCReplyListener(Listener):
+    
+    # TODO - determine if need locking
+    
+    # one instance per session
+    def __new__(cls, session):
+        instance = session.get_listener_instance(cls)
+        if instance is None:
+            instance = object.__new__(cls)
+            instance._id2rpc = WeakValueDictionary()
+            instance._cisco = cisco_check(session)
+            instance._errback = None
+            session.add_listener(instance)
+        return instance
+    
+    def __str__(self):
+        return 'RPCReplyListener'
+    
+    def set_errback(self, errback):
+        self._errback = errback
+
+    def register(self, id, rpc):
+        self._id2rpc[id] = rpc
+    
+    def callback(self, root, raw):
+        tag, attrs = root
+        if __(tag) != 'rpc-reply':
+            return
+        rpc = None
+        for key in attrs:
+            if __(key) == 'message-id':
+                id = attrs[key]
+                try:
+                    rpc = self._id2rpc.pop(id)
+                except KeyError:
+                    logger.warning('[RPCReplyListener.callback] no object '
+                                   + 'registered for message-id: [%s]' % id)
+                except Exception as e:
+                    logger.debug('[RPCReplyListener.callback] error - %r' % e)
+                break
+        else:
+            if self._cisco:
+                assert(len(self._id2rpc) == 1)
+                rpc = self._id2rpc.values()[0]
+                self._id2rpc.clear()
+            else:
+                logger.warning('<rpc-reply> without message-id received: %s' % raw)
+        logger.debug('[RPCReplyListener.callback] delivering to %r' % rpc)
+        rpc.deliver(raw)
+    
+    def errback(self, err):
+        if self._errback is not None:
+            self._errback(err)
