[pypy-svn] r45797 - in pypy/branch/pypy-more-rtti-inprogress/rlib: . test

arigo at codespeak.net arigo at codespeak.net
Fri Aug 17 09:59:01 CEST 2007


Author: arigo
Date: Fri Aug 17 09:59:00 2007
New Revision: 45797

Modified:
   pypy/branch/pypy-more-rtti-inprogress/rlib/rmarshal.py
   pypy/branch/pypy-more-rtti-inprogress/rlib/test/test_rmarshal.py
Log:
Unmarshalling.


Modified: pypy/branch/pypy-more-rtti-inprogress/rlib/rmarshal.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/rlib/rmarshal.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/rlib/rmarshal.py	Fri Aug 17 09:59:00 2007
@@ -12,10 +12,13 @@
 class CannotMarshal(Exception):
     pass
 
+class CannotUnmarshall(Exception):
+    pass
+
 def get_marshaller(type):
     s_obj = annotation(type, None)
     try:
-        # look for a marshaller in the 'dumpers' dictionary
+        # look for a marshaller in the 'dumpers' list
         return find_dumper(s_obj)
     except CannotMarshal:
         # ask the annotation to produce an appropriate dumper
@@ -23,9 +26,28 @@
         return find_dumper(s_obj)
 get_marshaller._annspecialcase_ = 'specialize:memo'
 
+def get_loader(type):
+    s_obj = annotation(type, None)
+    try:
+        # look for a marshaller in the 'loaders' list
+        return find_loader(s_obj)
+    except CannotUnmarshall:
+        # ask the annotation to produce an appropriate loader
+        pair(_tag, s_obj).install_unmarshaller()
+        return find_loader(s_obj)
+
 def get_unmarshaller(type):
-    xxx
+    loader = get_loader(type)
+    # wrap the loader into a more convenient interface
+    try:
+        return _unmarshaller_cache[loader]
+    except KeyError:
+        def unmarshaller(buf):
+            return loader(Loader(buf))
+        _unmarshaller_cache[loader] = unmarshaller
+        return unmarshaller
 get_unmarshaller._annspecialcase_ = 'specialize:memo'
+_unmarshaller_cache = {}
 
 # ____________________________________________________________
 #
@@ -40,6 +62,7 @@
 TYPE_LIST     = '['
 
 dumpers = []
+loaders = []
 s_list_of_chars = ListDef(None, annmodel.SomeChar(),
                           mutated=True, resized=True)
 
@@ -47,15 +70,31 @@
     dumpers.append((s_obj, dumper))
     dumper._annenforceargs_ = [s_list_of_chars, s_obj]
 
+def add_loader(s_obj, loader):
+    loaders.append((s_obj, loader))
+
 def get_dumper_annotation(dumper):
     return dumper._annenforceargs_[1]
 
 def find_dumper(s_obj):
+    # select a suitable dumper - the condition is that the dumper must
+    # accept an input that is at least as general as the requested s_obj
     for s_cond, dumper in dumpers:
         if weakly_contains(s_cond, s_obj):
             return dumper
     raise CannotMarshal(s_obj)
 
+def find_loader(s_obj):
+    # select a suitable loader - note that we need more loaders than
+    # dumpers in general, because the condition is that the loader should
+    # return something that is contained within the requested s_obj
+    for s_cond, loader in loaders[::-1]:
+        if s_obj.contains(s_cond):
+            return loader
+    if s_obj == annmodel.s_None:
+        return load_none
+    raise CannotUnmarshall(s_obj)
+
 def w_long(buf, x):
     buf.append(chr(x & 0xff))
     x >>= 8
@@ -70,11 +109,31 @@
     buf.append(TYPE_NONE)
 add_dumper(annmodel.s_None, dump_none)
 
+def load_none(loader):
+    if loader.readchr() != TYPE_NONE:
+        raise ValueError("expected a None")
+    return None
+#add_loader(annmodel.s_None, load_none) -- cannot install it as a regular
+# loader, because it will also match any annotation that can be None
+
 def dump_int(buf, x):
     buf.append(TYPE_INT)
     w_long(buf, x)
 add_dumper(annmodel.SomeInteger(), dump_int)
 
+def load_int_nonneg(loader):
+    x = load_int(loader)
+    if x < 0:
+        raise ValueError("expected a non-negative int")
+    return x
+add_loader(annmodel.SomeInteger(nonneg=True), load_int_nonneg)
+
+def load_int(loader):
+    if loader.readchr() != TYPE_INT:
+        raise ValueError("expected an int")
+    return loader.r_long()
+add_loader(annmodel.SomeInteger(), load_int)
+
 def dump_float(buf, x):
     buf.append(TYPE_FLOAT)
     s = formatd("%.17g", x)
@@ -82,6 +141,14 @@
     buf += s
 add_dumper(annmodel.SomeFloat(), dump_float)
 
+##def load_float(loader):
+##    if loader.readchr() != TYPE_FLOAT:
+##        raise ValueError("expected a float")
+##    length = ord(loader.readchr())
+##    s = loader.read(length)
+##    return ...mess...
+##add_loader(annmodel.SomeFloat(), load_float)
+
 def dump_string_or_none(buf, x):
     if x is None:
         dump_none(buf, x)
@@ -91,6 +158,64 @@
         buf += x
 add_dumper(annmodel.SomeString(can_be_None=True), dump_string_or_none)
 
+def load_single_char(loader):
+    if loader.readchr() != TYPE_STRING or loader.r_long() != 1:
+        raise ValueError("expected a character")
+    return loader.readchr()
+add_loader(annmodel.SomeChar(), load_single_char)
+
+def load_string(loader):
+    if loader.readchr() != TYPE_STRING:
+        raise ValueError("expected a string")
+    length = loader.r_long()
+    return loader.read(length)
+add_loader(annmodel.SomeString(can_be_None=False), load_string)
+
+def load_string_or_none(loader):
+    t = loader.readchr()
+    if t == TYPE_STRING:
+        length = loader.r_long()
+        return loader.read(length)
+    elif t == TYPE_NONE:
+        return None
+    else:
+        raise ValueError("expected a string or None")
+add_loader(annmodel.SomeString(can_be_None=True), load_string_or_none)
+
+# ____________________________________________________________
+#
+# Loader support class
+
+class Loader(object):
+
+    def __init__(self, buf):
+        self.buf = buf
+        self.pos = 0
+
+    def read(self, count):
+        pos = self.pos
+        end = pos + count
+        if end > len(self.buf):
+            raise ValueError("not enough data")
+        self.pos = end
+        return self.buf[pos:end]
+
+    def readchr(self):
+        pos = self.pos
+        if pos >= len(self.buf):
+            raise ValueError("not enough data")
+        self.pos = pos + 1
+        return self.buf[pos]
+
+    def r_long(self):
+        a = ord(self.readchr())
+        b = ord(self.readchr())
+        c = ord(self.readchr())
+        d = ord(self.readchr())
+        if d >= 0x80:
+            d -= 0x100
+        return a | (b<<8) | (c<<16) | (d<<24)
+
 # ____________________________________________________________
 #
 # Annotations => dumpers and loaders
@@ -118,6 +243,9 @@
     def install_marshaller((tag, s_obj)):
         raise CannotMarshal(s_obj)
 
+    def install_unmarshaller((tag, s_obj)):
+        raise CannotUnmarshall(s_obj)
+
 
 class __extend__(pairtype(MTag, annmodel.SomeList)):
 
@@ -139,6 +267,23 @@
             s_general_list = annotation([s_item])
         add_dumper(s_general_list, dump_list_or_none)
 
+    def install_unmarshaller((tag, s_list)):
+        def load_list_or_none(loader):
+            t = loader.readchr()
+            if t == TYPE_LIST:
+                length = loader.r_long()
+                result = []
+                for i in range(length):
+                    result.append(itemloader(loader))
+                return result
+            elif t == TYPE_NONE:
+                return None
+            else:
+                raise ValueError("expected a list or None")
+
+        itemloader = get_loader(s_list.listdef.listitem.s_value)
+        add_loader(s_list, load_list_or_none)
+
 
 class __extend__(pairtype(MTag, annmodel.SomeTuple)):
 
@@ -155,3 +300,19 @@
                               for itemdumper in itemdumpers]
         s_general_tuple = annmodel.SomeTuple(dumper_annotations)
         add_dumper(s_general_tuple, dump_tuple)
+
+    def install_unmarshaller((tag, s_tuple)):
+        def load_tuple(loader):
+            if loader.readchr() != TYPE_TUPLE:
+                raise ValueError("expected a tuple")
+            if loader.r_long() != expected_length:
+                raise ValueError("wrong tuple length")
+            result = ()
+            for i, itemloader in unroll_item_loaders:
+                result += (itemloader(loader),)
+            return result
+
+        itemloaders = [get_loader(s_item) for s_item in s_tuple.items]
+        expected_length = len(itemloaders)
+        unroll_item_loaders = unrolling_iterable(enumerate(itemloaders))
+        add_loader(s_tuple, load_tuple)

Modified: pypy/branch/pypy-more-rtti-inprogress/rlib/test/test_rmarshal.py
==============================================================================
--- pypy/branch/pypy-more-rtti-inprogress/rlib/test/test_rmarshal.py	(original)
+++ pypy/branch/pypy-more-rtti-inprogress/rlib/test/test_rmarshal.py	Fri Aug 17 09:59:00 2007
@@ -1,5 +1,13 @@
+import py
 import marshal
 from pypy.rlib.rmarshal import *
+from pypy.annotation import model as annmodel
+
+types_that_can_be_none = [
+    [int],
+    annmodel.SomeString(can_be_None=True),
+    annmodel.s_None,
+    ]
 
 
 def test_marshaller():
@@ -23,3 +31,37 @@
     get_marshaller((int, float, (str, ())))(buf, (7, -1.5, ("foo", ())))
     assert marshal.loads(''.join(buf)) == (7, -1.5, ("foo", ()))
 
+    for typ in types_that_can_be_none:
+        buf = []
+        get_marshaller(typ)(buf, None)
+        assert marshal.loads(''.join(buf)) is None
+
+
+def test_unmarshaller():
+    buf = 'i\x05\x00\x00\x00'
+    assert get_unmarshaller(int)(buf) == 5
+
+##    buf = 'f\x043.25'
+##    assert get_unmarshaller(float)(buf) == 3.25
+
+    buf = 's\x0c\x00\x00\x00hello, world'
+    assert get_unmarshaller(str)(buf) == "hello, world"
+
+    buf = 's\x01\x00\x00\x00X'
+    assert get_unmarshaller(annmodel.SomeChar())(buf) == "X"
+
+    buf = 'i\x05\x00\x00\x00'
+    py.test.raises(ValueError, get_unmarshaller(str), buf)
+
+    buf = ('[\x03\x00\x00\x00i\x02\x00\x00\x00i\x05\x00\x00\x00'
+           'i\xf9\xff\xff\xff')
+    assert get_unmarshaller([int])(buf) == [2, 5, -7]
+
+    buf = ('(\x02\x00\x00\x00i\x07\x00\x00\x00(\x02\x00\x00\x00'
+           's\x03\x00\x00\x00foo(\x00\x00\x00\x00')
+    res = get_unmarshaller((int, (str, ())))(buf)
+    assert res == (7, ("foo", ()))
+
+    for typ in types_that_can_be_none:
+        buf = 'N'
+        assert get_unmarshaller(typ)(buf) is None



More information about the Pypy-commit mailing list