Index: /trunk/ext/_syckmodule.c
===================================================================
--- /trunk/ext/_syckmodule.c	(revision 20)
+++ /trunk/ext/_syckmodule.c	(revision 25)
@@ -43,5 +43,5 @@
 PyDoc_STRVAR(PySyckNode_doc,
     "_syck.Node() -> TypeError\n\n"
-    "_syck.Node is an abstract type. It is a base type for _syck.Scalar,\n"
+    "_syck.Node is an abstract type. It is the base type for _syck.Scalar,\n"
     "_syck.Seq, and _syck.Map. You cannot create an instance of _syck.Node\n"
     "directly. You may use _syck.Node for type checking or subclassing.\n");
@@ -220,5 +220,5 @@
     "      -> a Scalar node\n\n"
     "_syck.Scalar represents a scalar node in Syck parser and emitter\n"
-    "graphs. A scalar node points to a single string value.\n");
+    "trees. A scalar node points to a single string value.\n");
 
 typedef struct {
@@ -549,5 +549,5 @@
     "Seq(value=[], tag=None, inline=False) -> a Seq node\n\n"
     "_syck.Seq represents a sequence node in Syck parser and emitter\n"
-    "graphs. A sequence node points to an ordered set of subnodes.\n");
+    "trees. A sequence node points to an ordered set of subnodes.\n");
 
 typedef struct {
@@ -718,7 +718,7 @@
 
 PyDoc_STRVAR(PySyckMap_doc,
-    "Map(value='', tag=None, inline=False) -> a Map node\n\n"
+    "Map(value={}, tag=None, inline=False) -> a Map node\n\n"
     "_syck.Map represents a mapping node in Syck parser and emitter\n"
-    "graphs. A mapping node points to an unordered collections of pairs.\n");
+    "trees. A mapping node points to an unordered collections of pairs.\n");
 
 typedef struct {
@@ -833,5 +833,5 @@
         PyDoc_STR("the node kind, always 'map', read-only"), &PySyck_MapKind},
     {"value", (getter)PySyckNode_getvalue, (setter)PySyckMap_setvalue,
-        PyDoc_STR("the node value, a mapping"), NULL},
+        PyDoc_STR("the node value, a list of pairs or a dictionary"), NULL},
     {"tag", (getter)PySyckNode_gettag, (setter)PySyckNode_settag,
         PyDoc_STR("the node tag, a string or None"), NULL},
@@ -893,5 +893,5 @@
     "      -> a Parser object\n\n"
     "_syck.Parser is a low-lever wrapper of the Syck parser. It parses\n"
-    "a YAML stream and produces a graph of Nodes.\n");
+    "a YAML stream and produces a tree of Nodes.\n");
 
 typedef struct {
@@ -1017,5 +1017,5 @@
 static PyGetSetDef PySyckParser_getsetters[] = {
     {"source", (getter)PySyckParser_getsource, NULL,
-        PyDoc_STR("IO source, a string or file-like object"), NULL},
+        PyDoc_STR("IO source, a string or a file-like object"), NULL},
     {"implicit_typing", (getter)PySyckParser_getimplicit_typing, NULL,
         PyDoc_STR("implicit typing of builtin YAML types"), NULL},
@@ -1253,5 +1253,6 @@
 
     if (self->parsing) {
-        PyErr_SetString(PyExc_RuntimeError, "do not call Parser.parse while it is already parsing");
+        PyErr_SetString(PyExc_RuntimeError,
+                "do not call Parser.parse while it is already running");
         return NULL;
     }
@@ -1292,6 +1293,7 @@
 PyDoc_STRVAR(PySyckParser_parse_doc,
     "parse() -> the root Node object\n\n"
-    "Parses the source and returns the next document. On EOF, returns None\n"
-    "and sets the 'eof' attribute on.\n");
+    "Parses the source and returns the root of the Node tree. Call it\n"
+    "several times to retrieve all documents from the source. On EOF,\n"
+    "returns None and sets the 'eof' attribute on.\n");
 
 static PyMethodDef PySyckParser_methods[] = {
@@ -1348,7 +1350,8 @@
 
 PyDoc_STRVAR(PySyckEmitter_doc,
-    "Emitter(output, headless=False, use_header=True, explicit_typing=True,"
-    "        style=None, best_width=80, indent=2) -> an Emitter object\n\n"
-    "_syck.Emitter is a low-lever wrapper of the Syck emitter. It emit\n"
+    "Emitter(output, headless=False, use_header=False, use_version=False,\n"
+    "        explicit_typing=True, style=None, best_width=80, indent=2)\n"
+    "                -> an Emitter object\n\n"
+    "_syck.Emitter is a low-lever wrapper of the Syck emitter. It emits\n"
     "a tree of Nodes into a YAML stream.\n");
 
@@ -1530,7 +1533,7 @@
         PyDoc_STR("headerless document flag"), NULL},
     {"use_header", (getter)PySyckEmitter_getuse_header, NULL,
-        PyDoc_STR("force header"), NULL},
+        PyDoc_STR("force header flag"), NULL},
     {"use_version", (getter)PySyckEmitter_getuse_version, NULL,
-        PyDoc_STR("force version"), NULL},
+        PyDoc_STR("force version flag"), NULL},
     {"explicit_typing", (getter)PySyckEmitter_getexplicit_typing, NULL,
         PyDoc_STR("explicit typing for all collections"), NULL},
@@ -1752,19 +1755,4 @@
     Py_INCREF(output);
     self->output = output;
-
-/*
-    self->emitter = syck_new_emitter();
-    self->emitter->bonus = self;
-    self->emitter->headless = self->headless;
-    self->emitter->use_header = use_header;
-    self->emitter->use_version = use_version;
-    self->emitter->explicit_typing = explicit_typing;
-    self->emitter->style = self->style;
-    self->emitter->best_width = self->best_width;
-    self->emitter->indent = self->indent;
-
-    syck_emitter_handler(self->emitter, PySyckEmitter_node_handler);
-    syck_output_handler(self->emitter, PySyckEmitter_write_handler);
-*/
 
     self->emitting = 0;
@@ -1974,5 +1962,5 @@
 PyDoc_STRVAR(PySyckEmitter_emit_doc,
     "emit(root_node) -> None\n\n"
-    "Emit the Node tree to the output.\n");
+    "Emits the Node tree to the output.\n");
 
 static PyMethodDef PySyckEmitter_methods[] = {
@@ -2033,5 +2021,43 @@
 
 PyDoc_STRVAR(PySyck_doc,
-    "low-level wrapper for the Syck YAML parser and emitter");
+    "_syck is a low-level wrapper for the Syck YAML parser and emitter.\n"
+    "Do not use it directly, use the module 'syck' instead.\n");
+
+static int
+add_slotnames(PyTypeObject *type)
+{
+    PyObject *slotnames;
+    PyObject *name;
+    PyGetSetDef *getsetter;
+
+    if (!type->tp_getset) return 0;
+    if (!type->tp_dict) return 0;
+
+    slotnames = PyList_New(0);
+    if (!slotnames) return -1;
+
+    for (getsetter = type->tp_getset; getsetter->name; getsetter++) {
+        if (!getsetter->set) continue;
+        name = PyString_FromString(getsetter->name);
+        if (!name) {
+           Py_DECREF(slotnames);
+           return -1;
+        }
+        if (PyList_Append(slotnames, name) < 0) {
+            Py_DECREF(name);
+            Py_DECREF(slotnames);
+            return -1;
+        }
+        Py_DECREF(name);
+    }
+
+    if (PyDict_SetItemString(type->tp_dict, "__slotnames__", slotnames) < 0) {
+        Py_DECREF(slotnames);
+        return -1;
+    }
+
+    Py_DECREF(slotnames);
+    return 0;
+}
 
 PyMODINIT_FUNC
@@ -2044,7 +2070,13 @@
     if (PyType_Ready(&PySyckScalar_Type) < 0)
         return;
+    if (add_slotnames(&PySyckScalar_Type) < 0)
+        return;
     if (PyType_Ready(&PySyckSeq_Type) < 0)
         return;
+    if (add_slotnames(&PySyckSeq_Type) < 0)
+        return;
     if (PyType_Ready(&PySyckMap_Type) < 0)
+        return;
+    if (add_slotnames(&PySyckMap_Type) < 0)
         return;
     if (PyType_Ready(&PySyckParser_Type) < 0)
Index: /trunk/tests/test_loader.py
===================================================================
--- /trunk/tests/test_loader.py	(revision 20)
+++ /trunk/tests/test_loader.py	(revision 23)
@@ -136,4 +136,9 @@
 """
 
+MUTABLE_KEY = """
+? []
+: []
+"""
+
 class TestDocuments(test_parser.TestDocuments):
 
@@ -181,12 +186,11 @@
 
     def _testFileValues(self, (source, structure)):
-        filename = os.tempnam('/tmp', '_syck_test_')
-        file(filename, 'wb').write(source)
-        try:
-            self.assertEqualStructure(syck.parse(file(filename)), structure)
-            self.assertEqual(syck.load(file(filename)), structure)
-        except:
-            os.remove(filename)
-            raise
+        tempfile = os.tmpfile()
+        tempfile.write(source)
+        tempfile.seek(0)
+        self.assertEqualStructure(syck.parse(tempfile), structure)
+        tempfile.seek(0)
+        self.assertEqual(syck.load(tempfile), structure)
+        tempfile.seek(0)
 
 class TestImplicitScalars(unittest.TestCase):
@@ -202,13 +206,13 @@
 
     def testFloat(self):
-        self.assertAlmostEqual(syck.load('6.8523015e+5'), 685230.15)
+        self.assertEqual(syck.load('6.8523015e+5'), 685230.15)
         # Syck does not understand '_'.
         #self.assertAlmostEqual(syck.load('685.230_15e+03'), 685230.15)
         #self.assertAlmostEqual(syck.load('685_230.15'), 685230.15)
-        self.assertAlmostEqual(syck.load('685.23015e+03'), 685230.15)
-        self.assertAlmostEqual(syck.load('685230.15'), 685230.15)
-        self.assertAlmostEqual(syck.load('190:20:30.15'), 685230.15)
-        self.assertEqual(syck.load('-.inf'), -INF)
-        self.assertEqual(syck.load('.nan'), NAN)
+        self.assertEqual(syck.load('685.23015e+03'), 685230.15)
+        self.assertEqual(syck.load('685230.15'), 685230.15)
+        self.assertEqual(syck.load('190:20:30.15'), 685230.15)
+        self.assertEqual(repr(syck.load('-.inf')), repr(-INF))
+        self.assertEqual(repr(syck.load('.nan')), repr(NAN))
 
     def testInteger(self):
@@ -268,7 +272,4 @@
         node = syck.parse(ALIASES)
         values = node.value.values()
-        print values
-        print id(values[0])
-        print id(values[1])
         self.assert_(values[0] is values[1])
 
@@ -277,2 +278,11 @@
         self.assert_(document['foo'] is document['bar'])
 
+class TestMutableKey(unittest.TestCase):
+
+    def testMutableKey(self):
+        document = syck.load(MUTABLE_KEY)
+        self.assertEqual(type(document), list)
+        self.assertEqual(len(document), 1)
+        self.assertEqual(type(document[0]), tuple)
+        self.assertEqual(len(document[0]), 2)
+        self.assertEqual(document[0][0], document[0][1])
Index: /trunk/tests/test_pickle.py
===================================================================
--- /trunk/tests/test_pickle.py	(revision 25)
+++ /trunk/tests/test_pickle.py	(revision 25)
@@ -0,0 +1,365 @@
+# coding: utf-8
+
+import unittest
+
+import syck
+
+SIMPLE = """
+- !python/none ~
+- !python/bool True
+- !python/bool False
+- !python/int 123
+- !python/long 12345678901234567890
+- !python/float 123.456
+- !python/float 123.4560000001
+- !python/str foo bar
+- !python/unicode FOO Ð€Ð£ bar Ð±Ð°Ñ
+- !python/list [1, 2, 3]
+- !python/tuple [foo, bar]
+- !python/dict {foo: bar}
+""", [
+    None,
+    True,
+    False,
+    123,
+    12345678901234567890L,
+    123.456,
+    123.4560000001,
+    'foo bar',
+    unicode('FOO Ð€Ð£ bar Ð±Ð°Ñ', 'utf-8'),
+    [1, 2, 3],
+    ('foo', 'bar'),
+    {'foo': 'bar'},
+]
+
+class AClass:
+    pass
+
+class ASubclass(AClass):
+    pass
+
+class AType(object):
+    pass
+
+class ASubtype(AType):
+    pass
+
+def a_function(*args, **kwds):
+    pass
+
+NAMES = """
+- !python/name:unittest.TestCase
+- !python/name:test_pickle.AClass
+- !python/name:test_pickle.ASubclass
+- !python/name:test_pickle.AType
+- !python/name:test_pickle.ASubtype
+- !python/name:test_pickle.a_function
+- !python/name:list
+- !python/name:__builtin__.dict
+- !python/name:file
+- !python/name:map
+- !python/name:type
+- !python/name:object
+""", [
+    unittest.TestCase,
+    AClass,
+    ASubclass,
+    AType,
+    ASubtype,
+    a_function,
+    list,
+    dict,
+    file,
+    map,
+    type,
+    object,
+]
+
+class AnObject(object):
+
+    def __new__(cls, foo=None, bar=None, baz=None):
+        self = object.__new__(cls)
+        self.foo = foo
+        self.bar = bar
+        self.baz = baz
+        return self
+
+    def __cmp__(self, other):
+        return cmp((type(self), self.foo, self.bar, self.baz),
+                (type(other), other.foo, other.bar, other.baz))
+
+    def __eq__(self, other):
+        return type(self) is type(other) and    \
+                (self.foo, self.bar, self.baz) == (other.foo, other.bar, other.baz)
+
+class AnInstance:
+
+    def __init__(self, foo=None, bar=None, baz=None):
+        self.foo = foo
+        self.bar = bar
+        self.baz = baz
+
+    def __cmp__(self, other):
+        return cmp((type(self), self.foo, self.bar, self.baz),
+                (type(other), other.foo, other.bar, other.baz))
+
+    def __eq__(self, other):
+        return type(self) is type(other) and    \
+                (self.foo, self.bar, self.baz) == (other.foo, other.bar, other.baz)
+
+class AState(AnInstance):
+
+    def __getstate__(self):
+        return {
+            '_foo': self.foo,
+            '_bar': self.bar,
+            '_baz': self.baz,
+        }
+
+    def __setstate__(self, state):
+        self.foo = state['_foo']
+        self.bar = state['_bar']
+        self.baz = state['_baz']
+
+OBJECTS = """
+- !python/object:test_pickle.AnObject
+    foo: 1
+    bar: two
+    baz: [3, 4, 5]
+- !python/object:test_pickle.AnInstance
+    foo: 1
+    bar: two
+    baz: [3, 4, 5]
+- !python/object:test_pickle.AState
+    _foo: 1
+    _bar: two
+    _baz: [3, 4, 5]
+""", [
+    AnObject(foo=1, bar='two', baz=[3,4,5]),
+    AnInstance(foo=1, bar='two', baz=[3,4,5]),
+    AState(foo=1, bar='two', baz=[3,4,5]),
+]
+
+class ACustomState(AnInstance):
+
+    def __getstate__(self):
+        return (self.foo, self.bar, self.baz)
+
+    def __setstate__(self, state):
+        self.foo, self.bar, self.baz = state
+
+class InitArgs(AnInstance):
+
+    def __getinitargs__(self):
+        return (self.foo, self.bar, self.baz)
+
+class InitArgsWithState(AnInstance):
+
+    def __getinitargs__(self):
+        return (self.foo, self.bar)
+
+    def __getstate__(self):
+        return self.baz[:]
+
+    def __setstate__(self, state):
+        self.baz = state[:]
+
+class NewArgs(AnObject):
+
+    def __getnewargs__(self):
+        return (self.foo, self.bar, self.baz)
+
+    def __getstate__(self):
+        return None
+
+class NewArgsWithState(AnObject):
+
+    def __getnewargs__(self):
+        return (self.foo, self.bar)
+
+    def __getstate__(self):
+        return self.baz[:]
+
+    def __setstate__(self, state):
+        self.baz = state[:]
+
+NEWS = """
+- !python/new:test_pickle.ACustomState
+    state: !python/tuple [1, two, [3, 4, 5]]
+- !python/new:test_pickle.InitArgs [1, two, [3, 4, 5]]
+- !python/new:test_pickle.InitArgs
+    args: [1]
+    kwds: {bar: two, baz: [3, 4, 5]}
+- !python/new:test_pickle.InitArgsWithState
+    args: [1, two, [3, 4, 5]]
+    state: [3, 4, 5]
+- !python/new:test_pickle.NewArgs [1, two, [3, 4, 5]]
+- !python/new:test_pickle.NewArgs
+    args: [1]
+    kwds: {bar: two, baz: [3, 4, 5]}
+- !python/new:test_pickle.NewArgsWithState
+    args: [1, two, [3, 4, 5]]
+    state: [3, 4, 5]
+""", [
+    ACustomState(foo=1, bar='two', baz=[3,4,5]),
+    InitArgs(foo=1, bar='two', baz=[3,4,5]),
+    InitArgs(foo=1, bar='two', baz=[3,4,5]),
+    InitArgsWithState(foo=1, bar='two', baz=[3,4,5]),
+    NewArgs(foo=1, bar='two', baz=[3,4,5]),
+    NewArgs(foo=1, bar='two', baz=[3,4,5]),
+    NewArgsWithState(foo=1, bar='two', baz=[3,4,5]),
+]
+
+class Reduce(AnObject):
+
+    def __reduce__(self):
+        return self.__class__, (self.foo, self.bar, self.baz)
+
+class ReduceWithState(AnObject):
+
+    def __reduce__(self):
+        return self.__class__, (self.foo, self.bar), self.baz[:]
+
+    def __setstate__(self, state):
+        self.baz = state[:]
+
+APPLIES = """
+- !python/apply:test_pickle.Reduce [1, two, [3, 4, 5]]
+- !python/apply:test_pickle.Reduce
+    args: [1]
+    kwds: {bar: two, baz: [3, 4, 5]}
+- !python/apply:test_pickle.ReduceWithState
+    args: [1, two]
+    state: [3, 4, 5]
+""", [
+    Reduce(foo=1, bar='two', baz=[3,4,5]),
+    Reduce(foo=1, bar='two', baz=[3,4,5]),
+    ReduceWithState(foo=1, bar='two', baz=[3,4,5]),
+]
+
+class TestPickle(unittest.TestCase):
+
+    def testSimple(self):
+        self._testPickle(SIMPLE)
+
+    def testNames(self):
+        self._testPickle(NAMES)
+
+    def testObjects(self):
+        self._testPickle(OBJECTS)
+
+    def testNews(self):
+        self._testPickle(NEWS)
+
+    def testApplies(self):
+        self._testPickle(APPLIES)
+
+    def _testPickle(self, (source, object)):
+        for left, right in zip(syck.load(source), object):
+            self.assertEqual(left, right)
+        for left, right in zip(syck.load(syck.dump(object)), object):
+            self.assertEqual(left, right)
+
+class MyPrivateType(AnObject):
+    pass
+
+class MyPublicType(AnObject):
+    pass
+
+class MyMapping(AnObject):
+
+    def yaml_construct(cls, node):
+        return cls(**node.value)
+    yaml_construct = classmethod(yaml_construct)
+
+    def yaml_represent(self, node):
+        return syck.Map(self.__dict__.copy(), inline=True)
+
+EXTENSIONS = """
+- !!MyPrivateType [1, 2, 3]
+- !domain.tld,2005/MyPublicType [1, 2, 3]
+- !domain.tld,2005/AnotherPublicType [1, 2, 3]
+- {foo: 1, bar: 2, baz: 3}
+""", [
+    MyPrivateType(1,2,3),
+    MyPublicType(1,2,3),
+    MyPublicType(1,2,3),
+    MyMapping(1,2,3),
+]
+
+NODES = """
+- foo
+- [bar, baz]
+"""
+
+BUGGY_NODES = """
+- foo
+- { bar: baz }
+"""
+
+class ExLoader(syck.Loader):
+
+    def find_constructor(self, node):
+        if node.kind == 'map':
+            return MyMapping.yaml_construct
+        return super(ExLoader, self).find_constructor(node)
+
+    def make_mapping(self, node):
+        return MyMapping(**node.value)
+
+    def construct_private_MyPrivateType(self, node):
+        return MyPrivateType(*node.value)
+
+    def construct_domain_tld_2005(self, node):
+        return MyPublicType(*node.value)
+
+class ExDumper(syck.Dumper):
+
+    def find_representer(self, object):
+        if isinstance(object, MyMapping):
+            return object.yaml_represent
+        return super(ExDumper, self).find_representer(object)
+
+    def represent_test_pickle_MyPrivateType(self, object):
+        return syck.Seq([object.foo, object.bar, object.baz],
+                tag="x-private:MyPrivateType", inline=True)
+
+    def represent_test_pickle_MyPublicType(self, object):
+        return syck.Seq([object.foo, object.bar, object.baz],
+                tag="tag:domain.tld,2005:APublicType", inline=True)
+
+class TestExtensions(unittest.TestCase):
+
+    def testExtensions(self):
+        source = EXTENSIONS[0]
+        object = EXTENSIONS[1]
+        object2 = syck.load(source, Loader=ExLoader)
+        for left, right in zip(object, object2):
+            self.assertEqual(left, right)
+        source2 = syck.dump(object2, Dumper=ExDumper)
+        object3 = syck.load(source2, Loader=ExLoader)
+        for left, right in zip(object, object3):
+            self.assertEqual(left, right)
+
+class TestNodesReduce(unittest.TestCase):
+
+    def testNodesReduce(self):
+        object = syck.load(NODES)
+        nodes = syck.parse(NODES)
+        output = syck.dump(nodes)
+        print output
+        nodes2 = syck.load(output)
+        output2 = syck.emit(nodes2)
+        object2 = syck.load(output2)
+        self.assertEqual(object, object2)
+
+    def testBuggyNodesReduce(self):
+        object = syck.load(BUGGY_NODES)
+        nodes = syck.parse(BUGGY_NODES)
+        output = syck.dump(nodes)
+        print output
+        nodes2 = syck.load(output)
+        output2 = syck.emit(nodes2)
+        object2 = syck.load(output2)
+        self.assertEqual(object, object2)
+
Index: /trunk/tests/test_emitter.py
===================================================================
--- /trunk/tests/test_emitter.py	(revision 17)
+++ /trunk/tests/test_emitter.py	(revision 23)
@@ -163,16 +163,16 @@
         emitter = _syck.Emitter(StringIO.StringIO(), headless=False)
         emitter.emit(CYCLE)
-        self.assert_('---' in emitter.output.getvalue())
+        self.assert_(emitter.output.getvalue().find('---') != -1)
         emitter = _syck.Emitter(StringIO.StringIO(), headless=True)
         emitter.emit(CYCLE)
-        self.assert_('---' not in emitter.output.getvalue())
+        self.assert_(emitter.output.getvalue().find('---') == -1)
 
     def testUseHeader(self):
         emitter = _syck.Emitter(StringIO.StringIO(), headless=True)
         emitter.emit(EXAMPLE)
-        self.assert_('---' not in emitter.output.getvalue())
+        self.assert_(emitter.output.getvalue().find('---') == -1)
         emitter = _syck.Emitter(StringIO.StringIO(), use_header=True)
         emitter.emit(EXAMPLE)
-        self.assert_('---' in emitter.output.getvalue())
+        self.assert_(emitter.output.getvalue().find('---') != -1)
 
     def testExplicitTyping(self):
@@ -235,5 +235,6 @@
         document = parser.parse()
         self.assertEqual(len(document.value), len(TAGS))
-        for index, node in enumerate(document.value):
+        for index in range(len(document.value)):
+            node = document.value[index]
             self.assertEqual(node.tag, TAGS[index])
 
@@ -282,11 +283,11 @@
 
 
-class TestSyckBugWithTrailingSpace(unittest.TestCase):
-
-    def testSyckBugWithTrailingSpace(self):
-        emitter = _syck.Emitter(StringIO.StringIO())
-        emitter.emit(_syck.Scalar('foo ', tag="tag:yaml.org,2002:str"))
-        parser = _syck.Parser(emitter.output.getvalue())
-        self.assertEqual(parser.parse().value, 'foo ')
-
-
+#class TestSyckBugWithTrailingSpace(unittest.TestCase):
+#
+#    def testSyckBugWithTrailingSpace(self):
+#        emitter = _syck.Emitter(StringIO.StringIO())
+#        emitter.emit(_syck.Scalar('foo ', tag="tag:yaml.org,2002:str"))
+#        parser = _syck.Parser(emitter.output.getvalue())
+#        self.assertEqual(parser.parse().value, 'foo ')
+
+
Index: /trunk/tests/test_syck.py
===================================================================
--- /trunk/tests/test_syck.py	(revision 20)
+++ /trunk/tests/test_syck.py	(revision 21)
@@ -2,9 +2,10 @@
 import unittest
 
-#from test_node import *
+from test_node import *
 from test_parser import *
 from test_loader import *
 from test_emitter import *
 from test_dumper import *
+from test_pickle import *
 
 def main(module='__main__'):
Index: /trunk/tests/test_dumper.py
===================================================================
--- /trunk/tests/test_dumper.py	(revision 20)
+++ /trunk/tests/test_dumper.py	(revision 23)
@@ -1,7 +1,29 @@
+
+from __future__ import generators
 
 import unittest
 import syck
-import StringIO, datetime, sets
+import StringIO
 import test_emitter
+
+try:
+    import datetime
+except:
+    class _datetime:
+        def datetime(self, *args):
+            return args
+    datetime = _datetime()
+
+try:
+    import sets
+except:
+    class _sets:
+        def Set(self, items):
+            set = {}
+            for items in items:
+                set[items] = None
+            return set
+    sets = _sets()
+
 
 EXAMPLE = {
@@ -136,5 +158,8 @@
         for a, b in zip(scalars, SCALARS):
             self.assertEqual(type(a), type(b))
-            self.assertEqual(a, b)
+            if type(a) is float:
+                self.assertEqual(repr(a), repr(b))
+            else:
+                self.assertEqual(a, b)
 
 class TestCollectionTypes(unittest.TestCase):
Index: /trunk/tests/test_parser.py
===================================================================
--- /trunk/tests/test_parser.py	(revision 20)
+++ /trunk/tests/test_parser.py	(revision 23)
@@ -227,5 +227,6 @@
             self.assertEqual(type(structure), list)
             self.assertEqual(len(node.value), len(structure))
-            for i, item in enumerate(node.value):
+            for i in range(len(node.value)):
+                item = node.value[i]
                 self.assertEqualStructure(item, structure[i])
         elif node.kind == 'map':
Index: /trunk/lib/syck/loaders.py
===================================================================
--- /trunk/lib/syck/loaders.py	(revision 18)
+++ /trunk/lib/syck/loaders.py	(revision 25)
@@ -1,2 +1,6 @@
+"""
+syck.loaders is a high-level wrapper for the Syck YAML parser.
+Do not use it directly, use the module 'syck' instead.
+"""
 
 # Python 2.2 compatibility
@@ -21,5 +25,5 @@
 import _syck
 
-import re
+import sys, re
 
 __all__ = ['GenericLoader', 'Loader',
@@ -27,6 +31,13 @@
 
 class GenericLoader(_syck.Parser):
+    """
+    GenericLoader constructs primitive Python objects from YAML documents.
+    """
 
     def load(self):
+        """
+        Loads a YAML document from the source and return a native Python
+        object. On EOF, returns None and set the eof attribute on.
+        """
         node = self.parse()
         if self.eof:
@@ -50,8 +61,8 @@
                 value_object = self._convert(node.value[key_node],
                         node_to_object)
-                if key_object in value:
-                    value = None
-                    break
                 try:
+                    if key_object in value:
+                        value = None
+                        break
                     value[key_object] = value_object
                 except TypeError:
@@ -61,5 +72,5 @@
                 value = []
                 for key_node in node.value:
-                    key_object = self_convert(key_node, node_to_object)
+                    key_object = self._convert(key_node, node_to_object)
                     value_object = self._convert(node.value[key_node],
                             node_to_object)
@@ -71,18 +82,23 @@
 
     def construct(self, node):
+        """Constructs a Python object by the given node."""
         return node.value
 
 class Merge:
+    """Represents the merge key '<<'."""
     pass
 
 class Default:
+    """Represents the default key '='."""
     pass
 
 class Loader(GenericLoader):
+    """
+    Loader constructs native Python objects from YAML documents.
+    """
 
     inf_value = 1e300000
     nan_value = inf_value/inf_value
 
-    ymd_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)')
     timestamp_expr = re.compile(r'(?P<year>\d\d\d\d)-(?P<month>\d\d)-(?P<day>\d\d)'
             r'(?:'
@@ -95,67 +111,74 @@
     default_key = Default()
 
-    def __init__(self, *args, **kwds):
-        super(Loader, self).__init__(*args, **kwds)
-        self.tags = {}
-        self.add_builtin_types()
-
-    def add_builtin_types(self):
-        self.add_builtin_type('null', lambda node: None)
-        self.add_builtin_type('bool#yes', lambda node: True)
-        self.add_builtin_type('bool#no', lambda node: False)
-        self.add_builtin_type('float#fix', lambda node: float(node.value))
-        self.add_builtin_type('float#exp', lambda node: float(node.value))
-        self.add_builtin_type('float#base60', 'construct_base60_float')
-        self.add_builtin_type('float#inf', lambda node: self.inf_value)
-        self.add_builtin_type('float#neginf', lambda node: -self.inf_value)
-        self.add_builtin_type('float#nan', lambda node: self.nan_value)
-        self.add_builtin_type('int', lambda node: int(node.value))
-        self.add_builtin_type('int#hex', lambda node: int(node.value, 16))
-        self.add_builtin_type('int#oct', lambda node: int(node.value, 8))
-        self.add_builtin_type('int#base60', 'construct_base60_int')
-        self.add_builtin_type('binary', lambda node: node.value.decode('base64'))
-        self.add_builtin_type('timestamp#ymd', 'construct_timestamp')
-        self.add_builtin_type('timestamp#iso8601', 'construct_timestamp')
-        self.add_builtin_type('timestamp#spaced', 'construct_timestamp')
-        self.add_builtin_type('timestamp', 'construct_timestamp')
-        self.add_builtin_type('merge', 'construct_merge')
-        self.add_builtin_type('default', 'construct_default')
-        self.add_builtin_type('omap', 'construct_omap')
-        self.add_builtin_type('pairs', 'construct_pairs')
-        self.add_builtin_type('set', 'construct_set')
-
-    def add_type(self, type_tag, constuctor):
-        self.tags[type_tag] = constructor
-
-    def add_domain_type(self, domain, type_tag, constructor):
-        self.tags['tag:%s:%s' % (domain, type_tag)] = constructor
-
-    def add_builtin_type(self, type_tag, constructor):
-        self.tags['tag:yaml.org,2002:'+type_tag] = constructor
-
-    def add_python_type(self, type_tag, constructor):
-        self.tags['tag:python.yaml.org,2002:'+type_tag] = constructor
-
-    def add_private_type(self, type_tag, constructor):
-        self.tags['x-private:'+type_tag] = constructor
+    non_ascii = []
+    for i in range(256):
+        ch = chr(i)
+        if ch.isalnum():
+            non_ascii.append(ch)
+        else:
+            non_ascii.append('_')
+    non_ascii = ''.join(non_ascii)
+
+    python_bools = {'True': True, 'False': False}
+
+    class python_class:
+        pass
+
+    def find_constructor(self, node):
+        """
+        Returns the contructor for generating a Python object for the given
+        node.
+
+        The node tags are mapped to constructors by the following rule:
+
+        Tag                             Constructor
+        ---                             -----------
+        tag:yaml.org,2002:type          construct_type
+        tag:python.yaml.org,2002:type   construct_python_type
+        x-private:type                  construct_private_type
+        tag:domain.tld,2002:type        construct_domain_tld_2002_type
+
+        See the method code for more details.
+        """
+        parts = []
+        if node.tag:
+            parts = node.tag.split(':')
+        if parts:
+            if parts[0] == 'tag':
+                parts.pop(0)
+                if parts:
+                    if parts[0] == 'yaml.org,2002':
+                        parts.pop(0)
+                    elif parts[0] == 'python.yaml.org,2002':
+                        parts[0] = 'python'
+            elif parts[0] == 'x-private':
+                parts[0] = 'private'
+        parts = [part.translate(self.non_ascii) for part in parts]
+        while parts:
+            method = 'construct_'+'_'.join(parts)
+            if hasattr(self, method):
+                return getattr(self, method)
+            parts.pop()
 
     def construct(self, node):
+        """Constructs a Python object by the given node."""
         if node.kind == 'map' and self.merge_key in node.value:
             self.merge_maps(node)
-        if node.tag in self.tags:
-            constructor = self.tags[node.tag]
-            if isinstance(constructor, str):
-                constructor = getattr(self, constructor)
+        constructor = self.find_constructor(node)
+        if constructor:
             return constructor(node)
         else:
             return node.value
 
-    def construct_base60_float(self, node):
-        return self.construct_base60(float, node)
-
-    def construct_base60_int(self, node):
-        return self.construct_base60(int, node)
-
-    def construct_base60(self, num_type, node):
+    def construct_null(self, node):
+        return None
+
+    def construct_bool_yes(self, node):
+        return True
+
+    def construct_bool_no(self, node):
+        return False
+
+    def construct_numeric_base60(self, num_type, node):
         digits = [num_type(part) for part in node.value.split(':')]
         digits.reverse()
@@ -166,4 +189,36 @@
             base *= 60
         return value
+
+    def construct_int(self, node):
+        return int(node.value)
+
+    def construct_int_hex(self, node):
+        return int(node.value, 16)
+
+    def construct_int_oct(self, node):
+        return int(node.value, 8)
+
+    def construct_int_base60(self, node):
+        return self.construct_numeric_base60(int, node)
+
+    def construct_float(self, node):
+        return float(node.value)
+    construct_float_fix = construct_float
+    construct_float_exp = construct_float
+
+    def construct_float_base60(self, node):
+        return self.construct_numeric_base60(float, node)
+
+    def construct_float_inf(self, node):
+        return self.inf_value
+
+    def construct_float_neginf(self, node):
+        return -self.inf_value
+
+    def construct_float_nan(self, node):
+        return self.nan_value
+
+    def construct_binary(self, node):
+        return node.value.decode('base64')
 
     def construct_timestamp(self, node):
@@ -183,4 +238,7 @@
         diff = datetime.timedelta(hours=values['zhour'], minutes=values['zminute'])
         return stamp-diff
+    construct_timestamp_ymd = construct_timestamp
+    construct_timestamp_iso8601 = construct_timestamp
+    construct_timestamp_spaced = construct_timestamp
 
     def construct_merge(self, node):
@@ -217,17 +275,116 @@
         return sets.Set(node.value)
 
-def parse(source):
+    def construct_python_none(self, node):
+        return None
+
+    def construct_python_bool(self, node):
+        return self.python_bools[node.value]
+
+    def construct_python_int(self, node):
+        return int(node.value)
+
+    def construct_python_long(self, node):
+        return long(node.value)
+
+    def construct_python_float(self, node):
+        return float(node.value)
+
+    def construct_python_str(self, node):
+        return str(node.value)
+
+    def construct_python_unicode(self, node):
+        return unicode(node.value, 'utf-8')
+
+    def construct_python_list(self, node):
+        return node.value
+
+    def construct_python_tuple(self, node):
+        return tuple(node.value)
+
+    def construct_python_dict(self, node):
+        return node.value
+
+    def find_python_object(self, node):
+        full_name = node.tag.split(':')[3]
+        parts = full_name.split('.')
+        object_name = parts.pop()
+        module_name = '.'.join(parts)
+        if not module_name:
+            module_name = '__builtin__'
+        else:
+            __import__(module_name)
+        return getattr(sys.modules[module_name], object_name)
+
+    def find_python_state(self, node):
+        if node.kind == 'seq':
+            args = node.value
+            kwds = {}
+            state = {}
+        else:
+            args = node.value.get('args', [])
+            kwds = node.value.get('kwds', {})
+            state = node.value.get('state', {})
+        return args, kwds, state
+
+    def set_python_state(self, object, state):
+        if hasattr(object, '__setstate__'):
+            object.__setstate__(state)
+        else:
+            slotstate = {}
+            if isinstance(state, tuple) and len(state) == 2:
+                state, slotstate = state
+            if hasattr(object, '__dict__'):
+                object.__dict__.update(state)
+            elif state:
+                slotstate.update(state)
+            for key, value in slotstate.items():
+                setattr(object, key, value)
+
+    def construct_python_name(self, node):
+        return self.find_python_object(node)
+
+    def construct_python_object(self, node):
+        cls = self.find_python_object(node)
+        if type(cls) is type(self.python_class):
+            if hasattr(cls, '__getnewargs__'):
+                object = cls()
+            else:
+                object = self.python_class()
+                object.__class__ = cls
+        else:
+            object = cls.__new__(cls)
+        self.set_python_state(object, node.value)
+        return object
+
+    def construct_python_new(self, node):
+        cls = self.find_python_object(node)
+        args, kwds, state = self.find_python_state(node)
+        if type(cls) is type(self.python_class):
+            object = cls(*args, **kwds)
+        else:
+            object = cls.__new__(cls, *args, **kwds)
+        self.set_python_state(object, state)
+        return object
+
+    def construct_python_apply(self, node):
+        constructor = self.find_python_object(node)
+        args, kwds, state = self.find_python_state(node)
+        object = constructor(*args, **kwds)
+        self.set_python_state(object, state)
+        return object
+
+def parse(source, Loader=Loader, **parameters):
     """Parses 'source' and returns the root of the 'Node' graph."""
-    loader = Loader(source)
+    loader = Loader(source, **parameters)
     return loader.parse()
 
-def load(source):
+def load(source, Loader=Loader, **parameters):
     """Parses 'source' and returns the root object."""
-    loader = Loader(source)
+    loader = Loader(source, **parameters)
     return loader.load()
 
-def parse_documents(source):
-    """Iterates over 'source' and yields the root node of each document."""
-    loader = Loader(source)
+def parse_documents(source, Loader=Loader, **parameters):
+    """Iterates over 'source' and yields the root 'Node' for each document."""
+    loader = Loader(source, **parameters)
     while True:
         node = loader.parse()
@@ -236,7 +393,7 @@
         yield node
 
-def load_documents(source):
-    """Iterates over 'source' and yields the root object of each document."""
-    loader = Loader(source)
+def load_documents(source, Loader=Loader, **parameters):
+    """Iterates over 'source' and yields the root object for each document."""
+    loader = Loader(source, **parameters)
     while True:
         object = loader.load()
Index: /trunk/lib/syck/__init__.py
===================================================================
--- /trunk/lib/syck/__init__.py	(revision 19)
+++ /trunk/lib/syck/__init__.py	(revision 25)
@@ -1,2 +1,102 @@
+"""
+YAML is a data serialization format designed for human readability and
+interaction with scripting languages.
+
+Syck is an extension for reading and writing YAML in scripting languages.
+
+PySyck provides Python bindings for Syck YAML parser and emitter.
+
+To start working with PySyck, import the package 'syck':
+>>> from syck import *
+
+To parse a YAML document into a Python object, use the function 'load()':
+>>> load('''
+... - Mark McGwire
+... - Sammy Sosa
+... - Ken Griffey
+... ''')
+['Mark McGwire', 'Sammy Sosa', 'Ken Griffey']
+
+To emit a Python object into a YAML document, use the function 'dump()':
+>>> print dump(['Mark McGwire', 'Sammy Sosa', 'Ken Griffey'])
+---
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
+
+You may get access to the YAML parser tree using the function 'parse()':
+>>> root_node = parse('''
+... - Mark McGwire
+... - Sammy Sosa
+... - Ken Griffey
+... ''')
+>>> root_node
+<_syck.Seq object at 0xb7a1f874>
+>>> root_node.kind
+'seq'
+>>> root_node.value
+[<_syck.Scalar object at 0xb7a1e5fc>, <_syck.Scalar object at 0xb7a1e65c>, <_syck.Scalar object at 0xb7a1e6bc>]
+
+You may now use the function 'emit()' to obtain the YAML document again:
+>>> print emit(root_node)
+---
+- Mark McGwire
+- Sammy Sosa
+- Ken Griffey
+
+What do you get if you apply the function 'dump()' to root_node? Let's try it:
+>>> print dump(root_node)
+--- !python/object:_syck.Seq
+value:
+- !python/object:_syck.Scalar
+  value: Mark McGwire
+  tag: tag:yaml.org,2002:str
+- !python/object:_syck.Scalar
+  value: Sammy Sosa
+  tag: tag:yaml.org,2002:str
+- !python/object:_syck.Scalar
+  value: Ken Griffey
+  tag: tag:yaml.org,2002:str
+
+As you can see, PySyck allow you to represent complex Python objects.
+
+You can also dump the generated YAML output into any file-like object:
+>>> import os
+>>> stream = os.tmpfile()
+>>> object = ['foo', 'bar', ['baz']]
+>>> dump(object, stream)
+>>> stream.seek(0)
+>>> print stream.read()
+---
+- foo
+- bar
+- - baz
+
+To load several documents from a single YAML stream, use the function
+'load_documents()':
+>>> source = '''
+... ---
+... american:
+...   - Boston Red Sox
+...   - Detroit Tigers
+...   - New York Yankees
+... national:
+...   - New York Mets
+...   - Chicago Cubs
+...   - Atlanta Braves
+... ---
+... - [name        , hr, avg  ]
+... - [Mark McGwire, 65, 0.278]
+... - [Sammy Sosa  , 63, 0.288]
+... '''
+>>> for document in load_documents(source):
+...     print document
+...
+{'national': ['New York Mets', 'Chicago Cubs', 'Atlanta Braves'], 'american': ['Boston Red Sox', 'Detroit Tigers', 'New York Yankees']}
+[['name', 'hr', 'avg'], ['Mark McGwire', 65, 0.27800000000000002], ['Sammy Sosa', 63, 0.28799999999999998]]
+
+See the source code for more details.
+"""
+
 
 from _syck import *
Index: /trunk/lib/syck/dumpers.py
===================================================================
--- /trunk/lib/syck/dumpers.py	(revision 20)
+++ /trunk/lib/syck/dumpers.py	(revision 25)
@@ -1,2 +1,6 @@
+"""
+syck.dumpers is a high-level wrapper for the Syck YAML emitter.
+Do not use it directly, use the module 'syck' instead.
+"""
 
 import _syck
@@ -10,28 +14,34 @@
     'emit', 'dump', 'emit_documents', 'dump_documents']
 
-INF = 1e300000
-NEGINF = -INF
-NAN = INF/INF
-
-
 class GenericDumper(_syck.Emitter):
+    """
+    GenericDumper dumps native Python objects into YAML documents.
+    """
 
     def dump(self, object):
+        """Dumps the given Python object as a YAML document."""
         self.emit(self._convert(object, {}))
 
     def _convert(self, object, object_to_node):
         if id(object) in object_to_node and self.allow_aliases(object):
-            return object_to_node[id(object)]
+            return object_to_node[id(object)][1]
         node = self.represent(object)
-        object_to_node[id(object)] = node
+        object_to_node[id(object)] = object, node
         if node.kind == 'seq':
-            for index, item in enumerate(node.value):
+            for index in range(len(node.value)):
+                item = node.value[index]
                 node.value[index] = self._convert(item, object_to_node)
         elif node.kind == 'map':
-            for key in node.value.keys():
-                value = node.value[key]
-                del node.value[key]
-                node.value[self._convert(key, object_to_node)] =    \
-                        self._convert(value, object_to_node)
+            if isinstance(node.value, dict):
+                for key in node.value.keys():
+                    value = node.value[key]
+                    del node.value[key]
+                    node.value[self._convert(key, object_to_node)] =    \
+                            self._convert(value, object_to_node)
+            elif isinstance(node.value, list):
+                for index in range(len(node.value)):
+                    key, value = node.value[index]
+                    node.value[index] = (self._convert(key, object_to_node),
+                            self._convert(value, object_to_node))
 #        # Workaround against a Syck bug:
 #        if node.kind == 'scalar' and node.style not in ['1quote', '2quote'] \
@@ -41,4 +51,5 @@
 
     def represent(self, object):
+        """Represents the given Python object as a 'Node'."""
         if isinstance(object, dict):
             return _syck.Map(object.copy(), tag="tag:yaml.org,2002:map")
@@ -49,12 +60,26 @@
 
     def allow_aliases(self, object):
+        """Checks whether the given object can be aliased."""
         return True
 
 class Dumper(GenericDumper):
-
-    def __init__(self, *args, **kwds):
-        super(Dumper, self).__init__(*args, **kwds)
-
-    def represent(self, object):
+    """
+    Dumper dumps native Python objects into YAML documents.
+    """
+
+    INF = 1e300000
+    inf_value = repr(INF)
+    neginf_value = repr(-INF)
+    nan_value = repr(INF/INF)
+
+    def find_representer(self, object):
+        """
+        For the given object, find a method that can represent it as a 'Node'
+        object.
+
+        If the type of the object has the form 'package.module.type',
+        find_representer() returns the method 'represent_package_module_type'.
+        If this method does not exist, it checks the base types.
+        """
         for object_type in type(object).__mro__:
             if object_type.__module__ == '__builtin__':
@@ -62,8 +87,15 @@
             else:
                 name = '%s.%s' % (object_type.__module__, object_type.__name__)
-            method = 'represent_%s' % name.replace('.', '_')
+            method = 'represent_' + name.replace('.', '_')
             if hasattr(self, method):
-                return getattr(self, method)(object)
-        return super(Dumper, self).represent(object)
+                return getattr(self, method)
+
+    def represent(self, object):
+        """Represents the given Python object as a 'Node'."""
+        representer = self.find_representer(object)
+        if representer:
+            return representer(object)
+        else:
+            return super(Dumper, self).represent(object)
 
     def represent_object(self, object):
@@ -88,14 +120,11 @@
         return _syck.Scalar(repr(object), tag="tag:yaml.org,2002:int")
 
-    def represent_int(self, object):
-        return _syck.Scalar(repr(object), tag="tag:yaml.org,2002:int")
-
     def represent_float(self, object):
         value = repr(object)
-        if value == repr(INF):
+        if value == self.inf_value:
             value = '.inf'
-        elif value == repr(NEGINF):
+        elif value == self.neginf_value:
             value = '-.inf'
-        elif value == repr(NAN):
+        elif value == self.nan_value:
             value = '.nan'
         return _syck.Scalar(value, tag="tag:yaml.org,2002:float")
@@ -103,16 +132,126 @@
     def represent_sets_Set(self, object):
         return _syck.Seq(list(object), tag="tag:yaml.org,2002:set")
+    represent_set = represent_sets_Set
 
     def represent_datetime_datetime(self, object):
         return _syck.Scalar(object.isoformat(), tag="tag:yaml.org,2002:timestamp")
 
+    def represent_long(self, object):
+        return _syck.Scalar(repr(object), tag="tag:python.yaml.org,2002:long")
+
+    def represent_unicode(self, object):
+        return _syck.Scalar(object.encode('utf-8'), tag="tag:python.yaml.org,2002:unicode")
+
+    def represent_tuple(self, object):
+        return _syck.Seq(list(object), tag="tag:python.yaml.org,2002:tuple")
+
+    def represent_type(self, object):
+        name = '%s.%s' % (object.__module__, object.__name__)
+        return _syck.Scalar('', tag="tag:python.yaml.org,2002:name:"+name)
+    represent_classobj = represent_type
+    represent_class = represent_type
+    # TODO: Python 2.2 does not provide the module name of a function
+    represent_function = represent_type
+    represent_builtin_function_or_method = represent_type
+
+    def represent_instance(self, object):
+        cls = object.__class__
+        class_name = '%s.%s' % (cls.__module__, cls.__name__)
+        args = ()
+        state = {}
+        if hasattr(object, '__getinitargs__'):
+            args = object.__getinitargs__()
+        if hasattr(object, '__getstate__'):
+            state = object.__getstate__()
+        elif not hasattr(object, '__getinitargs__'):
+            state = object.__dict__.copy()
+        if not args and isinstance(state, dict):
+            return _syck.Map(state.copy(),
+                    tag="tag:python.yaml.org,2002:object:"+class_name)
+        value = {}
+        if args:
+            value['args'] = list(args)
+        if state or not isinstance(state, dict):
+            value['state'] = state
+        return _syck.Map(value,
+                tag="tag:python.yaml.org,2002:new:"+class_name)
+
+    def represent_object(self, object): # Do you understand this? I don't.
+        cls = type(object)
+        class_name = '%s.%s' % (cls.__module__, cls.__name__)
+        args = ()
+        state = {}
+        if cls.__reduce__ is type.__reduce__:
+            if hasattr(object, '__reduce_ex__'):
+                reduce = object.__reduce_ex__(2)
+                args = reduce[1][1:]
+            else:
+                reduce = object.__reduce__()
+            if len(reduce) > 2:
+                state = reduce[2]
+            if state is None:
+                state = {}
+            if not args and isinstance(state, dict):
+                return _syck.Map(state.copy(),
+                        tag="tag:python.yaml.org,2002:object:"+class_name)
+            if not state and isinstance(state, dict):
+                return _syck.Seq(list(args),
+                        tag="tag:python.yaml.org,2002:new:"+class_name)
+            value = {}
+            if args:
+                value['args'] = list(args)
+            if state or not isinstance(state, dict):
+                value['state'] = state
+            return _syck.Map(value,
+                    tag="tag:python.yaml.org,2002:new:"+class_name)
+        else:
+            reduce = object.__reduce__()
+            cls = reduce[0]
+            class_name = '%s.%s' % (cls.__module__, cls.__name__)
+            args = reduce[1]
+            state = None
+            if len(reduce) > 2:
+                state = reduce[2]
+            if state is None:
+                state = {}
+            if not state and isinstance(state, dict):
+                return _syck.Seq(list(args),
+                        tag="tag:python.yaml.org,2002:apply:"+class_name)
+            value = {}
+            if args:
+                value['args'] = list(args)
+            if state or not isinstance(state, dict):
+                value['state'] = state
+            return _syck.Map(value,
+                    tag="tag:python.yaml.org,2002:apply:"+class_name)
+
+    def represent__syck_Node(self, object):
+        object_type = type(object)
+        type_name = '%s.%s' % (object_type.__module__, object_type.__name__)
+        state = []
+        if hasattr(object_type, '__slotnames__'):
+            for name in object_type.__slotnames__:
+                value = getattr(object, name)
+                if value:
+                    state.append((name, value))
+        return _syck.Map(state,
+                tag="tag:python.yaml.org,2002:object:"+type_name)
+
     def allow_aliases(self, object):
+        """Checks whether the given object can be aliased."""
         if object is None or type(object) in [int, bool, float]:
             return False
         if type(object) is str and (not object or object.isalnum()):
             return False
+        if type(object) is tuple and not object:
+            return False
         return True
 
-def emit(node, output=None, **parameters):
+def emit(node, output=None, Dumper=Dumper, **parameters):
+    """
+    Emits the given node to the output.
+
+    If output is None, returns the produced YAML document.
+    """
     if output is None:
         dumper = Dumper(StringIO.StringIO(), **parameters)
@@ -123,5 +262,10 @@
         return dumper.output.getvalue()
 
-def dump(object, output=None, **parameters):
+def dump(object, output=None, Dumper=Dumper, **parameters):
+    """
+    Dumps the given object to the output.
+
+    If output is None, returns the produced YAML document.
+    """
     if output is None:
         dumper = Dumper(StringIO.StringIO(), **parameters)
@@ -132,5 +276,10 @@
         return dumper.output.getvalue()
 
-def emit_documents(nodes, output=None, **parameters):
+def emit_documents(nodes, output=None, Dumper=Dumper, **parameters):
+    """
+    Emits the list of nodes to the output.
+    
+    If output is None, returns the produced YAML document.
+    """
     if output is None:
         dumper = Dumper(StringIO.StringIO(), **parameters)
@@ -142,5 +291,10 @@
         return dumper.output.getvalue()
 
-def dump_documents(objects, output=None, **parameters):
+def dump_documents(objects, output=None, Dumper=Dumper, **parameters):
+    """
+    Dumps the list of objects to the output.
+    
+    If output is None, returns the produced YAML document.
+    """
     if output is None:
         dumper = Dumper(StringIO.StringIO(), **parameters)
Index: /trunk/setup.py
===================================================================
--- /trunk/setup.py	(revision 3)
+++ /trunk/setup.py	(revision 27)
@@ -1,2 +1,25 @@
+
+NAME = 'PySyck'
+VERSION = '0.55.1'
+DESCRIPTION = "Python bindings for the Syck YAML parser and emitter"
+LONG_DESCRIPTION = """\
+YAML is a data serialization format designed for human readability
+and interaction with scripting languages. Syck is an extension for
+reading and writing YAML in scripting languages. PySyck is aimed to
+update the current Python bindings for Syck."""
+AUTHOR = "Kirill Simonov"
+AUTHOR_EMAIL = 'xi@resolvent.net'
+LICENSE = "BSD"
+PLATFORMS = "Any"
+URL = "http://xitology.org/pysyck/"
+DOWNLOAD_URL = URL + "%s-%s.tar.gz" % (NAME, VERSION)
+CLASSIFIERS = [
+    "Development Status :: 3 - Alpha",
+    "Intended Audience :: Developers",
+    "License :: OSI Approved :: BSD License",
+    "Programming Language :: Python",
+    "Topic :: Software Development :: Libraries :: Python Modules",
+    "Topic :: Text Processing :: Markup",
+]
 
 from distutils.core import setup, Extension
@@ -12,6 +35,16 @@
 
 setup(
-    name='Syck',
-    version='0.55.1',
+    name=NAME,
+    version=VERSION,
+    description=DESCRIPTION,
+    long_description=LONG_DESCRIPTION,
+    author=AUTHOR,
+    author_email=AUTHOR_EMAIL,
+    license=LICENSE,
+    platforms=PLATFORMS,
+    url=URL,
+    download_url=DOWNLOAD_URL,
+    classifiers=CLASSIFIERS,
+
     package_dir={'': 'lib'},
     packages=['syck'],
@@ -23,18 +56,4 @@
         ),
     ],
-    description="Python bindings for the Syck YAML parser",
-    author="Kirill Simonov",
-    author_email="xi@resolvent.net",
-    license="BSD",
-    url="http://xitology.org/python-syck/",
-    download_url="http://xitology.org/*FIXME*",
-    classifiers=[
-        "Development Status :: 2 - Pre-Alpha",
-        "Intended Audience :: Developers",
-        "License :: OSI Approved :: BSD License",
-        "Programming Language :: Python",
-        "Topic :: Software Development :: Libraries :: Python Modules",
-        "Topic :: Text Processing :: Markup",
-    ],
 )
 
Index: /trunk/MANIFEST.in
===================================================================
--- /trunk/MANIFEST.in	(revision 27)
+++ /trunk/MANIFEST.in	(revision 27)
@@ -0,0 +1,2 @@
+include README.txt README.html
+recursive-include tests *.py
Index: /trunk/README.txt
===================================================================
--- /trunk/README.txt	(revision 30)
+++ /trunk/README.txt	(revision 30)
@@ -0,0 +1,890 @@
+
+============================================================
+PySyck: Python bindings for the Syck YAML parser and emitter
+============================================================
+
+:Author: Kirill Simonov
+:Contact: xi@resolvent.net
+:Web site: http://xitology.org/pysyck/
+
+.. contents::
+
+
+Overview
+========
+
+YAML_ is a data serialization format designed for human readability and
+interaction with scripting languages.
+
+Syck_ is an extension for reading and writing YAML in scripting languages. Syck
+provides bindings to the Python_ programming language, but they are somewhat
+limited and leak memory.
+
+PySyck_ is aimed to update the current Python bindings for Syck. The new
+bindings provide a wrapper for the Syck emitter and give access to YAML
+representation graphs. Hopefully it will not leak memory as well.
+
+PySyck_ may be used for various tasks, in particular, as a replacement of the
+module pickle_. Please be aware that PySyck_ is a beta-quality software and
+is not ready yet for production use.
+
+.. _YAML: http://yaml.org/
+.. _Syck: http://whytheluckystiff.net/syck/
+.. _Python: http://python.org/
+.. _PySyck: http://xitology.org/pysyck/
+.. _pickle: http://docs.python.org/lib/module-pickle.html
+
+Requirements
+============
+
+PySyck requires Syck 0.55 or higher and Python 2.3 or higher. Python 2.2 is
+supported to some extent.
+
+
+Installation
+============
+
+Please note that Syck 0.55 or higher must be installed. If you install PySyck
+from source, unpack the source tarball and type::
+
+  $ python setup.py install
+
+Windows binaries for Python 2.2, 2.3, and 2.4 are provided.
+
+
+Usage
+=====
+
+The documentation is still rough and incomplete. See `the source code`_ for
+more information.
+
+.. _the source code: http://trac.xitology.org/pysyck/browser/
+
+Quick Example
+-------------
+
+::
+
+  >>> from syck import *
+  >>> print load("""
+  ... - foo
+  ... - bar
+  ... - baz
+  ... """)
+  ['foo', 'bar', 'baz']
+  >>> print dump(['foo', 'bar', 'baz'])
+  ---
+  - foo
+  - bar
+  - baz
+
+YAML syntax
+-----------
+
+We do not describe the YAML syntax here. Please check http://yaml.org/ for the
+reference.
+
+In addition to the tags defined in `the YAML types repository`_, PySyck understands
+the following Python-specific tags:
+
+* ``tag:python.yaml.org,2002:none``,
+* ``tag:python.yaml.org,2002:bool``,
+* ``tag:python.yaml.org,2002:int``,
+* ``tag:python.yaml.org,2002:float``,
+* ``tag:python.yaml.org,2002:str``,
+* ``tag:python.yaml.org,2002:unicode``,
+* ``tag:python.yaml.org,2002:list``,
+* ``tag:python.yaml.org,2002:tuple``,
+* ``tag:python.yaml.org,2002:dict``,
+* ``tag:python.yaml.org,2002:name:...``,
+* ``tag:python.yaml.org,2002:object:...``,
+* ``tag:python.yaml.org,2002:new:...``,
+* ``tag:python.yaml.org,2002:apply:...``.
+
+Most of these tags are self-explanatory. The tags ``!python/name:...``,
+``!python/object:...``, ``!python/new:...``, and ``!python/apply:...`` are used
+for constructing Python functions, classes, and objects. See the sections `Use
+Python-specific tags in YAML documents`_ and `Use Python-specific tags to
+construct Python objects`_ for some examples.
+
+.. _the YAML types repository: http://yaml.org/type/index.html
+
+Common Tasks
+------------
+
+Import the module
+~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> from syck import *
+
+or
+
+::
+
+  >>> import syck
+
+Load a document from a string
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> source = "..."
+  >>> object = load(source)
+
+Load a document from a file
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> source = file(..., 'r')
+  >>> object = load(source)
+
+Convert a Python object to a YAML document
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> object = ...
+  >>> document = dump(object)
+
+Dump a Python object to a YAML stream
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> object = ...
+  >>> output = file(..., 'w')
+  >>> dump(object, output)
+
+Format the output YAML stream
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> object = ...
+  >>> output = file(..., 'w')
+  >>> dump(object, output,
+  ...     headless=False, use_header=False, use_version=False,
+  ...     explicit_typing=True, style=None, best_width=80, indent=2)
+
+Load several documents from a YAML stream
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> source = ...
+  >>> objects = load_documents(source)
+  >>> for object in objects:
+  ...     # ...
+
+Create several documents in a YAML stream
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> objects = [...]
+  >>> output = file(..., 'w')
+  >>> dump_documents(objects, output)
+
+Construct a representation tree of a YAML document
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> source = ...
+  >>> root_node = parse(source)
+
+Convert a representation tree to a YAML document
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> scalar_node = Scalar('...', tag='tag:...',
+  ...     style='...', indent=.., width=..)
+  >>> sequence_node = Seq(list_of_nodes, tag='tag:...', inline=..)
+  >>> mapping_node = Map(dictionary_of_nodes, tag='tag:...', inline=..)
+  >>> root_node = ...
+  >>> output = file(..., 'w')
+  >>> emit(root_node, output)
+
+Use PySyck as a pickle replacement
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> object = ...
+  >>> stream = ...
+  >>> dump(object, stream)
+  
+  >>> stream = ...
+  >>> object = load(stream)
+
+Use PySyck to display the structure of a complex object
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> object = ...
+  >>> print dump(object)
+
+Use PySyck to display a YAML representation graph
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> source = ...
+  >>> node = parse(source)
+  >>> print dump(node)
+
+Use Python-specific tags in YAML documents
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  --- %YAML:1.0
+  - !python/none ''       # You may also use '!null'.
+  - !python/bool 'False'  # You may also use '!bool'.
+  - !python/int '123'     # You may also use '!int'.
+  - !python/long '1234567890'
+  - !python/float '123.456789'  # Also '!float'.
+  - !python/str 'a string'      # Also '!str'.
+  - !python/unicode 'a unicode string encoded in utf-8'
+  - !python/list [1, 2, 3]      # The same as '!seq' or no tag.
+  - !python/tuple [1, 2, 3]
+  - !python/dict { 1: foo, 2: bar } # The same as '!map' or no tag.
+
+Use Python-specific tags to construct functions or classes
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  --- %YAML:1.0
+  - !python/name:package.module.function_name ''
+  - !python/name:package.module.class_name ''
+
+Use Python-specific tags to construct Python objects
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  --- %YAML:1.0
+  - !python/object:package.module.type
+    attribute1: value1
+    attribute2: value2
+    # ...
+  - !python/new:package.module.type
+    - parameter1
+    - parameter2
+    # ...
+  - !python/new:package.module.type
+    args: [parameter1, parameter2, ...]
+    kwds: {kwd1: val1, kwd2: val2, ...}
+    state: {attr1: val1, attr2: val2, ...}
+    # ...
+  - !python/apply:package.module.function
+    - parameter1
+    - parameter2
+    # ...
+  - !python/apply:package.module.function
+    args: [parameter1, parameter2, ...]
+    kwds: {kwd1: val1, kwd2: val2, ...}
+    state: {attr1: val1, attr2: val2, ...}
+    # ...
+
+Use application specific tags
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+::
+
+  >>> class MyClass:
+  ...   # ...
+
+  >>> class MyLoader(Loader):
+  ...     def construct_private_my_tag(self, node):
+  ...         # ...
+  ...         return MyClass(...)
+
+  >>> class MyDumper(Dumper):
+  ...     def represent_MyDumper(self, object):
+  ...         # ...
+  ...         return Map(...)
+
+  >>> source = """--- !!my_tag { ... }"""
+  >>> my_instance = load(source, Loader=MyLoader)
+
+  >>> my_instance = MyClass(...)
+  >>> output = dump(my_instance, Dumper=MyDumper)
+
+Reference
+---------
+
+Functions
+~~~~~~~~~
+
+``load`` : function
+  ``load(source, Loader=Loader, **parameters)``
+
+  The function ``load()`` returns a Python object corresponding to the first
+  document in the source. If the source is empty, ``load()`` returns ``None``.
+  ``source`` must be a string or a file-like object that has the method
+  ``read(max_length)``.
+
+  By default, the function ``load()`` uses an instance of the class ``Loader``
+  for parsing. You may use another class or pass additional parameters to the
+  class constructor. See the section Parser_ for more details.
+
+  Example::
+
+    >>> load("""
+    ... - foo
+    ... - bar
+    ... - baz
+    ... """)
+    ['foo', 'bar', 'baz']
+
+``parse`` : function
+  ``parse(source, Loader=Loader, **parameters)``
+
+  The function ``parse()`` parses the source and returns a representation tree
+  of the first document. ``source`` must be a string or a file-like object
+  that has the method ``read(max_length)``.
+
+  By default, the function ``parse()`` uses an instance of the class ``Loader``
+  for parsing. You may use another class or pass additional parameters to the
+  class constructor. See the section Parser_ for more details.
+
+  Example::
+
+    >>> parse("""
+    ... - foo
+    ... - bar
+    ... - baz
+    ... """)
+    <_syck.Seq object at 0xb7a3f2fc>
+
+``load_documents`` : function
+  ``load_documents(source, Loader=Loader, **parameters)``
+
+  The function ``load_documents()`` parses the source and an iterator.  The
+  iterator produces Python objects corresponding the documents of the source
+  stream. ``source`` must be a string or a file-like object that has the method
+  ``read(max_length)``.
+
+  By default, the function ``load_documents()`` uses an instance of the class
+  ``Loader`` for parsing. You may use another class or pass additional
+  parameters to the class constructor. See the section Parser_ for more
+  details.
+
+  Example::
+
+    >>> source = """
+    ... --- >
+    ... This is the
+    ... first document.
+    ... --- >
+    ... This is the
+    ... next document.
+    ... --- >
+    ... This is the
+    ... last document.
+    ... """
+    >>> for object in load_documents(source): print object
+    ...
+    This is the first document.
+
+    This is the next document.
+
+    This is the last document.
+
+``parse_documents`` : function
+  ``parse_documents(source, Loader=Loader, **parameters)``
+
+  The function ``parse_documents()`` is similar to ``load_documents()``, but
+  produces representation graphs for all documents in the source.
+  
+``dump`` : function
+  ``dump(object, output=None, Dumper=Dumper, **parameters)``
+
+  The function ``dump()`` converts ``object`` to a representation graph
+  and write it to ``output``. ``output`` must be ``None`` or a file-like
+  object that has the method ``write(data)``. If ``output`` is ``None``,
+  ``dump()`` returns the generated document.
+
+  By default, the function ``dump()`` uses an instance of the class ``Dumper``
+  for emitting. You may use another class or pass additional parameters to the
+  class constructor. See the section Emitter_ for more details.
+
+  Example::
+
+    >>> object = ['foo', 'bar', ['baz']]
+    >>> dump(object, sys.stdout)
+    ---
+    - foo
+    - bar
+    - - baz
+    >>> print dump(object)
+    ---
+    - foo
+    - bar
+    - - baz
+
+    >>> print dump(object, use_version=True, indent=5)
+    --- %YAML:1.0
+    - foo
+    - bar
+    -    - baz
+
+``emit`` : function
+  ``emit(node, output=None, Dumper=Dumper, **parameters)``
+
+  The function ``emit()`` write the representation graph to the output stream.
+  ``output`` must be ``None`` or a file-like object that has the method
+  ``write(data)``. If ``output`` is ``None``, ``emit()`` returns the generated
+  document.
+
+  By default, the function ``emit()`` uses an instance of the class ``Dumper``
+  for emitting. You may use another class or pass additional parameters to the
+  class constructor. See the section Emitter_ for more details.
+
+  Example::
+
+    >>> foo = Scalar('a string')
+    >>> bar = Scalar('a unicode string', tag="tag:python.yaml.org,2002:unicode")
+    >>> baz = Scalar('12345', tag="tag:yaml.org,2002:int")
+    >>> seq = Seq([foo, bar, baz], tag="tag:python.taml.org,2002:tuple")
+    >>> print emit(seq, use_version=True)
+    --- %YAML:1.0 !python.taml.org,2002/tuple
+    - a string
+    - !python/unicode a unicode string
+    - 12345
+
+``dump_documents`` : function
+  ``dump_documents(objects, output=None, Dumper=Dumper, **parameters)``
+
+  The function ``dump_documents()`` takes a list of objects and converts
+  each object to a YAML document. If ``output`` is ``None``, it returns
+  the produced documents. Otherwise it writes down them to ``output``,
+  which must be a file-like object with the method ``write(data)``.
+
+  By default, the function ``dump_documents()`` uses an instance of the class
+  ``Dumper`` for emitting. You may use another class or pass additional
+  parameters to the class constructor. See the section Emitter_ for more
+  details.
+
+  Example::
+
+    >>> print dump_documents(['foo', 'bar', 'baz'])
+    --- foo
+    --- bar
+    --- baz
+
+``emit_documents`` : function
+  ``emit_documents(nodes, output=None, Dumper=Dumper, **parameters)``
+
+  The function ``emit_documents()`` is similar to ``dump_documents()``, but
+  it requires a list of representation graphs.
+
+Exceptions
+~~~~~~~~~~
+
+``error`` : exception
+  This exception is raised by the Syck parser when it detects a syntax error.
+
+  The attribute ``args`` of the exception is a triple: *message*, *row*,
+  *column*.
+
+  Example::
+
+    >>> load("""---
+    ... - foo
+    ... - '''
+    ... - bar
+    ... """)
+    Traceback (most recent call last):
+      File "<stdin>", line 1, in ?
+      File "build/lib.linux-i686-2.3/syck/loaders.py", line 384, in load
+      File "build/lib.linux-i686-2.3/syck/loaders.py", line 42, in load
+    _syck.error: ('syntax error', 4, 2)
+
+Nodes
+~~~~~
+
+The following four classes represents nodes in the YAML representation graph:
+
+``Node`` : class
+  ``Node`` is an abstract class; you cannot create an instance of the class
+  ``Node``. ``Node`` is the base class of ``Scalar``, ``Seq``, and ``Map``.
+
+``Scalar`` : subclass of ``Node``
+  ``Scalar`` represents a scalar node. Its value is a string.
+
+``Seq`` : subclass of ``Node``
+  ``Seq`` represents a sequence node. Its value is a list of nodes.
+
+``Map`` : subclass of ``Node``
+  ``Map`` represents a mapping node. Its value is a list of pairs or a
+  dictionary.
+
+All instances of ``Scalar``, ``Seq``, and ``Map`` have the following
+attributes:
+
+``kind`` : string
+  ``'scalar'``, ``'seq'``, or ``'map'``; read-only.
+
+``anchor`` : string or ``None``
+  The node anchor.
+
+``tag`` : string or ``None``
+  The node tag.
+
+``value``
+  The node value. For scalar nodes, it should be a string. For sequence nodes,
+  it should be a list. For mapping nodes, it should be a list of pairs or a
+  dictionary.
+
+``Scalar`` instances have additional attributes:
+
+``style`` : string or ``None``
+  The node style. Possible values are ``None`` (means literal or plain),
+  ``'1quote'``, ``'2quote'``, ``'fold'``, ``'literal'``, ``'plain'``.
+
+``indent`` : integer
+  The node indentation. ``0`` means the default value.
+
+``width`` : integer
+  The width of the node field. Longer scalars will be broken on several lines
+  to fit the field. ``0`` means the default value.
+
+``chomp`` : string or ``None``
+  The chomping method. Possible values are ``None`` (clip), ``'-'`` (strip),
+  ``'+'`` (keep).
+
+``Seq`` and ``Map`` instances have an additional attribute:
+
+``inline`` : boolean
+  The block/flow flag.
+
+For example, let us create a representation graph and transform it into a YAML
+stream::
+
+  >>> # Create three scalar nodes:
+  >>> foo = Scalar('foo', tag="tag:example.com,2005:foo", style='fold',
+  ...     indent=5)
+  >>> bar = Scalar('bar', style='1quote')
+  >>> baz = Scalar('baz')
+
+  >>> # Create a sequence node:
+  >>> seq = Seq([foo, bar, baz], tag="x-private:seq")
+
+  >>> # Emit it into a YAML stream:
+  >>> print emit(seq)
+  --- !!seq
+  - !example.com,2005/foo >-
+       foo
+  - 'bar'
+  - baz
+
+Now let us construct a representation graph from a YAML document::
+
+  >>> # The function 'parse' generates a representation graph:
+  >>> root = parse("""
+  ... - foo
+  ... - bar
+  ... - baz
+  ... """)
+
+  >>> # The object 'root' is a sequence node:
+  >>> root
+  <_syck.Seq object at 0xb7e124b4>
+
+  >>> # We can transform 'root' back into a YAML stream:
+  >>> print emit(root)
+  ---
+  - foo
+  - bar
+  - baz
+
+  >>> # We can also display the structure of the representation tree using a
+  >>> # clever trick:
+  >>> print dump(root)
+  --- !python/object:_syck.Seq
+  value:
+  - !python/object:_syck.Scalar
+    value: foo
+    tag: tag:yaml.org,2002:str
+  - !python/object:_syck.Scalar
+    value: bar
+    tag: tag:yaml.org,2002:str
+  - !python/object:_syck.Scalar
+    value: baz
+    tag: tag:yaml.org,2002:str
+
+Parser
+~~~~~~
+
+``Parser`` : class
+  The class ``Parser`` is a low-level wrapper of a Syck YAML parser. It can
+  generate a representation graph from a YAML stream.
+
+  The class constructor has the following arguments:
+
+  * ``Parser(source, implicit_typing=True, taguri_expansion=True)``.
+
+  The parameter ``source`` is a YAML stream. It must be a string
+  or a file-like object. If it is not a string, it should have a
+  method named ``read(max_length)`` that returns a string.
+
+  It is not recommended to change the default values of the parameters
+  ``implicit_typing`` and ``taguri_expansion``. See the Syck documentation
+  for more details about them.
+
+  The class defines a single method:
+
+  * ``Parser.parse()``.
+
+  It parses the source and returns the root node of the corresponding
+  representation graph. If the stream is finished, it returns ``None`` and
+  set the flag ``eof`` on.
+
+``GenericLoader`` : subclass of ``Parser``
+  The subclass ``GenericLoader`` defines two additional methods:
+
+  * ``GenericLoader.load()``,
+
+  * ``GenericLoader.construct(node)``.
+
+  The method ``load()`` parses the source and constructs the corresponding
+  Python object. To generate an object by a node, ``load()`` uses the
+  ``construct()`` method. The ``construct()`` method defined in
+  ``GenericLoader`` just returns the value of the node: a string, a list,
+  or a dictionary.
+
+``Loader`` : subclass of ``GenericLoader``
+  
+  ``Loader`` redefines the method
+
+  * ``Loader.construct(node)``,
+
+  defines an additional method:
+
+  * ``Loader.find_constructor(node)``,
+
+  and add many other auxiliary methods for constructing Python objects.
+
+  ``Loader.construct()`` calls ``find_constructor()`` for the given node,
+  and uses the returned constructor to generate a Python object.
+
+  ``Loader.find_constructor()`` determines the constructor of a node by
+  the following rules:
+
+  * If the node tag has the form ``tag:yaml.org,2002:type_id``, returns the
+    method ``Loader.construct_type_id``.
+
+  * If the node tag has the form ``tag:python.yaml.org,2002:type_id``, returns
+    the method ``Loader.construct_python_type_id``.
+
+  * If the node tag has the form ``x-private:type_id``, returns
+    ``Loader.construct_private_type_id``.
+
+  * If the node tag has the form ``tag:domain.tld,year:type_id``, returns
+    ``Loader.construct_domain_tld_year_type_id``.
+
+  See the source for more details.
+
+Let us show how ``Parser``, ``GenericLoader``, and ``Loader`` parse the same
+document::
+
+  >>> # The source stream includes PySyck specific tags '!python/tuple'
+  >>> # and '!python/unicode'. It also includes implicitly typed integer
+  >>> # '12345'
+  >>> source = """--- !python/tuple
+  ... - a string
+  ... - !python/unicode a unicode string
+  ... - 12345
+  ... """
+
+  >>> # 'Parser.parse()' returns the root node of the representation tree:
+  >>> p = Parser(source)
+  >>> print p.parse()
+  <_syck.Seq object at 0xb7a33f54>
+
+  >>> # 'GenericLoader.load()' returns a Python object, but ignores the tags:
+  >>> gl = GenericLoader(source)
+  >>> print gl.load()
+  ['a string', 'a unicode string', '12345']
+
+  >>> # 'Loader.load()' is aware of the tags:
+  >>> l = Loader(source)
+  >>> print l.load()
+  ('a string', u'a unicode string', 12345)
+
+Emitter
+~~~~~~~
+
+``Emitter`` : class
+  The class ``Emitter`` is a low-level wrapper of a Syck YAML emitter. It can
+  generate a YAML stream from a representation graph.
+
+  The class constructor has the following signature:
+
+  * ``Emitter(output, headless=False, use_header=False, use_version=False,
+    explicit_typing=True, style=None, best_width=80, indent=2)``.
+
+  The parameter ``output`` must be a file-like object that provides a method
+  ``write(data)``. The other parameters describe the formatting of the output
+  document.
+
+  The class defines a single method:
+
+  * ``emit(node)``.
+
+  The parameter ``node`` must be the root node of a YAML representation graph.
+  The method ``emit()`` writes the generated YAML document to the ``output``
+  stream.
+
+``GenericDumper`` : subclass of ``Emitter``
+  The subclass ``GenericDumper`` adds the following methods:
+
+  * ``GenericDumper.dump(object)``,
+
+  * ``GenericDumper.represent(object)``,
+
+  * ``GenericDumper.allow_aliases(object)``.
+
+  The method ``dump()`` converts the given object into a representation graph,
+  generates a YAML document, and writes it to the ``output`` stream. It uses
+  the method ``represent()`` to convert an object to a representation node.
+  The method ``represent()`` defined in ``GenericDumper`` generates a sequence
+  node for a list object and a mapping node for a dictionary object. Otherwise
+  it generates a scalar node with the value equal to ``str(object)``.
+
+  The Syck YAML emitter automatically detects if the same object is reffered
+  from different parts of the graph and generates aliases for it. Unfortunately
+  it does not work well with immutable Python objects such as strings, numbers,
+  and tuples. To prevent generating unnecessary aliases, the method
+  ``allow_aliases()`` is used. If ``allow_aliases()`` for a given object
+  returns ``False``, the alias will never be generated.
+
+  The ``allow_aliases()`` method defined in ``GenericDumper`` always returns
+  ``True``.
+
+``Dumper`` : subclass of ``GenericDumper``
+  The subclass ``Dumpers`` redefines the methods:
+
+  * ``Dumper.represent(object)``,
+
+  * ``Dumper.allow_aliases(object)``,
+
+  defines the method
+
+  * ``Dumper.find_representer(object)``,
+
+  and add many other auxiliary methods for representing objects as nodes.
+
+  ``Dumper.find_representer()`` finds a method that can represent the given
+  object as a node in a representation tree. ``find_representer()`` checks the
+  class of the object. If the class has the form ``package.module.type``,
+  ``find_representer()`` returns the method
+  ``Dumper.represent_package_module_type`` if it exists. If this method does
+  not exists, ``find_representer()`` consults its base class, and so on.
+
+  ``Dumper.represent()`` calls ``Dumper.find_representer()`` for the given
+  object and uses the returned method to generate a representation node.
+
+  See the source for more details.
+
+Let us show how ``Emitter``, ``GenericDumper``, and ``Dumper`` work::
+
+  >>> # For our demonstration, we define a representation tree named 'seq'
+  >>> # and a Python tuple named 'object':
+  >>> foo = Scalar('a string')
+  >>> bar = Scalar('a unicode string', tag="tag:python.yaml.org,2002:unicode")
+  >>> baz = Scalar('12345', tag="tag:yaml.org,2002:int")
+  >>> seq = Seq([foo, bar, baz], tag="tag:python.taml.org,2002:tuple")
+  >>> object = ('a string', u'a unicode string', 12345)
+
+  >>> # An 'Emitter' instance can dump a representation tree into a stream,
+  >>> # but obviously failed to dump a Python object:
+  >>> e = Emitter(sys.stdout)
+  >>> e.emit(seq)
+  --- !python.taml.org,2002/tuple
+  - a string
+  - !python/unicode a unicode string
+  - 12345
+  >>> e.emit(object)
+  Traceback (most recent call last):
+    File "<stdin>", line 1, in ?
+  TypeError: Node instance is required
+
+  >>> # A 'GenericDumper' instance dumps almost everything as a scalar:
+  >>> gd = GenericDumper(sys.stdout)
+  >>> gd.dump(seq)
+  --- <_syck.Seq object at 0xb7a3c2fc>
+  >>> gd.dump(object)
+  --- ('a string', u'a unicode string', 12345)
+
+  >>> # Finally, a 'Dumper' instance dumps a representation tree as a complex
+  >>> # Python object:
+  >>> d = Dumper(sys.stdout)
+  >>> d.dump(seq)
+  --- !python/object:_syck.Seq
+  value:
+  - !python/object:_syck.Scalar
+    value: a string
+  - !python/object:_syck.Scalar
+    value: a unicode string
+    tag: tag:python.yaml.org,2002:unicode
+  - !python/object:_syck.Scalar
+    value: "12345"
+    tag: tag:yaml.org,2002:int
+  tag: tag:python.taml.org,2002:tuple
+  >>> # It also dumps the 'object' object as expected:
+  >>> d.dump(object)
+  --- !python/tuple
+  - a string
+  - !python/unicode a unicode string
+  - 12345
+
+
+Development and Bug Reports
+===========================
+
+You may check out the PySyck_ source code from `PySyck SVN repository`_.
+
+If you find a bug in PySyck_, please file a bug report to `PySyck BTS`_. You
+may review open bugs on `the list of active tickets`_.
+
+You may use `YAML-core mailing list`_ for discussions of PySyck_.
+
+.. _PySyck SVN repository: http://svn.xitology.org/pysyck/
+.. _PySyck BTS: http://trac.xitology.org/pysyck/newticket
+.. _the list of active tickets: http://trac.xitology.org/pysyck/report/1
+.. _YAML-core mailing list: http://lists.sourceforge.net/lists/listinfo/yaml-core
+
+
+Known Bugs
+==========
+
+To many to list all of them; see `the list of active tickets`_. If you find
+another bug, please submit it via `PySyck BTS`_.
+
+
+History
+=======
+
+* PySyck-0.55.1 (2005-08-30): Initial release.
+
+
+Author and Copyright
+====================
+
+The PySyck_ module was written by `Kirill Simonov`_.
+
+PySyck_ is released under the BSD license as Syck_ itself.
+
+.. _Kirill Simonov: mailto:xi@resolvent.net
+
+..
+  vim: ft=rst:
Index: /trunk/Makefile
===================================================================
--- /trunk/Makefile	(revision 4)
+++ /trunk/Makefile	(revision 29)
@@ -1,18 +1,21 @@
 
-.PHONY: default build force install test clean
+.PHONY: default build force install test clean	\
+	dist-src dist-win dist-win-2.2 dist-win-2.3 dist-win-2.4
 
 PYTHON=/usr/bin/python
+REST2HTML=/usr/bin/rest2html --embed-stylesheet --stylesheet-path=/usr/share/python-docutils/stylesheets/default.css
 TEST=
+PARAMETERS=
 
-default: build
+default: build README.html
 
 build:
-	${PYTHON} setup.py build
+	${PYTHON} setup.py build ${PARAMETERS}
 
 force:
-	${PYTHON} setup.py build -f
+	${PYTHON} setup.py build -f ${PARAMETERS}
 
 install: build
-	${PYTHON} setup.py install
+	${PYTHON} setup.py install ${PARAMETERS}
 
 test: build
@@ -22,2 +25,28 @@
 	${PYTHON} setup.py clean -a
 
+dist-src:
+	${PYTHON} setup.py sdist --formats=zip,gztar
+
+dist-win: dist-win-2.2 dist-win-2.3 dist-win-2.4
+
+dist-win-2.2: PYTHON=/c/Python22/python
+dist-win-2.2: PARAMETERS=--compiler=mingw32
+dist-win-2.2:
+	${PYTHON} setup.py build ${PARAMETERS}
+	${PYTHON} setup.py bdist_wininst
+
+dist-win-2.3: PYTHON=/c/Python23/python
+dist-win-2.3: PARAMETERS=--compiler=mingw32
+dist-win-2.3:
+	${PYTHON} setup.py build ${PARAMETERS}
+	${PYTHON} setup.py bdist_wininst --skip-build --target-version=2.3
+
+dist-win-2.4: PYTHON=/c/Python24/python
+dist-win-2.4: PARAMETERS=--compiler=mingw32
+dist-win-2.4:
+	${PYTHON} setup.py build ${PARAMETERS}
+	${PYTHON} setup.py bdist_wininst --skip-build --target-version=2.4
+
+README.html: README.txt
+	${REST2HTML} README.txt README.html
+
Index: /trunk/sandbox/syck-parser/test_my_tags.yml
===================================================================
--- /trunk/sandbox/syck-parser/test_my_tags.yml	(revision 21)
+++ /trunk/sandbox/syck-parser/test_my_tags.yml	(revision 21)
@@ -0,0 +1,11 @@
+- !python/new:test_pickle.ACustomState
+    state: !python/tuple [1, two, [3, 4, 5]]
+- !python/new:test_pickle.InitArgs [1, two, [3, 4, 5]]
+- !python/new:test_pickle.InitArgs
+    args: 1
+    kwds: {bar: two, baz: [3, 4, 5]}
+- !python/new:test_pickle.InitArgsWithState
+    args: [1, two, [3, 4, 5]]
+    state: [3, 4, 5]
+- !python/new:test_pickle.NewArgs [1, two, [3, 4, 5]]
+
Index: /trunk/sandbox/emit-it/Makefile
===================================================================
--- /trunk/sandbox/emit-it/Makefile	(revision 11)
+++ /trunk/sandbox/emit-it/Makefile	(revision 25)
@@ -1,15 +1,16 @@
+
+ALL = emit-it complex-key-bug trailing-space-bug
 
 .PHONY: default clean
 
-default: emit-it
+default: $(ALL)
 
 clean:
-	rm -f emit-it
-	rm -f emit-it.o
+	rm -f *.o
+	rm -f $(ALL)
 
-emit-it: emit-it.o
-	gcc emit-it.o -o emit-it -lsyck -L${HOME}/lib -Wall -Wstrict-prototypes
+%.o: %.c
+	gcc -c $< -o $@ -Wall -Wstrict-prototypes -I${HOME}/include
 
-emit-it.o: emit-it.c
-	gcc -c emit-it.c -o emit-it.o -I${HOME}/include
-
+$(ALL): %: %.o
+	gcc $< -o $@ -lsyck -L${HOME}/lib -Wall -Wstrict-prototypes
Index: /trunk/sandbox/emit-it/trailing-space-bug.c
===================================================================
--- /trunk/sandbox/emit-it/trailing-space-bug.c	(revision 25)
+++ /trunk/sandbox/emit-it/trailing-space-bug.c	(revision 25)
@@ -0,0 +1,36 @@
+
+#include <stdio.h>
+#include <string.h>
+
+#include <syck.h>
+
+#define VALUE "this scalar contains traling spaces   "
+
+void output_handler(SyckEmitter *e, char *str, long len)
+{
+    fwrite(str, 1, len, stdout);
+}
+
+void emitter_handler(SyckEmitter *e, st_data_t id)
+{
+    switch (id) {
+        case 1:
+            syck_emit_scalar(e, "tag:yaml.org,2002:str", scalar_none, 0, 0, 0, VALUE, strlen(VALUE));
+            break;
+    }
+        
+}
+
+int main(int argc, char *argv[])
+{
+    SyckEmitter *e;
+
+    e = syck_new_emitter();
+    syck_emitter_handler(e, emitter_handler);
+    syck_output_handler(e, output_handler);
+    syck_emitter_mark_node(e, 1);
+    syck_emit(e, 1);
+    syck_emitter_flush(e, 0);
+    syck_free_emitter(e);
+}
+
Index: /trunk/sandbox/emit-it/complex-key-bug.c
===================================================================
--- /trunk/sandbox/emit-it/complex-key-bug.c	(revision 25)
+++ /trunk/sandbox/emit-it/complex-key-bug.c	(revision 25)
@@ -0,0 +1,54 @@
+
+#include <stdio.h>
+
+#include <syck.h>
+
+void output_handler(SyckEmitter *e, char *str, long len)
+{
+    fwrite(str, 1, len, stdout);
+}
+
+void emitter_handler(SyckEmitter *e, st_data_t id)
+{
+    switch (id) {
+
+        case 1:
+            syck_emit_map(e, NULL, map_none);
+            syck_emit_item(e, 2);
+            syck_emit_item(e, 3);
+            syck_emit_end(e);
+            break;
+
+        case 2:
+            syck_emit_map(e, "x-private:key", map_none);
+            syck_emit_item(e, 4);
+            syck_emit_item(e, 5);
+            syck_emit_end(e);
+            break;
+
+        case 3:
+        case 4:
+        case 5:
+            syck_emit_scalar(e, NULL, scalar_none, 0, 0, 0, "foo", 3);
+            break;
+    }
+        
+}
+
+int main(int argc, char *argv[])
+{
+    SyckEmitter *e;
+
+    e = syck_new_emitter();
+    syck_emitter_handler(e, emitter_handler);
+    syck_output_handler(e, output_handler);
+    syck_emitter_mark_node(e, 1);
+    syck_emitter_mark_node(e, 2);
+    syck_emitter_mark_node(e, 3);
+    syck_emitter_mark_node(e, 4);
+    syck_emitter_mark_node(e, 5);
+    syck_emit(e, 1);
+    syck_emitter_flush(e, 0);
+    syck_free_emitter(e);
+}
+
Index: /trunk/sandbox/emit-it/emit-it.c
===================================================================
--- /trunk/sandbox/emit-it/emit-it.c	(revision 17)
+++ /trunk/sandbox/emit-it/emit-it.c	(revision 25)
@@ -16,4 +16,5 @@
 {
     switch (id) {
+/*
         case 1:
             syck_emit_seq(e, "tag:domainmyseq.tld,2002:zz", seq_none);
@@ -21,6 +22,4 @@
             syck_emit_item(e, 3);
             syck_emit_item(e, 4);
-/*            syck_emit_item(e, 2);
-            syck_emit_item(e, 1);*/
             syck_emit_end(e);
             break;
@@ -34,5 +33,27 @@
             syck_emit_scalar(e, "x-private:myowntype", scalar_none, 0, 0, 0, "Ken Griffey", strlen("Ken Griffey"));
             break;
+*/
+
+        case 1:
+            syck_emit_map(e, NULL, map_none);
+            syck_emit_item(e, 2);
+            syck_emit_item(e, 3);
+            syck_emit_end(e);
+            break;
+
+        case 2:
+            syck_emit_map(e, "x-private:key", map_none);
+            syck_emit_item(e, 4);
+            syck_emit_item(e, 5);
+            syck_emit_end(e);
+            break;
+
+        case 3:
+        case 4:
+        case 5:
+            syck_emit_scalar(e, NULL, scalar_none, 0, 0, 0, "foo", 3);
+            break;
     }
+        
 }
 
@@ -48,4 +69,5 @@
     syck_emitter_mark_node(e, 3);
     syck_emitter_mark_node(e, 4);
+    syck_emitter_mark_node(e, 5);
 /*    syck_emitter_mark_node(e, 2);
     syck_emitter_mark_node(e, 1);*/
