[pypy-commit] pypy strbufobject: backout c74e33eeef0d: undelete W_StringBufferObject and withstrbuf config option

rlamy pypy.commits at gmail.com
Sat May 20 16:10:45 EDT 2017


Author: Ronan Lamy <ronan.lamy at gmail.com>
Branch: strbufobject
Changeset: r91354:8c65231e844b
Date: 2017-05-20 21:09 +0100
http://bitbucket.org/pypy/pypy/changeset/8c65231e844b/

Log:	backout c74e33eeef0d: undelete W_StringBufferObject and withstrbuf
	config option

diff --git a/pypy/config/pypyoption.py b/pypy/config/pypyoption.py
--- a/pypy/config/pypyoption.py
+++ b/pypy/config/pypyoption.py
@@ -220,6 +220,9 @@
         BoolOption("withsmalllong", "use a version of 'long' in a C long long",
                    default=False),
 
+        BoolOption("withstrbuf", "use strings optimized for addition (ver 2)",
+                   default=False),
+
         BoolOption("withspecialisedtuple",
                    "use specialised tuples",
                    default=False),
diff --git a/pypy/objspace/std/bytesobject.py b/pypy/objspace/std/bytesobject.py
--- a/pypy/objspace/std/bytesobject.py
+++ b/pypy/objspace/std/bytesobject.py
@@ -611,31 +611,55 @@
         return mod_format(space, w_values, self, do_unicode=False)
 
     def descr_eq(self, space, w_other):
+        if space.config.objspace.std.withstrbuf:
+            from pypy.objspace.std.strbufobject import W_StringBufferObject
+            if isinstance(w_other, W_StringBufferObject):
+                return space.newbool(self._value == w_other.force())
         if not isinstance(w_other, W_BytesObject):
             return space.w_NotImplemented
         return space.newbool(self._value == w_other._value)
 
     def descr_ne(self, space, w_other):
+        if space.config.objspace.std.withstrbuf:
+            from pypy.objspace.std.strbufobject import W_StringBufferObject
+            if isinstance(w_other, W_StringBufferObject):
+                return space.newbool(self._value != w_other.force())
         if not isinstance(w_other, W_BytesObject):
             return space.w_NotImplemented
         return space.newbool(self._value != w_other._value)
 
     def descr_lt(self, space, w_other):
+        if space.config.objspace.std.withstrbuf:
+            from pypy.objspace.std.strbufobject import W_StringBufferObject
+            if isinstance(w_other, W_StringBufferObject):
+                return space.newbool(self._value < w_other.force())
         if not isinstance(w_other, W_BytesObject):
             return space.w_NotImplemented
         return space.newbool(self._value < w_other._value)
 
     def descr_le(self, space, w_other):
+        if space.config.objspace.std.withstrbuf:
+            from pypy.objspace.std.strbufobject import W_StringBufferObject
+            if isinstance(w_other, W_StringBufferObject):
+                return space.newbool(self._value <= w_other.force())
         if not isinstance(w_other, W_BytesObject):
             return space.w_NotImplemented
         return space.newbool(self._value <= w_other._value)
 
     def descr_gt(self, space, w_other):
+        if space.config.objspace.std.withstrbuf:
+            from pypy.objspace.std.strbufobject import W_StringBufferObject
+            if isinstance(w_other, W_StringBufferObject):
+                return space.newbool(self._value > w_other.force())
         if not isinstance(w_other, W_BytesObject):
             return space.w_NotImplemented
         return space.newbool(self._value > w_other._value)
 
     def descr_ge(self, space, w_other):
+        if space.config.objspace.std.withstrbuf:
+            from pypy.objspace.std.strbufobject import W_StringBufferObject
+            if isinstance(w_other, W_StringBufferObject):
+                return space.newbool(self._value >= w_other.force())
         if not isinstance(w_other, W_BytesObject):
             return space.w_NotImplemented
         return space.newbool(self._value >= w_other._value)
@@ -653,6 +677,18 @@
             from .bytearrayobject import W_BytearrayObject, _make_data
             self_as_bytearray = W_BytearrayObject(_make_data(self._value))
             return space.add(self_as_bytearray, w_other)
+        if space.config.objspace.std.withstrbuf:
+            from pypy.objspace.std.strbufobject import W_StringBufferObject
+            try:
+                other = self._op_val(space, w_other)
+            except OperationError as e:
+                if e.match(space, space.w_TypeError):
+                    return space.w_NotImplemented
+                raise
+            builder = StringBuilder()
+            builder.append(self._value)
+            builder.append(other)
+            return W_StringBufferObject(builder)
         return self._StringMethods_descr_add(space, w_other)
 
     _StringMethods__startswith = _startswith
diff --git a/pypy/objspace/std/objspace.py b/pypy/objspace/std/objspace.py
--- a/pypy/objspace/std/objspace.py
+++ b/pypy/objspace/std/objspace.py
@@ -16,7 +16,7 @@
 from pypy.objspace.std.boolobject import W_BoolObject
 from pypy.objspace.std.bufferobject import W_Buffer
 from pypy.objspace.std.bytearrayobject import W_BytearrayObject
-from pypy.objspace.std.bytesobject import W_BytesObject
+from pypy.objspace.std.bytesobject import W_AbstractBytesObject, W_BytesObject
 from pypy.objspace.std.complexobject import W_ComplexObject
 from pypy.objspace.std.dictmultiobject import W_DictMultiObject, W_DictObject
 from pypy.objspace.std.floatobject import W_FloatObject
@@ -81,6 +81,9 @@
             W_TypeObject.typedef: W_TypeObject,
             W_UnicodeObject.typedef: W_UnicodeObject,
         }
+        if self.config.objspace.std.withstrbuf:
+            builtin_type_classes[W_BytesObject.typedef] = W_AbstractBytesObject
+
         self.builtin_types = {}
         self._interplevel_classes = {}
         for typedef, cls in builtin_type_classes.items():
@@ -282,7 +285,7 @@
         return W_LongObject.fromint(self, val)
 
     @specialize.argtype(1)
-    def newlong_from_rarith_int(self, val): # val is an rarithmetic type
+    def newlong_from_rarith_int(self, val): # val is an rarithmetic type 
         return W_LongObject.fromrarith_int(val)
 
     def newlong_from_rbigint(self, val):
diff --git a/pypy/objspace/std/strbufobject.py b/pypy/objspace/std/strbufobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/strbufobject.py
@@ -0,0 +1,96 @@
+import inspect
+
+import py
+
+from pypy.objspace.std.bytesobject import W_AbstractBytesObject, W_BytesObject
+from pypy.interpreter.gateway import interp2app, unwrap_spec
+from pypy.interpreter.buffer import SimpleView, StringBuffer
+from pypy.interpreter.error import OperationError
+from rpython.rlib.rstring import StringBuilder
+
+
+class W_StringBufferObject(W_AbstractBytesObject):
+    w_str = None
+
+    def __init__(self, builder):
+        self.builder = builder             # StringBuilder
+        self.length = builder.getlength()
+
+    def force(self):
+        if self.w_str is None:
+            s = self.builder.build()
+            if self.length < len(s):
+                s = s[:self.length]
+            self.w_str = W_BytesObject(s)
+            return s
+        else:
+            return self.w_str._value
+
+    def __repr__(self):
+        """ representation for debugging purposes """
+        return "%s(%r[:%d])" % (
+            self.__class__.__name__, self.builder, self.length)
+
+    def unwrap(self, space):
+        return self.force()
+
+    def str_w(self, space):
+        return self.force()
+
+    def buffer_w(self, space, flags):
+        return SimpleView(StringBuffer(self.force()))
+
+    def descr_len(self, space):
+        return space.newint(self.length)
+
+    def descr_add(self, space, w_other):
+        try:
+            other = W_BytesObject._op_val(space, w_other)
+        except OperationError as e:
+            if e.match(space, space.w_TypeError):
+                return space.w_NotImplemented
+            raise
+        if self.builder.getlength() != self.length:
+            builder = StringBuilder()
+            builder.append(self.force())
+        else:
+            builder = self.builder
+        builder.append(other)
+        return W_StringBufferObject(builder)
+
+    def descr_str(self, space):
+        # you cannot get subclasses of W_StringBufferObject here
+        assert type(self) is W_StringBufferObject
+        return self
+
+
+delegation_dict = {}
+for key, value in W_BytesObject.typedef.rawdict.iteritems():
+    if not isinstance(value, interp2app):
+        continue
+    if key in ('__len__', '__add__', '__str__'):
+        continue
+
+    func = value._code._bltin
+    args = inspect.getargs(func.func_code)
+    if args.varargs or args.keywords:
+        raise TypeError("Varargs and keywords not supported in unwrap_spec")
+    argspec = ', '.join([arg for arg in args.args[1:]])
+    func_code = py.code.Source("""
+    def f(self, %(args)s):
+        self.force()
+        return self.w_str.%(func_name)s(%(args)s)
+    """ % {'args': argspec, 'func_name': func.func_name})
+    d = {}
+    exec func_code.compile() in d
+    f = d['f']
+    f.func_defaults = func.func_defaults
+    f.__module__ = func.__module__
+    # necessary for unique identifiers for pickling
+    f.func_name = func.func_name
+    unwrap_spec_ = getattr(func, 'unwrap_spec', None)
+    if unwrap_spec_ is not None:
+        f = unwrap_spec(**unwrap_spec_)(f)
+    setattr(W_StringBufferObject, func.func_name, f)
+
+W_StringBufferObject.typedef = W_BytesObject.typedef
diff --git a/pypy/objspace/std/test/test_stdobjspace.py b/pypy/objspace/std/test/test_stdobjspace.py
--- a/pypy/objspace/std/test/test_stdobjspace.py
+++ b/pypy/objspace/std/test/test_stdobjspace.py
@@ -57,6 +57,13 @@
         cls = space._get_interplevel_cls(w_sequenceiterator)
         assert cls is W_AbstractSeqIterObject
 
+    def test_withstrbuf_fastpath_isinstance(self):
+        from pypy.objspace.std.bytesobject import W_AbstractBytesObject
+
+        space = gettestobjspace(withstrbuf=True)
+        cls = space._get_interplevel_cls(space.w_bytes)
+        assert cls is W_AbstractBytesObject
+
     def test_wrap_various_unsigned_types(self):
         import sys
         from rpython.rlib.rarithmetic import r_uint
diff --git a/pypy/objspace/std/test/test_strbufobject.py b/pypy/objspace/std/test/test_strbufobject.py
new file mode 100644
--- /dev/null
+++ b/pypy/objspace/std/test/test_strbufobject.py
@@ -0,0 +1,85 @@
+import py
+
+from pypy.objspace.std.test import test_bytesobject
+
+class AppTestStringObject(test_bytesobject.AppTestBytesObject):
+    spaceconfig = {"objspace.std.withstrbuf": True}
+
+    def test_basic(self):
+        import __pypy__
+        # cannot do "Hello, " + "World!" because cpy2.5 optimises this
+        # away on AST level
+        s = "Hello, ".__add__("World!")
+        assert type(s) is str
+        assert 'W_StringBufferObject' in __pypy__.internal_repr(s)
+
+    def test_add_twice(self):
+        x = "a".__add__("b")
+        y = x + "c"
+        c = x + "d"
+        assert y == "abc"
+        assert c == "abd"
+
+    def test_add(self):
+        import __pypy__
+        all = ""
+        for i in range(20):
+            all += str(i)
+        assert 'W_StringBufferObject' in __pypy__.internal_repr(all)
+        assert all == "012345678910111213141516171819"
+
+    def test_hash(self):
+        import __pypy__
+        def join(s): return s[:len(s) // 2] + s[len(s) // 2:]
+        t = 'a' * 101
+        s = join(t)
+        assert 'W_StringBufferObject' in __pypy__.internal_repr(s)
+        assert hash(s) == hash(t)
+
+    def test_len(self):
+        s = "a".__add__("b")
+        r = "c".__add__("d")
+        t = s + r
+        assert len(s) == 2
+        assert len(r) == 2
+        assert len(t) == 4
+
+    def test_buffer(self):
+        s = b'a'.__add__(b'b')
+        assert buffer(s) == buffer(b'ab')
+        assert memoryview(s) == b'ab'
+
+    def test_add_strbuf(self):
+        # make three strbuf objects
+        s = 'a'.__add__('b')
+        t = 'x'.__add__('c')
+        u = 'y'.__add__('d')
+
+        # add two different strbufs to the same string
+        v = s + t
+        w = s + u
+
+        # check that insanity hasn't resulted.
+        assert v == "abxc"
+        assert w == "abyd"
+
+    def test_more_adding_fun(self):
+        s = 'a'.__add__('b') # s is a strbuf now
+        t = s + 'c'
+        u = s + 'd'
+        v = s + 'e'
+        assert v == 'abe'
+        assert u == 'abd'
+        assert t == 'abc'
+
+    def test_buh_even_more(self):
+        a = 'a'.__add__('b')
+        b = a + 'c'
+        c = '0'.__add__('1')
+        x = c + a
+        assert x == '01ab'
+
+    def test_add_non_string(self):
+        a = 'a'
+        a += 'b'
+        raises(TypeError, "a += 5")


More information about the pypy-commit mailing list