commit 9667bcb264fe302a8d6b10dd2c4711b6b796ba5d
Author: Shikhar Bhushan <shikhar@schmizz.net>
Date:   Tue Sep 15 15:15:10 2009 +0000

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

diff --git a/ncclient/capabilities.py b/ncclient/capabilities.py
index 473dece..0905708 100644
--- a/ncclient/capabilities.py
+++ b/ncclient/capabilities.py
@@ -12,27 +12,27 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-_capability_map = {
-    'urn:liberouter:params:netconf:capability:power-control:1.0':
-        [':power-control', ':power-control:1.0']
-}
+#_capability_map = {
+#    "urn:liberouter:params:netconf:capability:power-control:1.0":
+#        [":power-control", ":power-control:1.0"]
+#}
 
 def _abbreviate(uri):
-    if uri.startswith('urn:ietf:params:netconf:'):
-        splitted = uri.split(':')
-        if ':capability:' in uri:
-            return [ ':' + splitted[5], ':' + splitted[5] + ':' + splitted[6] ]
-        elif ':base:' in uri:
-            return [ ':base', ':base' + ':'+ splitted[5] ]
-    elif uri in _capability_map:
-        return _capability_map[uri]
+    if uri.startswith("urn:ietf:params:netconf:"):
+        splitted = uri.split(":")
+        if ":capability:" in uri:
+            return [ ":" + splitted[5], ":" + splitted[5] + ":" + splitted[6] ]
+        elif ":base:" in uri:
+            return [ ":base", ":base" + ":" + splitted[5] ]
+    #elif uri in _capability_map:
+    #    return _capability_map[uri]
     return []
 
 def schemes(url_uri):
     """Given a URI that has a *scheme* query string (i.e. *:url* capability
     URI), will return a list of supported schemes.
     """
-    return url_uri.partition("?scheme=")[2].split(',')
+    return url_uri.partition("?scheme=")[2].split(",")
 
 class Capabilities:
 
@@ -98,16 +98,17 @@ class Capabilities:
 
 #: :class:`Capabilities` object representing the capabilities currently supported by NCClient
 CAPABILITIES = Capabilities([
-    'urn:ietf:params:netconf:base:1.0',
-    'urn:ietf:params:netconf:capability:writable-running:1.0',
-    'urn:ietf:params:netconf:capability:candidate:1.0',
-    'urn:ietf:params:netconf:capability:confirmed-commit:1.0',
-    'urn:ietf:params:netconf:capability:rollback-on-error:1.0',
-    'urn:ietf:params:netconf:capability:startup:1.0',
-    'urn:ietf:params:netconf:capability:url:1.0?scheme=http,ftp,file,https,sftp',
-    'urn:ietf:params:netconf:capability:validate:1.0',
-    'urn:ietf:params:netconf:capability:xpath:1.0',
-    'urn:liberouter:params:netconf:capability:power-control:1.0'
+    "urn:ietf:params:netconf:base:1.0",
+    "urn:ietf:params:netconf:capability:writable-running:1.0",
+    "urn:ietf:params:netconf:capability:candidate:1.0",
+    "urn:ietf:params:netconf:capability:confirmed-commit:1.0",
+    "urn:ietf:params:netconf:capability:rollback-on-error:1.0",
+    "urn:ietf:params:netconf:capability:startup:1.0",
+    "urn:ietf:params:netconf:capability:url:1.0?scheme=http,ftp,file,https,sftp",
+    "urn:ietf:params:netconf:capability:validate:1.0",
+    "urn:ietf:params:netconf:capability:xpath:1.0",
+    "urn:liberouter:params:netconf:capability:power-control:1.0"
+    "urn:ietf:params:netconf:capability:interleave:1.0"
     #'urn:ietf:params:netconf:capability:notification:1.0', # TODO
-    #'urn:ietf:params:netconf:capability:interleave:1.0' # theoretically already supported
+    
 ])
diff --git a/ncclient/operations/edit.py b/ncclient/operations/edit.py
index c263a92..55b6f67 100644
--- a/ncclient/operations/edit.py
+++ b/ncclient/operations/edit.py
@@ -12,25 +12,21 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-from copy import deepcopy
-
-from ncclient import xml_
+from ncclient.xml_ import *
 
 from rpc import RPC
 
 import util
 
 import logging
-logger = logging.getLogger('ncclient.operations.edit')
+logger = logging.getLogger("ncclient.operations.edit")
 
 "Operations related to changing device configuration"
 
 class EditConfig(RPC):
 
     "*<edit-config>* RPC"
-
-    SPEC = {'tag': 'edit-config', 'subtree': []}
-
+    
     def request(self, target, config, default_operation=None, test_option=None,
                 error_option=None):
         """
@@ -51,36 +47,26 @@ class EditConfig(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(EditConfig.SPEC)
-        subtree = spec['subtree']
-        subtree.append(util.store_or_url('target', target, self._assert))
+        node = new_ele("edit-config")
+        node.append(util.datastore_or_url("target", target, self._assert))
         if error_option is not None:
-            if error_option == 'rollback-on-error':
-                self._assert(':rollback-on-error')
-            subtree.append({
-                'tag': 'error-option',
-                'text': error_option
-                })
+            if error_option == "rollback-on-error":
+                self._assert(":rollback-on-error")
+            sub_ele(node, "error-option").text = error_option
         if test_option is not None:
             self._assert(':validate')
-            subtree.append({
-                'tag': 'test-option',
-                'text': test_option
-                })
+            sub_ele(node, "test-option").text = test_option
         if default_operation is not None:
-            subtree.append({
-                'tag': 'default-operation',
-                'text': default_operation
-                })
-        subtree.append(xml_.validated_element(config, ('config', xml_.qualify('config'))))
+            # TODO: check if it is a valid default-operation
+            sub_ele(node, "default-operation").text = default_operation
+        node.append(validated_element(config, ("config", qualify("config"))))
         return self._request(spec)
 
+
 class DeleteConfig(RPC):
 
     "*<delete-config>* RPC"
 
-    SPEC = {'tag': 'delete-config', 'subtree': []}
-
     def request(self, target):
         """
         :arg target: See :ref:`source_target`
@@ -88,17 +74,15 @@ class DeleteConfig(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(DeleteConfig.SPEC)
-        spec['subtree'].append(util.store_or_url('target', target, self._assert))
+        node = new_ele("delete-config")
+        node.append(util.datastore_or_url("target", target, self._assert))
         return self._request(spec)
 
 
 class CopyConfig(RPC):
 
     "*<copy-config>* RPC"
-
-    SPEC = {'tag': 'copy-config', 'subtree': []}
-
+    
     def request(self, source, target):
         """
         :arg source: See :ref:`source_target`
@@ -109,9 +93,9 @@ class CopyConfig(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(CopyConfig.SPEC)
-        spec['subtree'].append(util.store_or_url('target', target, self._assert))
-        spec['subtree'].append(util.store_or_url('source', source, self._assert))
+        node = new_ele("copy-config")
+        node.append(util.datastore_or_url("target", target, self._assert))
+        node.append(util.datastore_or_url("source", source, self._assert))
         return self._request(spec)
 
 
@@ -121,8 +105,6 @@ class Validate(RPC):
 
     DEPENDS = [':validate']
 
-    SPEC = {'tag': 'validate', 'subtree': []}
-
     def request(self, source):
         """
         :arg source: See :ref:`source_target`
@@ -130,16 +112,13 @@ class Validate(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(Validate.SPEC)
+        node = new_ele("validate")
         try:
-            src = xml_.validated_element(source, ('config', xml_.qualify('config')))
+            src = validated_element(source, ("config", qualify("config")))
         except Exception as e:
             logger.debug(e)
-            src = util.store_or_url('source', source, self._assert)
-        spec['subtree'].append({
-            'tag': 'source',
-            'subtree': src
-            })
+            src = util.datastore_or_url("source", source, self._assert)
+        (node if src.tag == "source" else sub_ele(node, "source")).append(src)
         return self._request(spec)
 
 
@@ -148,12 +127,7 @@ class Commit(RPC):
     "*<commit>* RPC. Depends on the *:candidate* capability."
 
     DEPENDS = [':candidate']
-
-    SPEC = {'tag': 'commit', 'subtree': []}
-
-    def _parse_hook(self):
-        pass
-
+    
     def request(self, confirmed=False, timeout=None):
         """
         Requires *:confirmed-commit* capability if *confirmed* argument is
@@ -167,28 +141,22 @@ class Commit(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(Commit.SPEC)
+        node = new_ele("commit")
         if confirmed:
-            self._assert(':confirmed-commit')
-            spec['subtree'].append({'tag': 'confirmed'})
+            self._assert(":confirmed-commit")
+            sub_ele(node, "confirmed")
             if timeout is not None:
-                spec['subtree'].append({
-                    'tag': 'confirm-timeout',
-                    'text': timeout
-                })
+                # TODO check if timeout is a valid integer?
+                sub_ele(node, "confirm-timeout").text = timeout
         return self._request(Commit.SPEC)
 
 
 class DiscardChanges(RPC):
 
-    # TESTED
-
     "*<discard-changes>* RPC. Depends on the *:candidate* capability."
 
-    DEPENDS = [':candidate']
-
-    SPEC = {'tag': 'discard-changes'}
+    DEPENDS = [":candidate"]
 
     def request(self):
         ":seealso: :ref:`return`"
-        return self._request(DiscardChanges.SPEC)
+        return self._request(new_ele("discard-changes"))
\ No newline at end of file
diff --git a/ncclient/operations/flowmon.py b/ncclient/operations/flowmon.py
index 150751a..fa3fee0 100644
--- a/ncclient/operations/flowmon.py
+++ b/ncclient/operations/flowmon.py
@@ -14,24 +14,26 @@
 
 'Power-control operations'
 
-from ncclient import xml_
+from ncclient.xml_ import *
 
 from rpc import RPC
 
+PC_URN = "urn:liberouter:params:xml:ns:netconf:power-control:1.0"
+
 class PoweroffMachine(RPC):
 
     "*poweroff-machine* RPC (flowmon)"
 
-    DEPENDS = [':power-control:1.0']
-
-    SPEC = {'tag': xml_.qualify('poweroff-machine',
-                                'urn:liberouter:params:xml:ns:netconf:power-control:1.0')}
+    DEPENDS = ["urn:liberouter:params:netconf:capability:power-control:1.0"]
+    
+    def request(self, target):
+        return self._request(new_ele(qualify("poweroff-machine", PC_URN)))
 
 class RebootMachine(RPC):
 
     "*reboot-machine* RPC (flowmon)"
 
-    DEPENDS = [':power-control:1.0']
+    DEPENDS = ["urn:liberouter:params:netconf:capability:power-control:1.0"]
 
-    SPEC = {'tag': xml_.qualify('reboot-machine',
-                                'urn:liberouter:params:xml:ns:netconf:power-control:1.0')}
+    def request(self, target):
+        return self._request(new_ele(qualify("reboot-machine", PC_URN)))
diff --git a/ncclient/operations/lock.py b/ncclient/operations/lock.py
index e5b93d5..0efb224 100644
--- a/ncclient/operations/lock.py
+++ b/ncclient/operations/lock.py
@@ -14,27 +14,18 @@
 
 'Locking-related NETCONF operations'
 
-from copy import deepcopy
+from ncclient.xml_ import *
 
 from rpc import RPC
 
 # TODO:
-# should have some way to parse session-id from a lock-denied error
+# should have some way to parse session-id from a lock-denied error, and raise
+# a tailored exception
 
 class Lock(RPC):
 
     "*<lock>* RPC"
-
-    SPEC = {
-        'tag': 'lock',
-        'subtree': {
-            'tag': 'target',
-            'subtree': {'tag': None }
-        }
-    }
-
-    #REPLY_CLS = LockReply
-
+    
     def request(self, target):
         """
         :arg target: see :ref:`source_target`
@@ -42,23 +33,15 @@ class Lock(RPC):
 
         :rtype: :ref:`return`
         """
-        spec = deepcopy(Lock.SPEC)
-        spec['subtree']['subtree']['tag'] = target
-        return self._request(spec)
+        node = new_ele("lock")
+        sub_ele(sub_ele(node, "target"), "running")
+        return self._request(node)
 
 
 class Unlock(RPC):
 
     "*<unlock>* RPC"
-
-    SPEC = {
-        'tag': 'unlock',
-        'subtree': {
-            'tag': 'target',
-            'subtree': {'tag': None }
-        }
-    }
-
+    
     def request(self, target):
         """
         :arg target: see :ref:`source_target`
@@ -66,9 +49,9 @@ class Unlock(RPC):
 
         :rtype: :ref:`return`
         """
-        spec = deepcopy(Unlock.SPEC)
-        spec['subtree']['subtree']['tag'] = target
-        return self._request(spec)
+        node = new_ele("lock")
+        sub_ele(sub_ele(node, "target"), "running")
+        return self._request(node)
 
 
 class LockContext:
@@ -87,14 +70,9 @@ class LockContext:
         self.target = target
 
     def __enter__(self):
-        reply = Lock(self.session).request(self.target)
-        if not reply.ok: # an error locking should definitely always be raised
-            raise reply.error
-        else:
-            return self
+        Lock(self.session).request(self.target)
+        return self
 
     def __exit__(self, *args):
-        reply = Unlock(self.session).request(self.target)
-        if not reply.ok:
-            raise reply.error
+        Unlock(self.session).request(self.target)
         return False
diff --git a/ncclient/operations/retrieve.py b/ncclient/operations/retrieve.py
index 37b6d69..96ba43f 100644
--- a/ncclient/operations/retrieve.py
+++ b/ncclient/operations/retrieve.py
@@ -14,8 +14,7 @@
 
 from rpc import RPC, RPCReply
 
-from ncclient import xml_
-from copy import deepcopy
+from ncclient.xml_ import *
 
 import util
 
@@ -27,7 +26,7 @@ class GetReply(RPCReply):
     def _parsing_hook(self, root):
         self._data = None
         if not self._errors:
-            self._data = xml_.find(root, 'data', nslist=xml_.NSLIST)
+            self._data = root.find(qualify("data"))
 
     @property
     def data_ele(self):
@@ -41,23 +40,19 @@ class GetReply(RPCReply):
         "*<data>* element as an XML string"
         if not self._parsed:
             self.parse()
-        return xml_.ele2xml(self._data)
-
-    @property
-    def data_dtree(self):
-        "*<data>* element in :ref:`dtree`"
-        return xml_.ele2dtree(self._data)
-
+        return to_xml(self._data)
+    
     #: Same as :attr:`data_ele`
     data = data_ele
+    
+    #def __repr__(self):
+    #    return self.data_xml
 
 
 class Get(RPC):
 
     "The *<get>* RPC"
 
-    SPEC = {'tag': 'get', 'subtree': []}
-
     REPLY_CLS = GetReply
 
     def request(self, filter=None):
@@ -66,18 +61,16 @@ class Get(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(Get.SPEC)
+        node = new_ele("get")
         if filter is not None:
-            spec['subtree'].append(util.build_filter(filter))
-        return self._request(spec)
+            node.append(util.build_filter(filter))
+        return self._request(node)
 
 
 class GetConfig(RPC):
 
     "The *<get-config>* RPC"
 
-    SPEC = {'tag': 'get-config', 'subtree': []}
-
     REPLY_CLS = GetReply
 
     def request(self, source, filter=None):
@@ -88,8 +81,8 @@ class GetConfig(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(GetConfig.SPEC)
-        spec['subtree'].append(util.store_or_url('source', source, self._assert))
+        node = new_ele("get-config")
+        node.append(util.datastore_or_url("source", source, self._assert))
         if filter is not None:
-            spec['subtree'].append(util.build_filter(filter))
-        return self._request(spec)
+            node.append(util.build_filter(filter))
+        return self._request(node)
\ No newline at end of file
diff --git a/ncclient/operations/rpc.py b/ncclient/operations/rpc.py
index b782901..a43ce35 100644
--- a/ncclient/operations/rpc.py
+++ b/ncclient/operations/rpc.py
@@ -15,13 +15,13 @@
 from threading import Event, Lock
 from uuid import uuid1
 
-from ncclient import xml_
+from ncclient.xml_ import *
 from ncclient.transport import SessionListener
 
 from errors import OperationError, TimeoutExpiredError, MissingCapabilityError
 
 import logging
-logger = logging.getLogger('ncclient.operations.rpc')
+logger = logging.getLogger("ncclient.operations.rpc")
 
 
 class RPCReply:
@@ -55,25 +55,16 @@ class RPCReply:
         """Parse the *<rpc-reply>*"""
         if self._parsed:
             return
-        root = self._root = xml_.xml2ele(self._raw) # <rpc-reply> element
-        # per rfc 4741 an <ok/> tag is sent when there are no errors or warnings
-        ok = xml_.find(root, 'ok', nslist=xml_.NSLIST)
-        if ok is not None:
-            logger.debug('parsed [%s]' % ok.tag)
-        else: # create RPCError objects from <rpc-error> elements
-            error = xml_.find(root, 'rpc-error', nslist=xml_.NSLIST)
+        root = self._root = to_ele(self._raw) # The <rpc-reply> element
+        # Per RFC 4741 an <ok/> tag is sent when there are no errors or warnings
+        ok = root.find(qualify("ok"))
+        if ok is None:
+            # Create RPCError objects from <rpc-error> elements
+            error = root.find(qualify("rpc-error"))
             if error is not None:
-                logger.debug('parsed [%s]' % error.tag)
                 for err in root.getiterator(error.tag):
-                    # process a particular <rpc-error>
-                    d = {}
-                    for err_detail in err.getchildren(): # <error-type> etc..
-                        tag = xml_.unqualify(err_detail.tag)
-                        if tag != 'error-info':
-                            d[tag] = err_detail.text.strip()
-                        else:
-                            d[tag] = xml_.ele2xml(err_detail)
-                    self._errors.append(RPCError(d))
+                    # Process a particular <rpc-error>
+                    self._errors.append(RPCError(err))
         self._parsing_hook(root)
         self._parsed = True
 
@@ -110,13 +101,29 @@ class RPCReply:
         return self._errors
 
 
-class RPCError(OperationError): # raise it if you like
+class RPCError(OperationError):
 
-    """Represents an *<rpc-error>*. It is an instance of :exc:`OperationError`
+    """Represents an *<rpc-error>*. It is a type of :exc:`OperationError`
     and can be raised like any other exception."""
 
-    def __init__(self, err_dict):
-        self._dict = err_dict
+    def __init__(self, err):
+        self._type = None
+        self._severity = None
+        self._info = None
+        self._tag = None
+        self._path = None
+        self._message = None
+        for subele in err:
+            if subele.tag == qualify("error-tag"):
+                self._tag = subele.text
+            elif subele.tag == qualify("error-severity"):
+                self._severity = subele.text
+            elif subele.tag == qualify("error-info"):
+                self._info = subele.text
+            elif subele.tag == qualify("error-path"):
+                self._path = subele.text
+            elif subele.tag == qualify("error-message"):
+                self._message = subele.text
         if self.message is not None:
             OperationError.__init__(self, self.message)
         else:
@@ -125,56 +132,32 @@ class RPCError(OperationError): # raise it if you like
     @property
     def type(self):
         "`string` representing text of *error-type* element"
-        return self.get('error-type', None)
+        return self._type
 
     @property
     def severity(self):
         "`string` representing text of *error-severity* element"
-        return self.get('error-severity', None)
+        return self._severity
 
     @property
     def tag(self):
         "`string` representing text of *error-tag* element"
-        return self.get('error-tag', None)
+        return self._tag
 
     @property
     def path(self):
         "`string` or :const:`None`; representing text of *error-path* element"
-        return self.get('error-path', None)
+        return self._path
 
     @property
     def message(self):
         "`string` or :const:`None`; representing text of *error-message* element"
-        return self.get('error-message', None)
+        return self._message
 
     @property
     def info(self):
         "`string` (XML) or :const:`None`, representing *error-info* element"
-        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)
+        return self._info
 
 
 class RPCReplyListener(SessionListener):
@@ -198,25 +181,26 @@ class RPCReplyListener(SessionListener):
 
     def callback(self, root, raw):
         tag, attrs = root
-        if xml_.unqualify(tag) != 'rpc-reply':
+        if tag != qualify("rpc-reply"):
             return
         for key in attrs: # in the <rpc-reply> attributes
-            if xml_.unqualify(key) == 'message-id': # if we found msgid attr
+            logger.debug("key=%s" % key)
+            if key == "message-id": # if we found msgid attr
                 id = attrs[key] # get the msgid
-                try:
-                    with self._lock:
-                        rpc = self._id2rpc.get(id) # the corresponding rpc
-                        logger.debug('delivering to %r' % rpc)
+                with self._lock:
+                    try:                    
+                        rpc = self._id2rpc[id] # the corresponding rpc
+                        logger.debug("Delivering to %r" % rpc)
                         rpc.deliver_reply(raw)
-                except KeyError:
-                    raise OperationError('Unknown message-id: %s', id)
-                # no catching other exceptions, fail loudly if must
-                else:
-                    # if no error delivering, can del the reference to the RPC
-                    del self._id2rpc[id]
-                    break
+                    except KeyError:
+                        raise OperationError("Unknown message-id: %s", id)
+                    # no catching other exceptions, fail loudly if must
+                    else:
+                        # if no error delivering, can del the reference to the RPC
+                        del self._id2rpc[id]
+                        break
         else:
-            raise OperationError('Could not find "message-id" attribute in <rpc-reply>')
+            raise OperationError("Could not find 'message-id' attribute in <rpc-reply>")
     
     def errback(self, err):
         try:
@@ -244,7 +228,7 @@ class RPC(object):
     # subclass of :class:`RPCReply`.
     REPLY_CLS = RPCReply
 
-    def __init__(self, session, async=False, timeout=None, raise_mode='none'):
+    def __init__(self, session, async=False, timeout=None, raise_mode="none"):
         self._session = session
         try:
             for cap in self.DEPENDS:
@@ -254,25 +238,18 @@ class RPC(object):
         self._async = async
         self._timeout = timeout
         self._raise_mode = raise_mode
-        # keeps things simple instead of having a class attr that has to be locked
-        self._id = uuid1().urn
+        self._id = uuid1().urn # Keeps things simple instead of having a class attr that has to be locked
         self._listener = RPCReplyListener(session)
         self._listener.register(self._id, self)
         self._reply = None
         self._error = None
         self._event = Event()
 
-    def _build(self, opspec):
+    def _build(self, subele):
         # internal
-        spec = {
-            'tag': 'rpc',
-            'attrib': {
-                'xmlns': xml_.BASE_NS_1_0,
-                'message-id': self._id
-                },
-            'subtree': [ opspec ]
-            }
-        return xml_.dtree2xml(spec)
+        ele = new_ele("rpc", {"message-id": self._id}, xmlns=BASE_NS_1_0)
+        ele.append(subele)
+        return to_xml(ele)
 
     def _request(self, op):
         """Subclasses call this method to make the RPC request.
@@ -288,14 +265,15 @@ class RPC(object):
         :type opspec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
         :rtype: :class:`RPCReply` (sync) or :class:`RPC` (async)
         """
-        logger.debug('request %r with opsepc=%r' % (self, op))
+        logger.info('Requesting %r' % self.__class__.__name__)
         req = self._build(op)
         self._session.send(req)
         if self._async:
-            logger.debug('async, returning')
+            logger.debug('Async request, returning %r', self)
             return self
         else:
-            logger.debug('sync, will wait for timeout=%r' % self._timeout)
+            logger.debug('Sync request, will wait for timeout=%r' %
+                         self._timeout)
             self._event.wait(self._timeout)
             if self._event.isSet():
                 if self._error:
@@ -330,13 +308,11 @@ class RPC(object):
     def deliver_reply(self, raw):
         # internal use
         self._reply = self.REPLY_CLS(raw)
-        #self._delivery_hook() -- usecase?!
         self._event.set()
 
     def deliver_error(self, err):
         # internal use
         self._error = err
-        #self._delivery_hook() -- usecase?!
         self._event.set()
 
     @property
@@ -379,16 +355,17 @@ class RPC(object):
             raise UserWarning('Asynchronous mode not supported for this device/session')
 
     def set_raise_mode(self, mode):
-        assert(choice in ('all', 'errors', 'none'))
+        assert(choice in ("all", "errors", "none"))
         self._raise_mode = mode
 
     def set_timeout(self, timeout):
-        """Set the timeout for synchronous waiting defining how long the RPC
-        request will block on a reply before raising an error."""
+        """Set the timeout for synchronous waiting; defining how long the RPC
+        request will block on a reply before raising an error. Irrelevant for
+        asynchronous usage."""
         self._timeout = timeout
 
     #: Whether this RPC is asynchronous
-    async = property(fget=lambda self: self._async, fset=set_async)
+    is_async = property(fget=lambda self: self._async, fset=set_async)
 
     #: Timeout for synchronous waiting
     timeout = property(fget=lambda self: self._timeout, fset=set_timeout)
diff --git a/ncclient/operations/session.py b/ncclient/operations/session.py
index 8dc38ad..dbfb065 100644
--- a/ncclient/operations/session.py
+++ b/ncclient/operations/session.py
@@ -14,7 +14,7 @@
 
 'Session-related NETCONF operations'
 
-from copy import deepcopy
+from ncclient.xml_ import *
 
 from rpc import RPC
 
@@ -22,11 +22,9 @@ class CloseSession(RPC):
 
     "*<close-session>* RPC. The connection to NETCONF server is also closed."
 
-    SPEC = { 'tag': 'close-session' }
-
     def request(self):
         try:
-            return self._request(CloseSession.SPEC)
+            return self._request(new_ele("close-sesion"))
         finally:
             self.session.close()
 
@@ -34,12 +32,7 @@ class CloseSession(RPC):
 class KillSession(RPC):
 
     "*<kill-session>* RPC."
-
-    SPEC = {
-        'tag': 'kill-session',
-        'subtree': []
-    }
-
+    
     def request(self, session_id):
         """
         :arg session_id: *session-id* of NETCONF session to kill
@@ -47,11 +40,8 @@ class KillSession(RPC):
 
         :seealso: :ref:`return`
         """
-        spec = deepcopy(KillSession.SPEC)
+        node = new_ele("kill-session")
         if not isinstance(session_id, basestring): # make sure
             session_id = str(session_id)
-        spec['subtree'].append({
-            'tag': 'session-id',
-            'text': session_id
-        })
-        return self._request(spec)
+        sub_ele(node, "session-id").text = session_id
+        return self._request(node)
diff --git a/ncclient/operations/subscribe.py b/ncclient/operations/subscribe.py
index 300cd72..44635d3 100644
--- a/ncclient/operations/subscribe.py
+++ b/ncclient/operations/subscribe.py
@@ -12,6 +12,8 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
+# TODO
+
 class Notification:
     pass
 
diff --git a/ncclient/operations/util.py b/ncclient/operations/util.py
index 4ee58c4..e11ae59 100644
--- a/ncclient/operations/util.py
+++ b/ncclient/operations/util.py
@@ -14,28 +14,27 @@
 
 'Boilerplate ugliness'
 
-from ncclient import xml_
+from ncclient.xml_ import *
 
 from errors import OperationError, MissingCapabilityError
 
 def one_of(*args):
-    'Verifies that only one of the arguments is not None'
+    "Verifies that only one of the arguments is not None"
     for i, arg in enumerate(args):
         if arg is not None:
             for argh in args[i+1:]:
                 if argh is not None:
-                    raise OperationError('Too many parameters')
+                    raise OperationError("Too many parameters")
             else:
                 return
-    raise OperationError('Insufficient parameters')
+    raise OperationError("Insufficient parameters")
 
-def store_or_url(wha, loc, capcheck=None):
-    node = { 'tag': wha, 'subtree': {} }
-    if '://' in loc: # e.g. http://, file://, ftp://
+def datastore_or_url(wha, loc, capcheck=None):
+    node = new_ele(wha)
+    if "://" in loc: # e.g. http://, file://, ftp://
         if capcheck is not None:
-            capcheck(':url') # url schema check at some point!
-        node['subtree']['tag'] = 'url'
-        node['subtree']['text'] = loc
+            capcheck(":url") # url schema check at some point!
+            sub_ele(node, "url").text = loc
     else:
         #if loc == 'candidate':
         #    capcheck(':candidate')
@@ -43,23 +42,24 @@ def store_or_url(wha, loc, capcheck=None):
         #    capcheck(':startup')
         #elif loc == 'running' and wha == 'target':
         #    capcheck(':writable-running')
-        node['subtree']['tag'] = loc
+        sub_ele(node, loc)
     return node
 
 def build_filter(spec, capcheck=None):
     type = None
     if isinstance(spec, tuple):
         type, criteria = spec
-        rep = {'tag': 'filter', 'attrib': {'type': type}}
-        if type == 'xpath':
-            rep['attrib']['select'] = criteria
-        elif type == 'subtree':
-            rep['subtree'] = criteria
+        rep = new_ele("filter", type=type)
+        if type == "xpath":
+            rep.attrib["select"] = criteria
+        elif type == "subtree":
+            rep.append(to_ele(criteria))
         else:
             raise OperationError("Invalid filter type")
     else:
-        rep = xml_.validated_element(spec, ['filter', xml_.qualify('filter')],
-                                        attrs=[('type', xml_.qualify('type'))])
-    if type == 'xpath' and capcheck is not None:
-        capcheck(':xpath')
+        rep = validated_element(spec, ("filter", qualify("filter")),
+                                        attrs=("type",))
+        # TODO set type var here, check if select attr present in case of xpath..
+    if type == "xpath" and capcheck is not None:
+        capcheck(":xpath")
     return rep
diff --git a/ncclient/transport/errors.py b/ncclient/transport/errors.py
index 7bee2b7..84aa986 100644
--- a/ncclient/transport/errors.py
+++ b/ncclient/transport/errors.py
@@ -25,9 +25,9 @@ class SessionCloseError(TransportError):
     def __init__(self, in_buf, out_buf=None):
         msg = 'Unexpected session close'
         if in_buf:
-            msg += '\nIN_BUFFER: {%s}' % in_buf
+            msg += '\nIN_BUFFER: `%s`' % in_buf
         if out_buf:
-            msg += ' OUT_BUFFER: {%s}' % out_buf
+            msg += ' OUT_BUFFER: `%s`' % out_buf
         SSHError.__init__(self, msg)
 
 class SSHError(TransportError):
diff --git a/ncclient/transport/session.py b/ncclient/transport/session.py
index d503494..6486f6c 100644
--- a/ncclient/transport/session.py
+++ b/ncclient/transport/session.py
@@ -15,7 +15,7 @@
 from Queue import Queue
 from threading import Thread, Lock, Event
 
-from ncclient import xml_
+from ncclient.xml_ import *
 from ncclient.capabilities import Capabilities
 
 from errors import TransportError
@@ -30,7 +30,7 @@ class Session(Thread):
     def __init__(self, capabilities):
         Thread.__init__(self)
         self.setDaemon(True)
-        self._listeners = set() # 3.0's weakset would be ideal
+        self._listeners = set()
         self._lock = Lock()
         self.setName('session')
         self._q = Queue()
@@ -43,7 +43,7 @@ class Session(Thread):
 
     def _dispatch_message(self, raw):
         try:
-            root = xml_.parse_root(raw)
+            root = parse_root(raw)
         except Exception as e:
             logger.error('error parsing dispatch message: %s' % e)
             return
@@ -203,7 +203,8 @@ class HelloHandler(SessionListener):
         self._error_cb = error_cb
 
     def callback(self, root, raw):
-        if xml_.unqualify(root[0]) == 'hello':
+        tag, attrs = root
+        if tag == qualify("hello"):
             try:
                 id, capabilities = HelloHandler.parse(raw)
             except Exception as e:
@@ -217,28 +218,22 @@ class HelloHandler(SessionListener):
     @staticmethod
     def build(capabilities):
         "Given a list of capability URI's returns <hello> message XML string"
-        spec = {
-            'tag': 'hello',
-            'attrib': {'xmlns': xml_.BASE_NS_1_0},
-            'subtree': [{
-                'tag': 'capabilities',
-                'subtree': # this is fun :-)
-                    [{'tag': 'capability', 'text': uri} for uri in capabilities]
-                }]
-            }
-        return xml_.dtree2xml(spec)
+        hello = new_ele("hello", xmlns=BASE_NS_1_0)
+        caps = sub_ele(hello, "capabilities")
+        def fun(uri): sub_ele(caps, "capability").text = uri
+        map(fun, capabilities)
+        return to_xml(hello)
 
     @staticmethod
     def parse(raw):
         "Returns tuple of (session-id (str), capabilities (Capabilities)"
         sid, capabilities = 0, []
-        root = xml_.xml2ele(raw)
+        root = to_ele(raw)
         for child in root.getchildren():
-            tag = xml_.unqualify(child.tag)
-            if tag == 'session-id':
+            if child.tag == qualify("session-id"):
                 sid = child.text
-            elif tag == 'capabilities':
+            elif child.tag == qualify("capabilities"):
                 for cap in child.getchildren():
-                    if xml_.unqualify(cap.tag) == 'capability':
+                    if cap.tag == qualify("capability"):
                         capabilities.append(cap.text)
         return sid, Capabilities(capabilities)
diff --git a/ncclient/xml_.py b/ncclient/xml_.py
index cd1f8f0..424ac54 100644
--- a/ncclient/xml_.py
+++ b/ncclient/xml_.py
@@ -23,16 +23,13 @@ from xml.etree import cElementTree as ET
 
 from ncclient import NCClientError
 
-class ContentError(NCClientError):
-    "Raised by methods of the :mod:`content` module in case of an error."
+class XMLError(NCClientError):
     pass
 
 ### Namespace-related
 
 #: Base NETCONF namespace
 BASE_NS_1_0 = 'urn:ietf:params:xml:ns:netconf:base:1.0'
-#: ... and this is BASE_NS according to Cisco devices tested
-CISCO_BS_1_0 = 'urn:ietf:params:netconf:base:1.0'
 #: namespace for Tail-f data model
 TAILF_AAA_1_1 = 'http://tail-f.com/ns/aaa/1.1'
 #: namespace for Tail-f data model
@@ -63,137 +60,27 @@ for (ns, pre) in prefix_map.items():
 
 qualify = lambda tag, ns=BASE_NS_1_0: tag if ns is None else '{%s}%s' % (ns, tag)
 
-multiqualify = lambda tag, nslist=(BASE_NS_1_0, CISCO_BS_1_0): [qualify(tag, ns) for ns in nslist]
-
-unqualify = lambda tag: tag[tag.rfind('}')+1:]
-
-### XML representations
-
-class DictTree:
-
-    @staticmethod
-    def Element(spec):
-        """DictTree -> Element
-
-        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
-
-        :rtype: :class:`~xml.etree.ElementTree.Element`
-        """
-        if iselement(spec):
-            return spec
-        elif isinstance(spec, basestring):
-            return XML.Element(spec)
-        if not isinstance(spec, dict):
-            raise ContentError("Invalid tree spec")
-        if 'tag' in spec:
-            ele = ET.Element(spec.get('tag'), spec.get('attrib', {}))
-            ele.text = spec.get('text', '')
-            ele.tail = spec.get('tail', '')
-            subtree = spec.get('subtree', [])
-            # might not be properly specified as list but may be dict
-            if not isinstance(subtree, list):
-                subtree = [subtree]
-            for subele in subtree:
-                ele.append(DictTree.Element(subele))
-            return ele
-        elif 'comment' in spec:
-            return ET.Comment(spec.get('comment'))
-        else:
-            raise ContentError('Invalid tree spec')
-
-    @staticmethod
-    def XML(spec, encoding='UTF-8'):
-        """DictTree -> XML
-
-        :type spec: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
-
-        :arg encoding: chraracter encoding
-
-        :rtype: string
-        """
-        return Element.XML(DictTree.Element(spec), encoding)
-
-class Element:
-
-    @staticmethod
-    def DictTree(ele):
-        """DictTree -> Element
-
-        :type spec: :class:`~xml.etree.ElementTree.Element`
-        :rtype: :obj:`dict`
-        """
-        return {
-            'tag': ele.tag,
-            'attributes': ele.attrib,
-            'text': ele.text,
-            'tail': ele.tail,
-            'subtree': [ Element.DictTree(child) for child in ele.getchildren() ]
-        }
-
-    @staticmethod
-    def XML(ele, encoding='UTF-8'):
-        """Element -> XML
-
-        :type spec: :class:`~xml.etree.ElementTree.Element`
-        :arg encoding: character encoding
-        :rtype: :obj:`string`
-        """
-        xml = ET.tostring(ele, encoding)
-        if xml.startswith('<?xml'):
-            return xml
-        else:
-            return '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
-
-class XML:
-
-    @staticmethod
-    def DictTree(xml):
-        """XML -> DictTree
-
-        :type spec: :obj:`string`
-        :rtype: :obj:`dict`
-        """
-        return Element.DictTree(XML.Element(xml))
-
-    @staticmethod
-    def Element(xml):
-        """XML -> Element
-
-        :type xml: :obj:`string`
-        :rtype: :class:`~xml.etree.ElementTree.Element`
-        """
-        return ET.fromstring(xml)
-
-dtree2ele = DictTree.Element
-dtree2xml = DictTree.XML
-ele2dtree = Element.DictTree
-ele2xml = Element.XML
-xml2dtree = XML.DictTree
-xml2ele = XML.Element
-
-### Other utility functions
+#unqualify = lambda tag: tag[tag.rfind('}')+1:]
 
-iselement = ET.iselement
-
-
-NSLIST = [BASE_NS_1_0, CISCO_BS_1_0]
-
-def find(ele, tag, nslist=[]):
-    """If *nslist* is empty, same as :meth:`xml.etree.ElementTree.Element.find`.
-    If it is not, *tag* is interpreted as an unqualified name and qualified
-    using each item in *nslist* (with a :const:`None` item in *nslit* meaning no
-    qualification is done). The first match is returned.
-
-    :arg nslist: optional list of namespaces
-    :type nslit: `string` `list`
+def to_xml(ele, encoding="UTF-8"):
+    """Element -> XML
+    
+    :type spec: :class:`~xml.etree.ElementTree.Element`
+    :arg encoding: character encoding
+    :rtype: :obj:`string`
     """
-    if nslist:
-        for qname in multiqualify(tag):
-            found = ele.find(qname)
-            if found is not None:
-                return found
-    else:
-        return ele.find(tag)
+    xml = ET.tostring(ele, encoding)
+    return xml if xml.startswith('<?xml') else '<?xml version="1.0" encoding="%s"?>%s' % (encoding, xml)
+
+def to_ele(x):
+    """XML -> Element
+    
+    :type xml: :obj:`string`
+    :rtype: :class:`~xml.etree.ElementTree.Element`
+    """
+    return x if iselement(x) else ET.fromstring(x)
+
+iselement = ET.iselement
 
 def parse_root(raw):
     """Efficiently parses the root element of an XML document.
@@ -203,27 +90,26 @@ def parse_root(raw):
     :returns: a tuple of `(tag, attributes)`, where `tag` is the (qualified) name of the element and `attributes` is a dictionary of its attributes.
     :rtype: `tuple`
     """
-    fp = StringIO(raw[:1024]) # this is a guess but start element beyond 1024 bytes would be a bit absurd
+    fp = StringIO(raw)
     for event, element in ET.iterparse(fp, events=('start',)):
         return (element.tag, element.attrib)
 
-def validated_element(rep, tags=None, attrs=None, text=None):
+def validated_element(x, tags=None, attrs=None):
     """Checks if the root element meets the supplied criteria. Returns a
     :class:`~xml.etree.ElementTree.Element` instance if so, otherwise raises
     :exc:`ContentError`.
 
-    :arg tags: tag name or a list of allowable tag names
-    :arg attrs: list of required attribute names, each item may be a list of allowable alternatives
+    :arg tags: tag name or a sequence of allowable tag names
+    :arg attrs: sequence of required attribute names, each item may be a list of allowable alternatives
     :arg text: textual content to match
-    :type rep: :obj:`dict` or :obj:`string` or :class:`~xml.etree.ElementTree.Element`
+    :type rep: :class:`~xml.etree.ElementTree.Element`
     """
-    ele = dtree2ele(rep)
-    err = False
+    ele = to_ele(x)
     if tags:
         if isinstance(tags, basestring):
             tags = [tags]
         if ele.tag not in tags:
-            err = True
+            raise XMLError("Element [%s] does not meet requirement" % ele.tag)
     if attrs:
         for req in attrs:
             if isinstance(req, basestring): req = [req]
@@ -231,9 +117,11 @@ def validated_element(rep, tags=None, attrs=None, text=None):
                 if alt in ele.attrib:
                     break
             else:
-                err = True
-    if text and ele.text != text:
-        err = True
-    if err:
-        raise ContentError("Element [%s] does not meet requirements" % ele.tag)
+                raise XMLError("Element [%s] does not have required attributes" % ele.tag)
     return ele
+
+def new_ele(tag, attrs={}, **extra):
+    return ET.Element(tag, attrs, **extra)
+
+def sub_ele(parent, tag, attrs={}, **extra):
+    return ET.SubElement(parent, tag, attrs, **extra)
