[Et-mgmt-commits-list] [SCM] koan branch, master now at v0.2.8-7-g1a82d21

Michael DeHaan mdehaan at redhat.com
Thu Apr 19 19:24:12 UTC 2007


Hello,

This is an automated email from the git hooks/update script, it was
generated because a ref change was pushed to the repository.

Updating branch, master,
       via  1a82d21838cd892eeb3abb1693edab4a0112f2b2 (commit)
      from  c8ca54b3492df95bc0ae9f99c826a5be88a39a69 (commit)

- Log -----------------------------------------------------------------
commit 1a82d21838cd892eeb3abb1693edab4a0112f2b2
Author: Michael DeHaan <mdehaan at mdehaan.rdu.redhat.com>
Date:   Thu Apr 19 15:25:28 2007 -0400

    This version of koan speaks XMLRPC in place of parsing YAML, and still normal
    http:// in other places where file transfer is required.
-----------------------------------------------------------------------

Diffstat:
 koan/app.py               |  126 +++++++------
 koan/yaml/README          |  105 ----------
 koan/yaml/__init__.py     |   17 --
 koan/yaml/dump.py         |  296 -----------------------------
 koan/yaml/implicit.py     |   46 -----
 koan/yaml/inline.py       |   38 ----
 koan/yaml/klass.py        |   48 -----
 koan/yaml/load.py         |  327 --------------------------------
 koan/yaml/ordered_dict.py |   31 ---
 koan/yaml/redump.py       |   16 --
 koan/yaml/stream.py       |  193 -------------------
 koan/yaml/timestamp.py    |  145 --------------
 koan/yaml/ypath.py        |  462 ---------------------------------------------
 13 files changed, 70 insertions(+), 1780 deletions(-)

diff --git a/koan/app.py b/koan/app.py
index 81d13c5..91670dc 100755
--- a/koan/app.py
+++ b/koan/app.py
@@ -16,7 +16,6 @@ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 """
 
 import os
-import yaml          # Howell-Evans version
 import traceback
 import tempfile
 import urllib2
@@ -28,6 +27,8 @@ import shutil
 import errno
 import re
 import sys
+import xmlrpclib
+import string
 
 """
 koan --virt [--profile=webserver|--system=name] --server=hostname
@@ -72,6 +73,10 @@ def main():
                  dest="verbose",
                  action="store_false",
                  help="run (more) quietly")
+    p.add_option("-t", "--port",
+                 dest="port",
+                 help="cobbler xmlrpc port (default 25151)")
+
     (options, args) = p.parse_args()
 
     full_access = 1
@@ -93,7 +98,8 @@ def main():
         k.profile           = options.profile
         k.system            = options.system
         k.verbose           = options.verbose
-        #k.interactive       = options.interactive
+        if options.port is not None:
+            k.port          = options.port
         k.run()
     except InfoException, ie:
         print str(ie)
@@ -110,6 +116,11 @@ class InfoException(exceptions.Exception):
     """
     pass
 
+class ServerProxy(xmlrpclib.ServerProxy):
+
+    def __init__(self, url=None):
+        xmlrpclib.ServerProxy.__init__(self, url)
+
 class Koan:
 
     def __init__(self):
@@ -125,11 +136,13 @@ class Koan:
         self.is_virt           = None
         self.is_auto_kickstart = None
         self.dryrun            = None
-        # self.interactive       = False
+        self.port              = 25151
 
     def run(self):
         if self.server is None:
             raise InfoException, "no server specified"
+        url = "http://%s:%s" % (self.server, self.port)
+        self.xmlrpc_server = ServerProxy(url)
         if self.list_systems:
             self.do_list_systems()
         if self.list_profiles:
@@ -148,6 +161,8 @@ class Koan:
             raise InfoException, "must specify --profile or --system"
         if self.profile and self.system:
             raise InfoException, "--profile and --system are exclusive"
+
+
         if self.is_virt:
             self.do_virt()
         else:
@@ -238,14 +253,14 @@ class Koan:
         """
         self.debug("processing profile: %s" % self.profile)
         if self.profile:
-            profile_data = self.get_profile_yaml(self.profile)
+            profile_data = self.get_profile_xmlrpc(self.profile)
         else:
-            profile_data = self.get_system_yaml(self.system)
+            profile_data = self.get_system_xmlrpc(self.system)
         self.debug(profile_data)
         if not 'distro' in profile_data:
             raise InfoException, "invalid response from boot server"
         distro = self.safe_load(profile_data,'distro')
-        distro_data = self.get_distro_yaml(distro)
+        distro_data = self.get_distro_xmlrpc(distro)
         if distro_data.has_key("breed") and distro_data["breed"] != "redhat":
             raise InfoException, "koan only works for Red Hat based distros"
         self.debug(distro_data)
@@ -266,27 +281,13 @@ class Koan:
 
     def do_list(self,is_profiles):
         if is_profiles:
-           urlseg = "profile_list"
-           what = "profiles"
+            data = self.get_profiles_xmlrpc()
         else:
-           urlseg = "system_list"
-           what = "systems"
-        print "listing defined %s..." % what
-        data = None
-        try:
-            url = "http://%s/cobbler/%s" % (self.server, urlseg)
-            self.debug("url=%s" % url)
-            # FIXME
-            data = self.urlread(url)
-            data = yaml.load(data).next() # first record
-            data.sort()
-            for x in data:
-                print "%s" % x
-            return True
-        except:
-            traceback.print_exc()
-            raise InfoException, "couldn't access listing information"
-        return False # shouldn't be here
+            data = self.get_systems_xmlrpc()
+        for x in data:
+            if x.has_key("name"):
+                print x["name"]
+        return True
                  
     def do_virt(self):
         """
@@ -310,11 +311,6 @@ class Koan:
                 raise InfoException, "grubby is not installed"
             k_args = self.safe_load(distro_data,'kernel_options')
             k_args = k_args + " ks=file:ks.cfg"
-            self.build_initrd(
-                self.safe_load(distro_data,'initrd_local'), 
-                self.safe_load(profile_data,'kickstart')
-            )
-            k_args = k_args.replace("lang ","lang= ")
 
             cmd = [ "/sbin/grubby", 
                     "--bootloader-probe" ]
@@ -350,8 +346,19 @@ class Koan:
         """
         Get contents of data in network kickstart file.
         """
+        print "- kickstart: %s" % kickstart
         if kickstart is None or kickstart == "":
             return None
+        if kickstart.startswith("/var/www/cobbler/kickstarts/"):
+            kickstart = kickstart.replace(
+                "/var/www/cobbler/kickstarts",
+                "http://%s/cblr/kickstarts" % self.server
+            )
+        if kickstart.startswith("/var/www/cobbler/kickstarts_sys/"):
+            kickstart = kickstart.replace(
+                "/var/www/cobbler/kickstarts_sys",
+                "http://%s/cblr/kickstarts_sys" % self.server
+           )
         if kickstart.startswith("nfs"):
             ndir  = os.path.dirname(kickstart[6:])
             nfile = os.path.basename(kickstart[6:])
@@ -424,19 +431,36 @@ class Koan:
         self.subprocess_call([ "/bin/bash", "/var/spool/koan/insert.sh" ])
         self.copyfile("/var/spool/koan/initrd_final", initrd)
 
-    def get_profile_yaml(self,profile_name):
+    def trace_me(self):
+        x = traceback.extract_stack()
+        bar = string.join(traceback.format_list(x))
+        return bar
+
+    def connect_fail(self):
+        print self.trace_me()
+        raise InfoException, "Could not communicate with %s:%s" % (self.server, self.port)
+
+    def get_profiles_xmlrpc(self):
+        try:
+            return self.xmlrpc_server.get_profiles()
+        except:
+            self.connect_fail()
+
+    def get_profile_xmlrpc(self,profile_name):
         """
         Fetches profile yaml from a from a remote bootconf tree.
         """
-        self.debug("fetching configuration for profile: %s" % profile_name)
         try:
-            url = "http://%s/cobbler/profiles/%s" % (self.server,profile_name)
-            self.debug("url=%s" % url)
-            # FIXME
-            data = self.urlread(url)
-            return yaml.load(data).next() # first record
+            return self.xmlrpc_server.get_profile_for_koan(profile_name)
         except:
-            raise InfoException, "couldn't download profile information: %s" % profile_name
+            traceback.print_exc()
+            self.connect_fail()
+
+    def get_systems_xmlrpc(self):
+        try:
+            return self.xmlrpc_server.get_systems()
+        except:
+            self.connect_fail()
 
     def is_ip(self,strdata):
         """
@@ -482,7 +506,7 @@ class Koan:
             return self.fix_mac(system_name)
         return system_name
 
-    def get_system_yaml(self,system_name):
+    def get_system_xmlrpc(self,system_name):
         """
         If user specifies --system, return the profile data
         but use the system kickstart and kernel options in place
@@ -493,18 +517,12 @@ class Koan:
         system_data = None
         self.debug("fetching configuration for system: %s" % old_system_name)
         try:
-            url = "http://%s/cobbler/systems/%s" % (self.server,system_name)
-            self.debug("url=%s" % url)
-            # FIXME 
-            data = self.urlread(url)
-            system_data = yaml.load(data).next() # first record
+            data = self.xmlrpc_server.get_system_for_koan(system_name)
         except:
-            raise InfoException, "couldn't download profile information: %s" % system_name
-        profile_data = self.get_profile_yaml(self.safe_load(system_data,'profile'))
+            self.connect_fail()
+        profile_data = self.get_profile_xmlrpc(self.safe_load(system_data,'profile'))
         # system overrides the profile values where relevant
         profile_data.update(system_data)
-        # still have to override the kickstart since these are not in the
-        # YAML (kickstarts are per-profile but template eval'd for each system)
         try_this = "http://%s/cobbler/kickstarts_sys/%s/ks.cfg" % (self.server,system_name)
         try:
             # can only use a per-system kickstart if it exists.  It may
@@ -520,19 +538,15 @@ class Koan:
         print profile_data
         return profile_data
 
-    def get_distro_yaml(self,distro_name):
+    def get_distro_xmlrpc(self,distro_name):
         """
         Fetches distribution yaml from a remote bootconf tree.
         """
         self.debug("fetching configuration for distro: %s" % distro_name)
         try:
-            url = "http://%s/cobbler/distros/%s" % (self.server,distro_name)
-            self.debug("url=%s" % url)
-            # FIXME
-            data = self.urlread(url)
-            return yaml.load(data).next() # first record
+            return self.xmlrpc_server.get_distro_for_koan(distro_name)
         except:
-            raise InfoException, "couldn't download distro information: %s" % distro_name
+            self.connect_fail()
 
     def get_distro_files(self,distro_data, download_root):
         """
diff --git a/koan/yaml/README b/koan/yaml/README
deleted file mode 100644
index 17df48b..0000000
--- a/koan/yaml/README
+++ /dev/null
@@ -1,105 +0,0 @@
-- Introductions: >
-   Hello.  Welcome to the Python YAML parser.  This is 
-   a work in progress.   The primary author is Steve Howell,
-   with a few contributions by Clark Evans.
-
-- installation: >
-   Simply type "make install" as root, this runs python setup.py install.
-   If you do not have distutils installed, you can simply copy the
-   yaml sub-directory into site-packages.
-
-- testing: >
-   You should be able to type "make test" both before and
-   after the installation.  This simply runs python on the
-   test programs.
-
-   This package uses python iterators, but there is a surrogate
-   which returns a list object instead of an iterator for python
-   versions below 2.2
-
-   This package should work with 1.5.2 and up, although many of
-   the tests fail with 1.5.2; if you are an expert with 1.5.2
-   please help us patch this up so that it works on older versions
-   of Python.
-
-- playing: >
-   The best way to play, is to start with demo.py and 
-   work from there.  Note that this implementation has quite
-   a way to go before it is compliant with the YAML specification.
-   
-   If something is failing in the tests for your platform
-   please let us know.  If something doesn't work, check the
-   tests to see if the test covering the feature you need is
-   active; if not, chances are it's not implemented.
-
-   Your feedback or any other contributions are certainly welcome.
-
-- ypath: >
-   The YPATH implementation is EXPERIMENTAL but included
-   in the yaml package beacuse it is fun and we'd like to get
-   feedback from the user community as to how they'd like it 
-   to work.  It requries Python 2.2 since it uses iterators.
-
-- query: >
-   There is a query.py and query.yml file in this directory,
-   it is currently broke as ypath was re-written.
-
----
-contributors:
-  - who: Steve Howell
-    why?: |
-      Original author of the pure Python implementations of the 
-      YAML parser and emitter.  Many thanks to the other folks 
-      listed here, and some not listed here.  
-    email: showell at zipcon.net
-
-  - who: Brian Ingerson
-    why?: |
-      Brian got me hooked on YAML.  We have used his Perl 
-      implementation to do some really cool stuff, even on projects
-      that primarily used XML.  He also got me started on 
-      the Python project.
-
-  - who: Clark Evans
-    why?: |
-      Clark's YAML fame far precedes the Python implementation--he
-      founded the whole project.  But, he also contributed alias 
-      emitting to this project, and he's also generously allowed
-      me to bundle his very cool YPATH implementation.  Finally,
-      he's helped with module packaging issues and miscellaneous
-      features and bug fixes.
-
-  - who: Why The Lucky Stiff
-    why?: |
-      That's right, Why's the name, Ruby's the game.  Why devotes
-      most of his YAML effort to a Ruby implementation that grows
-      increasingly robust, but he's also a great team player on 
-      the YAML project.  For example, he consolidated the YAML
-      testing suites, so that multiple YAML implementations can 
-      share the same YAML test files.  If you look in this YAML
-      distribution, you will see Ruby all over the place.  Think
-      of it as a free introduction to another great scripting 
-      language.
-
-  - who: Ryan King
-    why?: |
-      Sharpener of saws and pair programmer extraordinaire.
-
-  - who: Neil Watkiss
-    why?: |
-      Donated hardware and major expertise to the project.       
-
-  - who: Oren Ben-Kiki
-    why?: |
-      YAML cofounder.  All library implementors owe a huge gratitude 
-      toward Oren for his work on the YAML spec.
-
-  - who: Lion Kimbro
-    why?: |
-      Early adopter, also known for his three-humped YAML.
-       
-  - who: Dave Kuhlman
-    why?: |
-      Dave's contributions include, but are not limited to, the 
-      XmlYaml code bundled with this distribution.  The README
-      with that code talks more about Dave.
diff --git a/koan/yaml/__init__.py b/koan/yaml/__init__.py
deleted file mode 100644
index 419d1f3..0000000
--- a/koan/yaml/__init__.py
+++ /dev/null
@@ -1,17 +0,0 @@
-__version__ = "0.32"
-from load import loadFile, load, Parser, l
-from dump import dump, dumpToFile, Dumper, d
-from stream import YamlLoaderException, StringStream, FileStream
-from timestamp import timestamp
-import sys
-if sys.hexversion >= 0x02020000:
-    from redump import loadOrdered
-
-try:
-    from ypath import ypath
-except NameError:
-    def ypath(expr,target='',cntx=''):
-        raise NotImplementedError("ypath requires Python 2.2")
-
-if sys.hexversion < 0x02010000:
-    raise 'YAML is not tested for pre-2.1 versions of Python'
diff --git a/koan/yaml/dump.py b/koan/yaml/dump.py
deleted file mode 100644
index c55dbfe..0000000
--- a/koan/yaml/dump.py
+++ /dev/null
@@ -1,296 +0,0 @@
-import types
-import string
-from types import StringType, UnicodeType, IntType, FloatType
-from types import DictType, ListType, TupleType, InstanceType
-from klass import hasMethod, isDictionary
-import re
-
-"""
-  The methods from this module that are exported to the top 
-  level yaml package should remain stable.  If you call
-  directly into other methods of this module, be aware that 
-  they may change or go away in future implementations.
-  Contact the authors if there are methods in this file 
-  that you wish to remain stable.
-"""
-
-def dump(*data):
-    return Dumper().dump(*data)
-
-def d(data): return dump(data)
-
-def dumpToFile(file, *data):
-    return Dumper().dumpToFile(file, *data)
-
-class Dumper:
-    def __init__(self):
-        self.currIndent   = "\n"
-        self.indent = "    "
-        self.keysrt   = None
-        self.alphaSort = 1 # legacy -- on by default
-
-    def setIndent(self, indent):
-        self.indent = indent
-        return self
-
-    def setSort(self, sort_hint):
-        self.keysrt = sortMethod(sort_hint)
-        return self
-
-    def dump(self, *data):
-        self.result = []  
-        self.output = self.outputToString
-        self.dumpDocuments(data)
-        return string.join(self.result,"")
-
-    def outputToString(self, data):
-        self.result.append(data)
-
-    def dumpToFile(self, file, *data):
-        self.file = file
-        self.output = self.outputToFile
-        self.dumpDocuments(data)
-
-    def outputToFile(self, data):
-        self.file.write(data)
-
-    def dumpDocuments(self, data):
-        for obj in data:
-            self.anchors  = YamlAnchors(obj)
-            self.output("---")
-            self.dumpData(obj)
-            self.output("\n")       
-
-    def indentDump(self, data):
-        oldIndent = self.currIndent
-        self.currIndent += self.indent
-        self.dumpData(data)
-        self.currIndent = oldIndent
-
-    def dumpData(self, data):
-        anchor = self.anchors.shouldAnchor(data)
-        if anchor: 
-            self.output(" &%d" % anchor )
-        else:
-            anchor = self.anchors.isAlias(data)
-            if anchor:
-                self.output(" *%d" % anchor )
-                return
-        if (data is None):
-            self.output(' ~')
-        elif hasMethod(data, 'to_yaml'):
-            self.dumpTransformedObject(data)            
-        elif hasMethod(data, 'to_yaml_implicit'):
-            self.output(" " + data.to_yaml_implicit())
-        elif type(data) is InstanceType:
-            self.dumpRawObject(data)
-        elif isDictionary(data):
-            self.dumpDict(data)
-        elif type(data) in [ListType, TupleType]:
-            self.dumpList(data)
-        else:
-            self.dumpScalar(data)
-
-    def dumpTransformedObject(self, data):
-        obj_yaml = data.to_yaml()
-        if type(obj_yaml) is not TupleType:
-            self.raiseToYamlSyntaxError()
-        (data, typestring) = obj_yaml
-        if typestring:
-            self.output(" " + typestring)
-        self.dumpData(data)
-
-    def dumpRawObject(self, data):
-        self.output(' !!%s.%s' % (data.__module__, data.__class__.__name__))
-        self.dumpData(data.__dict__)
-
-    def dumpDict(self, data):
-        keys = data.keys()
-        if len(keys) == 0:
-            self.output(" {}")
-            return
-        if self.keysrt:
-            keys = sort_keys(keys,self.keysrt)
-        else:
-            if self.alphaSort:
-                keys.sort()
-        for key in keys:
-            self.output(self.currIndent)
-            self.dumpKey(key)
-            self.output(":")
-            self.indentDump(data[key])
-
-    def dumpKey(self, key):
-        if type(key) is TupleType:
-            self.output("?")
-            self.indentDump(key) 
-            self.output("\n")
-        else:
-            self.output(quote(key))
-
-    def dumpList(self, data):
-        if len(data) == 0:
-            self.output(" []")
-            return
-        for item in data:
-            self.output(self.currIndent)
-            self.output("-")
-            self.indentDump(item)
-
-    def dumpScalar(self, data):
-        if isUnicode(data):
-            self.output(' "%s"' % repr(data)[2:-1])
-        elif isMulti(data):
-            self.dumpMultiLineScalar(data.splitlines())
-        else:
-            self.output(" ")
-            self.output(quote(data))
-    
-    def dumpMultiLineScalar(self, lines):
-        self.output(" |")
-        if lines[-1] == "":
-            self.output("+")
-        for line in lines:
-            self.output(self.currIndent)
-            self.output(line)
-
-    def raiseToYamlSyntaxError(self):
-            raise """
-to_yaml should return tuple w/object to dump 
-and optional YAML type.  Example:
-({'foo': 'bar'}, '!!foobar')
-"""
-
-#### ANCHOR-RELATED METHODS
-
-def accumulate(obj,occur):
-    typ = type(obj)
-    if obj is None or \
-       typ is IntType or \
-       typ is FloatType or \
-       ((typ is StringType or typ is UnicodeType) \
-       and len(obj) < 32): return
-    obid = id(obj)
-    if 0 == occur.get(obid,0):
-        occur[obid] = 1
-        if typ is ListType:
-            for x in obj: 
-                accumulate(x,occur)
-        if typ is DictType:
-            for (x,y) in obj.items():
-                accumulate(x,occur)
-                accumulate(y,occur)
-    else:
-        occur[obid] = occur[obid] + 1
-
-class YamlAnchors:
-     def __init__(self,data):
-         occur = {}
-         accumulate(data,occur)
-         anchorVisits = {}
-         for (obid, occur) in occur.items():
-             if occur > 1:
-                 anchorVisits[obid] = 0 
-         self._anchorVisits = anchorVisits
-         self._currentAliasIndex     = 0
-     def shouldAnchor(self,obj):
-         ret = self._anchorVisits.get(id(obj),None)
-         if 0 == ret:
-             self._currentAliasIndex = self._currentAliasIndex + 1
-             ret = self._currentAliasIndex
-             self._anchorVisits[id(obj)] = ret
-             return ret
-         return 0
-     def isAlias(self,obj):
-         return self._anchorVisits.get(id(obj),0)
-
-### SORTING METHODS
-
-def sort_keys(keys,fn):
-    tmp = []
-    for key in keys:
-        val = fn(key)
-        if val is None: val = '~'
-        tmp.append((val,key))
-    tmp.sort()
-    return [ y for (x,y) in tmp ]
-
-def sortMethod(sort_hint):
-    typ = type(sort_hint)
-    if DictType == typ:
-        return sort_hint.get
-    elif ListType == typ or TupleType == typ:
-        indexes = {}; idx = 0
-        for item in sort_hint:
-            indexes[item] = idx
-            idx += 1
-        return indexes.get
-    else:
-        return sort_hint
-
-### STRING QUOTING AND SCALAR HANDLING
-def isStr(data):
-    # XXX 2.1 madness
-    if type(data) == type(''):
-        return 1
-    if type(data) == type(u''):
-        return 1
-    return 0
-    
-def doubleUpQuotes(data):
-    return data.replace("'", "''")
-
-def quote(data):
-    if not isStr(data):
-        return str(data)
-    single = "'"
-    double = '"'
-    quote = ''
-    if len(data) == 0:
-        return "''"
-    if hasSpecialChar(data) or data[0] == single:
-        data = `data`[1:-1]
-        data = string.replace(data, r"\x08", r"\b")
-        quote = double 
-    elif needsSingleQuote(data):
-        quote = single
-        data = doubleUpQuotes(data)
-    return "%s%s%s" % (quote, data, quote)
-
-def needsSingleQuote(data):
-    if re.match(r"^-?\d", data):
-        return 1
-    if re.match(r"\*\S", data):
-        return 1
-    if data[0] in ['&', ' ']:
-        return 1
-    if data[0] == '"':
-        return 1
-    if data[-1] == ' ':
-        return 1
-    return (re.search(r'[:]', data) or re.search(r'(\d\.){2}', data))
-
-def hasSpecialChar(data):
-    # need test to drive out '#' from this
-    return re.search(r'[\t\b\r\f#]', data)
-
-def isMulti(data):
-    if not isStr(data):
-        return 0
-    if hasSpecialChar(data):
-        return 0
-    return re.search("\n", data)
-
-def isUnicode(data):
-    return type(data) == unicode
-    
-def sloppyIsUnicode(data):
-        # XXX - hack to make tests pass for 2.1
-        return repr(data)[:2] == "u'" and repr(data) != data
-
-import sys
-if sys.hexversion < 0x20200000:
-    isUnicode = sloppyIsUnicode
-    
-
-
diff --git a/koan/yaml/implicit.py b/koan/yaml/implicit.py
deleted file mode 100644
index 6172564..0000000
--- a/koan/yaml/implicit.py
+++ /dev/null
@@ -1,46 +0,0 @@
-import re
-import string
-from timestamp import timestamp, matchTime
-
-DATETIME_REGEX   = re.compile("^[0-9]{4}-[0-9]{2}-[0-9]{2}$")
-FLOAT_REGEX      = re.compile("^[-+]?[0-9][0-9,]*\.[0-9]*$")
-SCIENTIFIC_REGEX = re.compile("^[-+]?[0-9]+(\.[0-9]*)?[eE][-+][0-9]+$")
-OCTAL_REGEX      = re.compile("^[-+]?([0][0-7,]*)$")
-HEX_REGEX        = re.compile("^[-+]?0x[0-9a-fA-F,]+$")
-INT_REGEX        = re.compile("^[-+]?(0|[1-9][0-9,]*)$")
-
-def convertImplicit(val):
-    if val == '~':
-        return None
-    if val == '+':
-        return 1
-    if val == '-':
-        return 0
-    if val[0] == "'" and val[-1] == "'":
-        val = val[1:-1]
-        return string.replace(val, "''", "\'")
-    if val[0] == '"' and val[-1] == '"':
-        if re.search(r"\u", val):
-            val = "u" + val
-        unescapedStr = eval (val)
-        return unescapedStr
-    if matchTime.match(val):
-        return timestamp(val)
-    if INT_REGEX.match(val):
-        return int(cleanseNumber(val))
-    if OCTAL_REGEX.match(val):
-        return int(val, 8)
-    if HEX_REGEX.match(val):
-        return int(val, 16)
-    if FLOAT_REGEX.match(val):
-        return float(cleanseNumber(val))
-    if SCIENTIFIC_REGEX.match(val):
-        return float(cleanseNumber(val))
-    return val
-
-def cleanseNumber(str):
-    if str[0] == '+':
-        str = str[1:]
-    str = string.replace(str,',','')
-    return str
-
diff --git a/koan/yaml/inline.py b/koan/yaml/inline.py
deleted file mode 100644
index 8e647de..0000000
--- a/koan/yaml/inline.py
+++ /dev/null
@@ -1,38 +0,0 @@
-import re
-import string
-
-class InlineTokenizer:
-    def __init__(self, data):
-        self.data = data
-
-    def punctuation(self):
-        puncts = [ '[', ']', '{', '}' ]
-        for punct in puncts:
-            if self.data[0] == punct:
-                self.data = self.data[1:]
-                return punct
-
-    def up_to_comma(self):
-        match = re.match('(.*?)\s*, (.*)', self.data)
-        if match: 
-            self.data = match.groups()[1]
-            return match.groups()[0]
-
-    def up_to_end_brace(self):
-        match = re.match('(.*?)(\s*[\]}].*)', self.data)
-        if match: 
-            self.data = match.groups()[1]
-            return match.groups()[0]
-
-    def next(self):
-        self.data = string.strip(self.data)
-        productions = [
-            self.punctuation,
-            self.up_to_comma,
-            self.up_to_end_brace
-        ]
-        for production in productions:
-            token = production()
-            if token:
-                return token
-
diff --git a/koan/yaml/klass.py b/koan/yaml/klass.py
deleted file mode 100644
index edcf5a8..0000000
--- a/koan/yaml/klass.py
+++ /dev/null
@@ -1,48 +0,0 @@
-import new
-import re
-
-class DefaultResolver:
-    def resolveType(self, data, typestring):
-        match = re.match('!!(.*?)\.(.*)', typestring)
-        if not match:
-            raise "Invalid private type specifier"
-        (modname, classname) = match.groups()
-        return makeClass(modname, classname, data)
-
-def makeClass(module, classname, dict):
-    exec('import %s' % (module))
-    klass = eval('%s.%s' % (module, classname))
-    obj = new.instance(klass) 
-    if hasMethod(obj, 'from_yaml'):
-        return obj.from_yaml(dict)
-    obj.__dict__ = dict
-    return obj
-
-def hasMethod(object, method_name):
-    try:    
-        klass = object.__class__
-    except:
-        return 0
-    if not hasattr(klass, method_name):
-        return 0
-    method = getattr(klass, method_name)
-    if not callable(method):
-        return 0
-    return 1
-
-def isDictionary(data):
-    return isinstance(data, dict)
-
-try:
-    isDictionary({})
-except:
-    def isDictionary(data): return type(data) == type({}) # XXX python 2.1
-    
-if __name__ == '__main__':
-    print isDictionary({'foo': 'bar'})
-    try:
-        print isDictionary(dict())
-        from ordered_dict import OrderedDict
-        print isDictionary(OrderedDict())
-    except:
-        pass
diff --git a/koan/yaml/load.py b/koan/yaml/load.py
deleted file mode 100644
index 259178d..0000000
--- a/koan/yaml/load.py
+++ /dev/null
@@ -1,327 +0,0 @@
-import re, string
-from implicit import convertImplicit
-from inline import InlineTokenizer
-from klass import DefaultResolver
-from stream import YamlLoaderException, FileStream, StringStream, NestedDocs
-
-try:
-    iter(list()) # is iter supported by this version of Python?
-except:
-    # XXX - Python 2.1 does not support iterators   
-    class StopIteration: pass
-    class iter:
-        def __init__(self,parser):
-            self._docs = []
-            try:
-                while 1:
-                   self._docs.append(parser.next())
-            except StopIteration: pass
-            self._idx = 0
-        def __len__(self): return len(self._docs)
-        def __getitem__(self,idx): return self._docs[idx]
-        def next(self):
-            if self._idx < len(self._docs):
-                ret = self._docs[self._idx] 
-                self._idx = self._idx + 1
-                return ret
-            raise StopIteration
-
-def loadFile(filename, typeResolver=None):
-    return loadStream(FileStream(filename),typeResolver)
-   
-def load(str, typeResolver=None):
-    return loadStream(StringStream(str), typeResolver)
-
-def l(str): return load(str).next()
-
-def loadStream(stream, typeResolver):
-    return iter(Parser(stream, typeResolver))
-
-def tryProductions(productions, value):
-    for production in productions:
-        results = production(value)
-        if results:
-            (ok, result) = results
-            if ok:
-                return (1, result)
-
-def dumpDictionary(): return {}
-
-class Parser:
-    def __init__(self, stream, typeResolver=None):
-        try:
-            self.dictionary = dict
-        except:
-            self.dictionary = dumpDictionary
-        self.nestedDocs = NestedDocs(stream)
-        self.aliases = {}
-        if typeResolver:
-            self.typeResolver = typeResolver
-        else:
-            self.typeResolver = DefaultResolver()
-
-    def error(self, msg):
-        self.nestedDocs.error(msg, self.line)
-
-    def nestPop(self):
-        line = self.nestedDocs.pop()
-        if line is not None:
-            self.line = line
-            return 1
-
-    def value(self, indicator):
-        return getToken(indicator+"\s*(.*)", self.line)
-
-    def getNextDocument(self): raise "getNextDocument() deprecated--use next()"
-
-    def next(self):
-        line = self.nestedDocs.popDocSep()
-        indicator = getIndicator(line)
-        if indicator:
-            return self.parse_value(indicator)
-        if line:
-            self.nestedDocs.nestToNextLine()
-            return self.parseLines()
-        raise StopIteration
-
-    def __iter__(self): return self
-
-    def parseLines(self):
-        peekLine = self.nestedDocs.peek()
-        if peekLine:
-            if re.match("\s*-", peekLine):
-                return self.parse_collection([], self.parse_seq_line)
-            else:
-                return self.parse_collection(self.dictionary(), self.parse_map_line)
-        raise StopIteration
-
-    def parse_collection(self, items, lineParser):
-        while self.nestPop():
-            if self.line:
-                lineParser(items)
-        return items    
-
-    def parse_seq_line(self, items):
-        value = self.value("-")
-        if value is not None:
-            items.append(self.parse_seq_value(value))
-        else:
-            self.error("missing '-' for seq")
-
-    def parse_map_line(self, items):
-        if (self.line == '?'):
-            self.parse_map_line_nested(items)
-        else:
-            self.parse_map_line_simple(items, self.line)
-
-    def parse_map_line_nested(self, items):
-        self.nestedDocs.nestToNextLine()
-        key = self.parseLines()
-        if self.nestPop():
-            value = self.value(':')
-            if value is not None:
-                items[tuple(key)] = self.parse_value(value)
-                return
-        self.error("key has no value for nested map")
-
-    def parse_map_line_simple(self, items, line):
-        map_item = self.key_value(line)
-        if map_item:
-            (key, value) = map_item
-            key = convertImplicit(key)
-            if items.has_key(key):
-                self.error("Duplicate key "+key)
-            items[key] = self.parse_value(value)
-        else:
-            self.error("bad key for map")
-
-    def is_map(self, value):
-        # XXX - need real tokenizer
-        if len(value) == 0:
-            return 0
-        if value[0] == "'":
-            return 0
-        if re.search(':(\s|$)', value):       
-            return 1
-
-    def parse_seq_value(self, value):
-        if self.is_map(value):
-            return self.parse_compressed_map(value)
-        else:
-            return self.parse_value(value)
-
-    def parse_compressed_map(self, value):
-        items = self.dictionary()
-        line = self.line
-        token = getToken("(\s*-\s*)", line)
-        self.nestedDocs.nestBySpecificAmount(len(token))
-        self.parse_map_line_simple(items, value)
-        return self.parse_collection(items, self.parse_map_line)
-
-    def parse_value(self, value):
-        (alias, value) = self.testForRepeatOfAlias(value)
-        if alias:
-            return value
-        (alias, value) = self.testForAlias(value)            
-        value = self.parse_unaliased_value(value)
-        if alias:
-            self.aliases[alias] = value
-        return value          
-
-    def parse_unaliased_value(self, value):
-        match = re.match(r"(!\S*)(.*)", value)
-        if match:
-            (url, value) = match.groups()
-            value = self.parse_untyped_value(value)
-            if url[:2] == '!!':
-                return self.typeResolver.resolveType(value, url)
-            else:
-                # XXX - allows syntax, but ignores it
-                return value
-        return self.parse_untyped_value(value)
-
-    def parseInlineArray(self, value):        
-        if re.match("\s*\[", value):
-            return self.parseInline([], value, ']', 
-                self.parseInlineArrayItem)
-
-    def parseInlineHash(self, value):        
-        if re.match("\s*{", value):
-            return self.parseInline(self.dictionary(), value, '}', 
-                self.parseInlineHashItem)
-
-    def parseInlineArrayItem(self, result, token):
-        return result.append(convertImplicit(token))
-
-    def parseInlineHashItem(self, result, token):
-        (key, value) = self.key_value(token)
-        result[key] = value
-
-    def parseInline(self, result, value, end_marker, itemMethod):
-        tokenizer = InlineTokenizer(value)
-        tokenizer.next()
-        while 1:
-            token = tokenizer.next()
-            if token == end_marker:
-                break
-            itemMethod(result, token)
-        return (1, result)
-
-    def parseSpecial(self, value):
-        productions = [
-            self.parseMultiLineScalar,
-            self.parseInlineHash,
-            self.parseInlineArray,
-        ]
-        return tryProductions(productions, value)
-
-    def parse_untyped_value(self, value):
-        parse = self.parseSpecial(value)
-        if parse:
-            (ok, data) = parse
-            return data
-        token = getToken("(\S.*)", value)
-        if token:
-            lines = [token] + \
-                pruneTrailingEmpties(self.nestedDocs.popNestedLines())
-            return convertImplicit(joinLines(lines))
-        else:
-            self.nestedDocs.nestToNextLine()
-            return self.parseLines()
-
-    def parseNative(self, value):
-        return (1, convertImplicit(value))
-
-    def parseMultiLineScalar(self, value):
-        if value == '>':
-            return (1, self.parseFolded())
-        elif value == '|':
-            return (1, joinLiteral(self.parseBlock()))
-        elif value == '|+':
-            return (1, joinLiteral(self.unprunedBlock()))
-
-    def parseFolded(self):
-        data = self.parseBlock()
-        i = 0
-        resultString = ''
-        while i < len(data)-1:
-            resultString = resultString + data[i]
-            resultString = resultString + foldChar(data[i], data[i+1])
-            i = i + 1
-        return resultString + data[-1] + "\n"        
-
-    def unprunedBlock(self):
-        self.nestedDocs.nestToNextLine()
-        data = []
-        while self.nestPop():
-            data.append(self.line)
-        return data
-
-    def parseBlock(self):
-        return pruneTrailingEmpties(self.unprunedBlock())
-
-    def testForAlias(self, value):
-        match = re.match("&(\S*)\s*(.*)", value)
-        if match:
-            return match.groups()
-        return (None, value)
-
-    def testForRepeatOfAlias(self, value):
-        match = re.match("\*(\S+)", value)
-        if match:
-            alias = match.groups()[0]
-            if self.aliases.has_key(alias):
-                return (alias, self.aliases[alias])
-            else:
-                self.error("Unknown alias")
-        return (None, value)
-
-    def key_value(self, str):
-        if str[-1] == ' ':
-            self.error("Trailing spaces not allowed without quotes.")
-        # XXX This allows mis-balanced " vs. ' stuff
-        match = re.match("[\"'](.+)[\"']\s*:\s*(.*)", str)
-        if match:
-            (key, value) = match.groups()
-            return (key, value)
-        match = re.match("(.+?)\s*:\s*(.*)", str)
-        if match:
-            (key, value) = match.groups()
-            if len(value) and value[0] == '#':
-                value = ''
-            return (key, value)
-
-def getToken(regex, value):
-    match = re.search(regex, value)
-    if match:
-        return match.groups()[0]
-
-def pruneTrailingEmpties(data):
-    while len(data) > 0 and data[-1] == '':
-        data = data[:-1]
-    return data
-
-def foldChar(line1, line2):
-    if re.match("^\S", line1) and re.match("^\S", line2):
-        return " "
-    return "\n"
-
-def getIndicator(line):
-    if line:
-        header = r"(#YAML:\d+\.\d+\s*){0,1}"
-        match = re.match("--- "+header+"(\S*.*)", line)
-        if match:
-            return match.groups()[-1]
-
-def joinLines(lines):
-    result = ''
-    for line in lines[:-1]:
-        if line[-1] == '\\':
-            result = result + line[:-1]
-        else:
-            result = result + line + " "
-    return result + lines[-1]
-
-def joinLiteral(data):
-    return string.join(data,"\n") + "\n"
-
diff --git a/koan/yaml/ordered_dict.py b/koan/yaml/ordered_dict.py
deleted file mode 100644
index b3788b7..0000000
--- a/koan/yaml/ordered_dict.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# This is extremely crude implementation of an OrderedDict.
-# If you know of a better implementation, please send it to
-# the author Steve Howell.  You can find my email via
-# the YAML mailing list or wiki.
-
-class OrderedDict(dict):
-    def __init__(self):
-        self._keys = []
-
-    def __setitem__(self, key, val):
-        self._keys.append(key)
-        dict.__setitem__(self, key, val)
-
-    def keys(self):
-        return self._keys
-
-    def items(self):
-        return [(key, self[key]) for key in self._keys]
-
-if __name__ == '__main__':
-    data = OrderedDict()
-    data['z'] = 26
-    data['m'] = 13
-    data['a'] = 1
-    for key in data.keys():
-        print "The value for %s is %s" % (key, data[key])
-    print data
-
-
-
-
diff --git a/koan/yaml/redump.py b/koan/yaml/redump.py
deleted file mode 100644
index 56ea958..0000000
--- a/koan/yaml/redump.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from ordered_dict import OrderedDict
-from load import Parser
-from dump import Dumper
-from stream import StringStream
-
-def loadOrdered(stream):
-    parser = Parser(StringStream(stream))
-    parser.dictionary = OrderedDict
-    return iter(parser)
-
-def redump(stream):
-    docs = list(loadOrdered(stream))
-    dumper = Dumper()
-    dumper.alphaSort = 0
-    return dumper.dump(*docs)
-
diff --git a/koan/yaml/stream.py b/koan/yaml/stream.py
deleted file mode 100644
index cc78c4b..0000000
--- a/koan/yaml/stream.py
+++ /dev/null
@@ -1,193 +0,0 @@
-import re
-import string
-
-def indentLevel(line):
-    n = 0
-    while n < len(line) and line[n] == ' ':
-        n = n + 1
-    return n
-
-class LineNumberStream:
-    def __init__(self, filename=None):
-        self.curLine = 0
-        self.filename = filename
-
-    def get(self):
-        line = self.getLine()
-        self.curLine += 1 # used by subclass
-        if line:
-            line = noLineFeed(line)
-        return line
-
-    def lastLineRead(self):
-        return self.curLine
-
-class FileStream(LineNumberStream):
-    def __init__(self, filename):
-        self.fp = open(filename)
-        LineNumberStream.__init__(self, filename)
-
-    def getLine(self):
-        line = self.fp.readline()
-        if line == '': line = None
-        return line
-
-class StringStream(LineNumberStream):
-    def __init__(self, text):
-        self.lines = split(text)
-        self.numLines = len(self.lines)
-        LineNumberStream.__init__(self)
-
-    def getLine(self):
-        if self.curLine < self.numLines:
-            return self.lines[self.curLine]
-
-def split(text):
-    lines = string.split(text, '\n')
-    if lines[-1] == '':
-        lines.pop()
-    return lines
-
-def eatNewLines(stream):
-    while 1:
-       line = stream.get()
-       if line is None or len(string.strip(line)):
-           return line
-
-COMMENT_LINE_REGEX = re.compile(R"\s*#")
-def isComment(line):
-    return line is not None and COMMENT_LINE_REGEX.match(line)
-
-class CommentEater:
-    def __init__(self, stream):
-        self.stream = stream
-        self.peeked = 1
-        self.line = eatNewLines(stream)
-        self.eatComments()
-
-    def eatComments(self):
-        while isComment(self.line):
-            self.line = self.stream.get()
-
-    def peek(self):
-        if self.peeked:
-            return self.line
-        self.peeked = 1
-        self.line = self.stream.get()
-        self.eatComments()
-        return self.line
-
-    def lastLineRead(self):
-        return self.stream.lastLineRead()
-
-    def pop(self):
-        data = self.peek()
-        self.peeked = 0
-        return data
-
-class NestedText:
-    def __init__(self, stream):
-        self.commentEater = CommentEater(stream)
-        self.reset()
-
-    def lastLineRead(self):
-        return self.commentEater.lastLineRead()
-
-    def reset(self):
-        self.indentLevel = 0
-        self.oldIndents = [0]
-
-    def peek(self):
-        nextLine = self.commentEater.peek()
-        if nextLine is not None:
-            if indentLevel(nextLine) >= self.indentLevel:
-                return nextLine[self.indentLevel:]
-            elif nextLine == '':
-                return ''                
-
-    def pop(self):
-        line = self.peek()
-        if line is None:
-            self.indentLevel = self.oldIndents.pop()
-            return
-        self.commentEater.pop()
-        return line
-
-    def popNestedLines(self):
-        nextLine = self.peek()
-        if nextLine is None or nextLine == '' or nextLine[0] != ' ':
-            return []
-        self.nestToNextLine()
-        lines = []
-        while 1:
-            line = self.pop()
-            if line is None:
-                break
-            lines.append(line)
-        return lines
-
-    def nestToNextLine(self):
-        line = self.commentEater.peek()
-        indentation = indentLevel(line)
-        if len(self.oldIndents) > 1 and indentation <= self.indentLevel:
-            self.error("Inadequate indentation", line)
-        self.setNewIndent(indentation)
-
-    def nestBySpecificAmount(self, adjust):
-        self.setNewIndent(self.indentLevel + adjust)
-        
-    def setNewIndent(self, indentLevel):
-        self.oldIndents.append(self.indentLevel)
-        self.indentLevel = indentLevel    
-
-class YamlLoaderException(Exception):
-    def __init__(self, *args):
-        (self.msg, self.lineNum, self.line, self.filename) = args
-
-    def __str__(self):
-        msg = """\
-%(msg)s:
-near line %(lineNum)d:
-%(line)s
-""" % self.__dict__
-        if self.filename:
-            msg += "file: " + self.filename
-        return msg
-
-class NestedDocs(NestedText):
-    def __init__(self, stream):
-        self.filename = stream.filename
-        NestedText.__init__(self,stream)
-        line = NestedText.peek(self)
-        self.sep = '---'
-        if self.startsWithSep(line):
-            self.eatenDocSep = NestedText.pop(self)
-        else:
-            self.eatenDocSep = self.sep
-
-    def startsWithSep(self,line):
-        if line and self.sep == line[:3]: return 1
-        return 0
-
-    def popDocSep(self):
-        line = self.eatenDocSep
-        self.eatenDocSep = None
-        self.reset()
-        return line
-
-    def pop(self):
-        if self.eatenDocSep is not None:
-            raise "error"
-        line = self.commentEater.peek()
-        if line and self.startsWithSep(line):
-            self.eatenDocSep = NestedText.pop(self)
-            return None
-        return NestedText.pop(self)
-
-    def error(self, msg, line):
-        raise YamlLoaderException(msg, self.lastLineRead(), line, self.filename)
-
-def noLineFeed(s):
-    while s[-1:] in ('\n', '\r'):
-        s = s[:-1]
-    return s
diff --git a/koan/yaml/timestamp.py b/koan/yaml/timestamp.py
deleted file mode 100644
index abcb2e6..0000000
--- a/koan/yaml/timestamp.py
+++ /dev/null
@@ -1,145 +0,0 @@
-import time, re, string
-from types import ListType, TupleType
-
-PRIVATE_NOTICE = """
-  This module is considered to be private implementation
-  details and is subject to change.  Please only use the
-  objects and methods exported to the top level yaml package.
-"""
-
-#
-# Time specific operations
-#
-
-_splitTime = re.compile('\-|\s|T|t|:|\.|Z')
-matchTime = re.compile(\
-          '\d+-\d+-\d+([\s|T|t]\d+:\d+:\d+.\d+(Z|(\s?[\-|\+]\d+:\d+)))?')
-
-def _parseTime(val):
-    if not matchTime.match(val): raise ValueError(val)
-    tpl = _splitTime.split(val)
-    if not(tpl): raise ValueError(val)
-    siz = len(tpl)
-    sec = 0
-    if 3 == siz:
-       tpl += [0,0,0,0,0,-1]
-    elif 7 == siz:
-       tpl.append(0)
-       tpl.append(-1)
-    elif 8 == siz:
-       if len(tpl.pop()) > 0: raise ValueError(val)
-       tpl.append(0)
-       tpl.append(-1)
-    elif 9 == siz or 10 == siz:
-       mn = int(tpl.pop())
-       hr = int(tpl.pop())
-       sec = (hr*60+mn)*60
-       if val.find("+") > -1: sec = -sec
-       if 10 == siz: tpl.pop()
-       tpl.append(0)
-       tpl.append(-1)
-    else:
-       raise ValueError(val)
-    idx = 0
-    while idx < 9:
-       tpl[idx] = int(tpl[idx])
-       idx += 1
-    if tpl[1] < 1 or tpl[1] > 12: raise ValueError(val)
-    if tpl[2] < 1 or tpl[2] > 31: raise ValueError(val)
-    if tpl[3] > 24: raise ValueError(val)
-    if tpl[4] > 61: raise ValueError(val)
-    if tpl[5] > 61: raise ValueError(val)
-    if tpl[0] > 2038:
-        #TODO: Truncation warning
-        tpl = (2038,1,18,0,0,0,0,0,-1)
-    tpl = tuple(tpl)
-    ret = time.mktime(tpl)
-    ret = time.localtime(ret+sec)
-    ret = ret[:8] + (0,)
-    return ret
-
-
-class _timestamp:
-    def __init__(self,val=None):
-        if not val:
-           self.__tval = time.gmtime()
-        else:
-           typ = type(val)
-           if ListType == typ:
-               self.__tval = tuple(val)
-           elif TupleType == typ:
-               self.__tval = val
-           else:
-               self.__tval = _parseTime(val)
-           if 9 != len(self.__tval): raise ValueError
-    def __getitem__(self,idx): return self.__tval[idx]
-    def __len__(self): return 9
-    def strftime(self,format): return time.strftime(format,self.__tval)
-    def mktime(self):          return time.mktime(self.__tval)
-    def asctime(self):  return time.asctime(self.__tval)
-    def isotime(self):
-        return "%04d-%02d-%02dT%02d:%02d:%02d.00Z" % self.__tval[:6]
-    def __repr__(self): return "yaml.timestamp('%s')" % self.isotime()
-    def __str__(self):  return self.isotime()
-    def to_yaml_implicit(self): return self.isotime()
-    def __hash__(self): return hash(self.__tval[:6])
-    def __cmp__(self,other):
-        try:
-            return cmp(self.__tval[:6],other.__tval[:6])
-        except AttributeError:
-            return -1
-
-try: # inherit from mx.DateTime functionality if available
-    from mx import DateTime
-    class timestamp(_timestamp):
-        def __init__(self,val=None):
-            _timestamp.__init__(self,val)
-            self.__mxdt = DateTime.mktime(self.__tval)
-        def __getattr__(self, name):
-              return getattr(self.__mxdt, name)
-except:
-    class timestamp(_timestamp): pass
-
-
-
-def unquote(expr):
-    """
-        summary: >
-           Simply returns the unquoted string, and the
-           length of the quoted string token at the
-           beginning of the expression.
-    """
-    tok = expr[0]
-    if "'" == tok:
-        idx = 1
-        odd = 0
-        ret = ""
-        while idx < len(expr):
-            chr = expr[idx]
-            if "'" == chr:
-                if odd: ret += chr
-                odd = not odd
-            else:
-                if odd:
-                    tok = expr[:idx]
-                    break
-                ret += chr
-            idx += 1
-        if "'" == tok: tok = expr
-        return (ret,len(tok))
-    if '"' == tok:
-        idx = 1
-        esc = 0
-        while idx < len(expr):
-            chr = expr[idx]
-            if '"' == chr and not esc:
-                tok = expr[:idx] + '"'
-                break
-            if '\\' == chr and not esc: esc = 1
-            else: esc = 0
-            idx += 1
-        if '"' == tok:
-            raise SyntaxError("unmatched quote: " + expr)
-        ret = eval(tok)  #TODO: find better way to unquote
-        return (ret,len(tok))
-    return (expr,len(expr))
diff --git a/koan/yaml/ypath.py b/koan/yaml/ypath.py
deleted file mode 100644
index 51d9d2f..0000000
--- a/koan/yaml/ypath.py
+++ /dev/null
@@ -1,462 +0,0 @@
-from types import ListType, StringType, IntType, DictType, InstanceType
-import re
-from urllib import quote
-from timestamp import unquote
-
-noTarget = object()
-
-def escape(node):
-    """
-        summary: >
-            This function escapes a given key so that it
-            may appear within a ypath.  URI style escaping
-            is used so that ypath expressions can be a 
-            valid URI expression.
-    """
-    typ = type(node)
-    if typ is IntType: return str(node)
-    if typ is StringType: 
-        return quote(node,'')
-    raise ValueError("TODO: Support more than just string and integer keys.")
-
-class context:
-    """
-        summary: >
-            A ypath visit context through a YAML rooted graph.
-            This is implemented as a 3-tuple including the parent
-            node, the current key/index and the value.  This is
-            an immutable object so it can be cached.
-        properties: 
-            key:    mapping key or index within the parent collection
-            value:  current value within the parent's range
-            parent: the parent context
-            root:   the very top of the yaml graph
-            path:   a tuple of the domain keys
-        notes: >
-            The context class doesn't yet handle going down the
-            domain side of the tree... 
-    """         
-    def __init__(self,parent,key,value):
-        """
-            args:
-                parent: parent context (or None if this is the root)
-                key:    mapping key or index for this context
-                value:  value of current location...
-        """
-        self.parent = parent
-        self.key    = key
-        self.value  = value
-        if parent: 
-            assert parent.__class__ is self.__class__
-            self.path = parent.path + (escape(key),)
-            self.root = parent.root
-        else:      
-            assert not key
-            self.path = tuple()
-            self.root = self
-    def __setattr__(self,attname,attval):
-        if attname in ('parent','key','value'):
-            if self.__dict__.get(attname):
-                 raise ValueError("context is read-only")
-        self.__dict__[attname] = attval
-    def __hash__(self): return hash(self.path)
-    def __cmp__(self,other):   
-        try:
-            return cmp(self.path,other.path)
-        except AttributeError:
-            return -1
-    def __str__(self):
-        if self.path:
-            return "/".join(('',)+self.path)
-        else:
-            return '/'
-
-def to_context(target):
-    if type(target) is InstanceType:
-        if target.__class__ is context:
-            return target
-    return context(None,None,target)
-
-def context_test():
-    lst = ['value']
-    map = {'key':lst}
-    x = context(None,None,map)
-    y = context(x,'key',lst)
-    z = context(y,0,'value')
-    assert ('key',) == y.path
-    assert 'key'    == y.key
-    assert lst      == y.value
-    assert x        == y.parent
-    assert x        == y.root
-    assert 0        == z.key
-    assert 'value'  == z.value
-    assert y        == z.parent
-    assert x        == z.root 
-    assert hash(x)  
-    assert hash(y)
-    assert hash(z)
-    assert '/' == str(x)
-    assert '/key' == str(y)
-    assert '/key/0' == str(z)
-
-class null_seg:
-    """
-        summary: >
-            This is the simplest path segment, it
-            doesn't return any results and doesn't
-            depend upon its context.  It also happens to 
-            be the base class which all segments derive.
-    """
-    def __iter__(self): 
-        return self
-    def next_null(self):
-        raise StopIteration
-    def bind(self,cntx):  
-        """
-            summary: >
-                The bind function is called whenever
-                the parent context has changed.
-        """
-        assert(cntx.__class__ is context)
-        self.cntx = cntx
-    def apply(self,target):
-        self.bind(to_context(target))
-        return iter(self)
-    def exists(self,cntx):
-        try:
-            self.bind(cntx)
-            self.next()
-            return 1
-        except StopIteration:
-            return 0
-    next = next_null
- 
-class self_seg(null_seg):
-    """
-        summary: >
-            This path segment returns the context
-            node exactly once.
-    """
-    def __str__(self): return '.'
-    def next_self(self):
-        self.next = self.next_null
-        return self.cntx
-    def bind(self,cntx):
-        null_seg.bind(self,cntx)
-        self.next = self.next_self
-
-class root_seg(self_seg):
-    def __str__(self): return '/'
-    def bind(self,cntx):  
-        self_seg.bind(self,cntx.root)
-
-class parent_seg(self_seg):
-    def __str__(self): return '..'
-    def bind(self,cntx):
-        if cntx.parent: cntx = cntx.parent
-        self_seg.bind(self,cntx)
-
-class wild_seg(null_seg):
-    """
-        summary: >
-            The wild segment simply loops through
-            all of the sub-contexts for a given object.
-            If there aren't any children, this isn't an
-            error it just doesn't return anything.
-    """
-    def __str__(self): return '*'
-    def next_wild(self):
-        key = self.keys.next()
-        return context(self.cntx,key,self.values[key])
-    def bind(self,cntx):  
-        null_seg.bind(self,cntx)
-        typ = type(cntx.value)
-        if typ is ListType:
-            self.keys   = iter(xrange(0,len(cntx.value)))
-            self.values = cntx.value
-            self.next   = self.next_wild
-            return
-        if typ is DictType:
-            self.keys   = iter(cntx.value)
-            self.values = cntx.value
-            self.next   = self.next_wild
-            return 
-        self.next = self.next_null
-
-class trav_seg(null_seg):
-    """
-        summary: >
-            This is a recursive traversal of the range, preorder.
-            It is a recursive combination of self and wild.
-    """
-    def __str__(self): return '/'
-    def next(self): 
-        while 1:
-            (cntx,seg) = self.stk[-1]
-            if not seg:
-                seg = wild_seg()
-                seg.bind(cntx)
-                self.stk[-1] = (cntx,seg)
-                return cntx
-            try:
-                cntx = seg.next()
-                self.stk.append((cntx,None))
-            except StopIteration:
-                self.stk.pop()
-                if not(self.stk):
-                    self.next = self.next_null
-                    raise StopIteration
-
-    def bind(self,cntx):
-        null_seg.bind(self,cntx)
-        self.stk = [(cntx,None)]
-
-class match_seg(self_seg):
-    """
-        summary: >
-            Matches a particular key within the
-            current context.  Kinda boring.
-    """
-    def __str__(self): return str(self.key)
-    def __init__(self,key):
-        #TODO: Do better implicit typing
-        try:
-           key = int(key)
-        except: pass
-        self.key = key
-    def bind(self,cntx):
-        try: 
-            mtch = cntx.value[self.key]
-            cntx = context(cntx,self.key,mtch)
-            self_seg.bind(self,cntx)
-        except:
-            null_seg.bind(self,cntx)
-        
-class conn_seg(null_seg):
-    """
-        summary: >
-            When two segments are connected via a slash,
-            this is a composite.  For each context of the
-            parent, it binds the child, and returns each
-            context of the child.
-    """
-    def __str__(self): 
-        if self.parent.__class__ == root_seg:  
-            return "/%s" % self.child
-        return "%s/%s" % (self.parent, self.child)
-    def __init__(self,parent,child):
-        self.parent = parent
-        self.child  = child
-    def next(self):
-        while 1:
-            try:
-                return self.child.next()
-            except StopIteration:
-                cntx = self.parent.next()
-                self.child.bind(cntx)
- 
-    def bind(self,cntx):
-        null_seg.bind(self,cntx)
-        self.parent.bind(cntx)
-        try:
-            cntx = self.parent.next()
-        except StopIteration: 
-            return
-        self.child.bind(cntx)
-
-
-class pred_seg(null_seg):
-    def __str__(self): return "%s[%s]" % (self.parent, self.filter)
-    def __init__(self,parent,filter):
-        self.parent = parent
-        self.filter = filter
-    def next(self):
-        while 1:
-            ret = self.parent.next()
-            if self.filter.exists(ret):
-                return ret
-    def bind(self,cntx):
-        null_seg.bind(self,cntx)
-        self.parent.bind(cntx)
-
-class or_seg(null_seg):
-    def __str__(self): return "%s|%s" % (self.lhs,self.rhs)
-    def __init__(self,lhs,rhs):
-        self.rhs = rhs
-        self.lhs = lhs
-        self.unq = {}
-    def next(self):
-        seg = self.lhs
-        try:
-            nxt = seg.next()
-            self.unq[nxt] = nxt
-            return nxt
-        except StopIteration: pass
-        seg = self.rhs
-        while 1:
-            nxt = seg.next()
-            if self.unq.get(nxt,None): 
-                continue  
-            return nxt
-    def bind(self,cntx):
-        null_seg.bind(self,cntx)
-        self.lhs.bind(cntx)
-        self.rhs.bind(cntx)
-
-class scalar:
-    def __init__(self,val):  
-        self.val = val
-    def __str__(self): 
-        return str(self.val)
-    def value(self): 
-        return self.val
-
-class equal_pred: 
-    def exists_true(self,cntx): return 1
-    def exists_false(self,cntx): return 0
-    def exists_scalar(self,cntx):
-        self.rhs.bind(cntx)
-        try:
-            while 1:
-                cntx = self.rhs.next()
-                if str(cntx.value) == self.lhs:  #TODO: Remove type hack
-                     return 1
-        except StopIteration: pass
-        return 0
-    def exists_segment(self,cntx):
-        raise NotImplementedError()
-    def __init__(self,lhs,rhs):
-        if lhs.__class__ == scalar:
-            if rhs.__class__ == scalar:
-                if rhs.value() == lhs.value():
-                    self.exists = self.exists_true
-                else:
-                    self.exists = self.exists_false
-            else:
-                self.exists = self.exists_scalar
-        else:
-            if rhs.__class__ == scalar:
-                (lhs,rhs) = (rhs,lhs)
-                self.exists = self.exists_scalar
-            else:
-                self.exists = self.exists_segment
-        self.lhs = str(lhs.value())  #TODO: Remove type hack
-        self.rhs = rhs
- 
-matchSegment = re.compile(r"""^(\w+|/|\.|\*|\"|\')""")
-
-def parse_segment(expr):
-    """
-        Segments occur between the slashes...
-    """
-    mtch = matchSegment.search(expr)
-    if not(mtch): return (None,expr)
-    tok = mtch.group(); siz = len(tok)
-    if   '/' == tok: return (trav_seg(),expr)
-    elif '.' == tok: 
-        if len(expr) > 1 and '.' == expr[1]:
-            seg = parent_seg()
-            siz = 2
-        else: 
-            seg = self_seg()
-    elif '*' == tok: seg = wild_seg()
-    elif '"' == tok or "'" == tok:
-        (cur,siz) = unquote(expr)
-        seg = match_seg(cur)
-    else:
-        seg = match_seg(tok)
-    return (seg,expr[siz:])
-
-matchTerm = re.compile(r"""^(\w+|/|\.|\(|\"|\')""")
-
-def parse_term(expr):
-    mtch = matchTerm.search(expr)
-    if not(mtch): return (None,expr)
-    tok = mtch.group(); siz = len(tok)
-    if '/' == tok or '.' == tok:
-        return parse(expr)
-    if '(' == tok:
-        (term,expr) = parse_predicate(expr)
-        assert ')' == expr[0]
-        return (term,expr[1:])
-    elif '"' == tok or "'" == tok:
-        (val,siz) = unquote(expr)
-    else:
-        val = tok; siz = len(tok)
-    return (scalar(val),expr[siz:])
-
-def parse_predicate(expr):
-    (term,expr) = parse_term(expr)
-    if not term: raise SyntaxError("term expected: '%s'" % expr)
-    tok = expr[0]
-    if '=' == tok:
-        (rhs,expr) = parse_term(expr[1:])
-        return (equal_pred(term,rhs),expr)
-    if '(' == tok:
-        raise "No functions allowed... yet!"
-    if ']' == tok or ')' == tok:
-        if term.__class__ is scalar:
-            term = match_seg(str(term))
-        return (term,expr)
-    raise SyntaxError("ypath: expecting operator '%s'" % expr)
-
-def parse_start(expr):
-    """
-        Initial checking on the expression, and 
-        determine if it is relative or absolute.
-    """
-    if type(expr) != StringType or len(expr) < 1: 
-        raise TypeError("string required: " + repr(expr))
-    if '/' == expr[0]:
-        ypth = root_seg()
-    else:
-        ypth = self_seg()
-        expr = '/' + expr
-    return (ypth,expr)
-
-def parse(expr):
-    """
-        This the parser entry point, the top level node
-        is always a root or self segment.  The self isn't
-        strictly necessary, but it keeps things simple.
-    """
-    (ypth,expr) = parse_start(expr)
-    while expr:
-        tok = expr[0]
-        if '/' == tok:
-            (child, expr) = parse_segment(expr[1:])    
-            if child: ypth = conn_seg(ypth,child)
-            continue
-        if '[' == tok:
-            (filter, expr) = parse_predicate(expr[1:])
-            assert ']' == expr[0]
-            expr = expr[1:]
-            ypth = pred_seg(ypth,filter)
-            continue
-        if '|' == tok:
-            (rhs, expr) = parse(expr[1:])
-            ypth = or_seg(ypth,rhs)
-            continue
-        if '(' == tok:
-            (child,expr) = parse(expr[1:])
-            assert ')' == expr[0]
-            expr = expr[1:]
-            ypth = conn_seg(ypth,child)
-            continue
-        break
-    return (ypth,expr)
-
-class convert_to_value(null_seg):
-    def __init__(self,itr):
-        self.itr = itr
-    def next(self):
-        return self.itr.next().value
-    def bind(self,cntx):
-        self.itr.bind(cntx)
-
-def ypath(expr,target=noTarget,cntx=0):
-    (ret,expr) = parse(expr)
-    if expr: raise SyntaxError("ypath parse error `%s`" % expr)
-    if not cntx: ret = convert_to_value(ret)
-    if target is noTarget: return ret
-    return ret.apply(target)

hooks/update
---
Git Source Code Management System
hooks/update refs/heads/master \
  c8ca54b3492df95bc0ae9f99c826a5be88a39a69 \
  1a82d21838cd892eeb3abb1693edab4a0112f2b2




More information about the Et-mgmt-commits-list mailing list