[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