From e929ff9fa198400f97f1bd848b3efd8aacf7514a Mon Sep 17 00:00:00 2001
From: Christian Siefkes <christian@siefkes.net>
Date: Mon, 1 Mar 2010 17:54:04 +0100
Subject: Worked on TDF writer

---
 server/util/tdf.py | 101 +++++++++++++++++++++++++++++++++++++----------------
 1 file changed, 70 insertions(+), 31 deletions(-)

diff --git a/server/util/tdf.py b/server/util/tdf.py
index b7547cd..0e4c179 100644
--- a/server/util/tdf.py
+++ b/server/util/tdf.py
@@ -20,6 +20,7 @@ doc/formats for a description. The API for reading and writing TDF is similar to
 the Python standard modules marshal, pickle, and json.
 """
 
+import cStringIO
 import re
 import sys
 
@@ -102,6 +103,8 @@ def dump(obj, fp, encoding = u"UTF-8", comment = None, complexMap = True,
     The *indentPrefix* specifies the string of whitespace preceding each deeper
     indentation level. Only spaces and tabs are allowed. By default, two spaces
     are used.
+
+    A `ValueError` is thrown if the *indentPrefix* is invalid.
     """
     # Delegate
     return _doDump(obj, fp, encoding, comment, complexMap, indentPrefix)
@@ -119,9 +122,13 @@ def dumps(obj, comment = None, complexMap = True, indentPrefix = u"  "):
     The *indentPrefix* specifies the string of whitespace preceding each deeper
     indentation level. Only spaces and tabs are allowed. By default, two spaces
     are used.
+
+    A `ValueError` is thrown if the *indentPrefix* is invalid.
     """
-    # TODO implement dumps
-    pass
+    # Create string buffer object and delegate
+    output = cStringIO.StringIO()
+    dump(obj, output, None, comment, complexMap,indentPrefix)
+    return output.getvalue()
 
 def load(fp, encoding = u"UTF-8"):
     """
@@ -162,51 +169,45 @@ def _doDump(obj, fp, encoding, comment, complexMap, indentPrefix,
     """
     Helper function that serializes *obj* as a TDF formatted stream to *fp*.
     """
-    # TODO check that indentPrefix contains only spaces+tabs and it not empty
+    # Check that indentPrefix contains only spaces+tabs and it not empty
+    if indentPrefix == u"":
+        raise ValueError("Indent prefix is empty")
+    elif indentPrefix.strip(_INLINE_WS) != u"":
+        raise ValueError("Indent prefix contains invalid characters (only "
+            "spaces and tabs are allowed): %r" % indentPrefix.strip(_INLINE_WS))
+
     if comment:
         # Serialize the comment at the begin of the stream
         commentlines = comment.splitlines()
         for line in commentlines:
-            _writeEncoded(fp, u"%s %s\n" % (_COMMENT_START, line), encoding)
+            _writeEncoded(u"%s %s\n" % (_COMMENT_START, line), fp, encoding)
 
     # Determine type of object
     if isinstance(obj, dict):
         # Serialize dictionary (possibly as complex map)
         if complexMap and currentIndent == u"" and _isComplexMap(obj):
-            # TODO Serialize as a complex map
-            pass
+            # Serialize as a complex map
+            for k, v in obj.iteritems():
+                _dumpComplexKey(k, fp, encoding, indentPrefix, currentIndent)
+                # Call myself recursively without increasing the indent
+                _doDump(v, fp, encoding, None, complexMap, indentPrefix,
+                        currentIndent)
         else:
             # TODO Serialize as a normal map
-            pass
+            for k, v in obj.iteritems():
+                pass
         # TODO For lists and maps, call myself recursively, setting
         # currentIndent += indentPrefix.
     elif _isListLike(obj):
         # TODO Serialize list (possibly as an inline list)
         pass
     else:
-        # TODO throw exception if currentIndent == u""
-        if isinstance(obj, basestring):
-            # TODO Serialize string
-            pass
-        elif isinstance(obj, int) or isinstance(obj, long):
-            # TODO Serialize integer
-            pass
-        elif isinstance(obj, float):
-            # TODO Serialize float
-            pass
-        elif obj == True:
-            # TODO Serialize true
-            pass
-        elif obj == False:
-            # TODO Serialize false
-            pass
-        elif obj == None:
-            # TODO Serialize null
-            pass
-        else:
-            # TODO throw TypeError
-            pass
-    pass
+        # It must be an atom
+        if currentIndent == u"":
+            raise TypeError(u"TDF document must be a list or map: %r" % obj)
+        _writeEncoded(u"%s" % currentIndent, fp, encoding)
+        _dumpAtom(obj, fp, encoding, indentPrefix, currentIndent)
+        _writeEncoded(u"\n", fp, encoding)
 
 def _doLoads(s, commentsStripped = False):
     """
@@ -241,6 +242,44 @@ def _doLoads(s, commentsStripped = False):
     else:
         return _parseMap(s)
 
+def _dumpAtom(atom, fp, encoding, indentPrefix, currentIndent):
+    """
+    Dump an *atom*. If the atom is a string that spans multiple lines, the
+    continuation lines are prefixed by *currentIndent* + *indentPrefix*.
+    The first line of the atom is NOT indented; neither is a newline or other
+    terminator written after the atom.
+
+    A *TypeError* is thrown if *atom* is not an atomic value (string, number,
+    Boolean, or None).
+    """
+    if isinstance(atom, basestring):
+        # TODO Serialize string, handling indentation + escapes
+        pass
+    elif isinstance(atom, int) or isinstance(atom, long) or \
+            isinstance(atom, float):
+        # Serialize number
+        _writeEncoded(str(atom), fp, encoding)
+    elif atom == True:
+        # Serialize true
+        _writeEncoded(_TRUE, fp, encoding)
+    elif atom == False:
+        # Serialize false
+        _writeEncoded(_FALSE, fp, encoding)
+    elif atom == None:
+        # Serialize null
+        _writeEncoded(_NULL, fp, encoding)
+    else:
+        # Throw TypeError
+        raise TypeError(u"Not a valid atom: %r" % atom)
+
+def _dumpComplexKey(k, fp, encoding, indentPrefix, currentIndent):
+    """
+    Dump a key in a complex map.
+    """
+    _writeEncoded(u"%s%s" % (currentIndent, _BRACKET_KEY_START), fp, encoding)
+    _dumpAtom(k, fp, encoding, indentPrefix, currentIndent)
+    _writeEncoded(u"%s\n" % _BRACKET_KEY_END, fp, encoding)
+
 def _isListLike(obj):
     """
     Check whether *obj* is a list-like object. Return True iff *obj* is a list,
@@ -579,7 +618,7 @@ def _splitItems(s):
             result.append(item)
     return result
 
-def _writeEncoded(fp, s, encoding = None):
+def _writeEncoded(s, fp, encoding = None):
     """
     Writes the string *s* to the file-like object *fp*, using the specified
     character *encoding*. If *encoding* is None, no encoding is performed.
-- 
cgit v1.2.3