summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--server/util/tdf.py101
1 files 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.