Author: Armin Rigo <[email protected]>
Branch: cpyext-ext
Changeset: r83743:6940834b346d
Date: 2016-04-18 16:29 +0200
http://bitbucket.org/pypy/pypy/changeset/6940834b346d/

Log:    Add another test in cpyext, and fix, including to
        operator.is{Sequence,Mapping}Type()

diff --git a/pypy/module/cpyext/test/test_iterator.py 
b/pypy/module/cpyext/test/test_iterator.py
--- a/pypy/module/cpyext/test/test_iterator.py
+++ b/pypy/module/cpyext/test/test_iterator.py
@@ -58,5 +58,53 @@
         obj = module.test()
         assert obj["hi there"] == 42
         assert len(obj) == 2
+        assert not hasattr(obj, "__iter__")
         e = raises(TypeError, iter, obj)
         assert str(e.value).endswith("object is not iterable")
+        #
+        import operator
+        assert not operator.isSequenceType(obj)
+        assert operator.isMappingType(obj)
+
+    def test_iterable_nonmapping_object(self):
+        module = self.import_extension('foo', [
+           ("test", "METH_NOARGS",
+            '''
+                PyObject *obj;
+                Foo_Type.tp_flags = Py_TPFLAGS_DEFAULT;
+                Foo_Type.tp_as_sequence = &tp_as_sequence;
+                tp_as_sequence.sq_length = sq_length;
+                tp_as_sequence.sq_item = sq_item;
+                if (PyType_Ready(&Foo_Type) < 0) return NULL;
+                obj = PyObject_New(PyObject, &Foo_Type);
+                return obj;
+            '''
+            )],
+            '''
+            static PyObject *
+            sq_item(PyObject *self, Py_ssize_t size)
+            {
+                return PyInt_FromLong(42);
+            }
+            static Py_ssize_t
+            sq_length(PyObject *self)
+            {
+                return 2;
+            }
+            PySequenceMethods tp_as_sequence;
+            static PyTypeObject Foo_Type = {
+                PyVarObject_HEAD_INIT(NULL, 0)
+                "foo.foo",
+            };
+            ''')
+        obj = module.test()
+        assert obj[1] == 42
+        assert len(obj) == 2
+        assert not hasattr(obj, "__iter__")
+        it = iter(obj)
+        assert it.next() == 42
+        assert it.next() == 42
+        #
+        import operator
+        assert operator.isSequenceType(obj)
+        assert not operator.isMappingType(obj)
diff --git a/pypy/module/cpyext/typeobject.py b/pypy/module/cpyext/typeobject.py
--- a/pypy/module/cpyext/typeobject.py
+++ b/pypy/module/cpyext/typeobject.py
@@ -385,6 +385,12 @@
         if not space.is_true(space.issubtype(self, space.w_type)):
             self.flag_cpytype = True
         self.flag_heaptype = False
+        # if a sequence or a mapping, then set the flag to force it
+        if pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_item:
+            self.flag_map_or_seq = 'S'
+        elif (pto.c_tp_as_mapping and pto.c_tp_as_mapping.c_mp_subscript and
+              not (pto.c_tp_as_sequence and pto.c_tp_as_sequence.c_sq_slice)):
+            self.flag_map_or_seq = 'M'
         if pto.c_tp_doc:
             self.w_doc = space.wrap(rffi.charp2str(pto.c_tp_doc))
 
diff --git a/pypy/module/operator/__init__.py b/pypy/module/operator/__init__.py
--- a/pypy/module/operator/__init__.py
+++ b/pypy/module/operator/__init__.py
@@ -18,7 +18,7 @@
 
     app_names = ['__delslice__', '__getslice__', '__repeat__', '__setslice__',
                  'countOf', 'delslice', 'getslice', 'indexOf',
-                 'isMappingType', 'isNumberType', 'isSequenceType',
+                 'isNumberType',
                  'repeat', 'setslice',
                  'attrgetter', 'itemgetter', 'methodcaller',
     ]
@@ -36,7 +36,8 @@
                     'sub', 'truediv', 'truth', 'xor',
                     'iadd', 'iand', 'iconcat', 'idiv', 'ifloordiv',
                     'ilshift', 'imod', 'imul', 'ior', 'ipow', 'irepeat',
-                    'irshift', 'isub', 'itruediv', 'ixor', '_length_hint']
+                    'irshift', 'isub', 'itruediv', 'ixor', '_length_hint',
+                    'isSequenceType', 'isMappingType']
 
     interpleveldefs = {
         '_compare_digest': 'tscmp.compare_digest',
diff --git a/pypy/module/operator/app_operator.py 
b/pypy/module/operator/app_operator.py
--- a/pypy/module/operator/app_operator.py
+++ b/pypy/module/operator/app_operator.py
@@ -6,6 +6,7 @@
 '''
 
 import types
+import __pypy__
 
 
 def countOf(a,b):
@@ -39,27 +40,18 @@
         index += 1
     raise ValueError('sequence.index(x): x not in sequence')
 
-def isMappingType(obj,):
-    'isMappingType(a) -- Return True if a has a mapping type, False otherwise.'
-    if isinstance(obj, types.InstanceType):
-        return hasattr(obj, '__getitem__')
-    return hasattr(obj, '__getitem__') and not hasattr(obj, '__getslice__')
-
 def isNumberType(obj,):
     'isNumberType(a) -- Return True if a has a numeric type, False otherwise.'
-    return hasattr(obj, '__int__') or hasattr(obj, '__float__')
-
-def isSequenceType(obj,):
-    'isSequenceType(a) -- Return True if a has a sequence type, False 
otherwise.'
-    if isinstance(obj, dict):
-        return False
-    return hasattr(obj, '__getitem__')
+    return (__pypy__.lookup_special(obj, '__int__') is not None or
+            __pypy__.lookup_special(obj, '__float__') is not None)
 
 def repeat(obj, num):
     'repeat(a, b) -- Return a * b, where a is a sequence, and b is an integer.'
+    import operator
+
     if not isinstance(num, (int, long)):
         raise TypeError('an integer is required')
-    if not isSequenceType(obj):
+    if not operator.isSequenceType(obj):
         raise TypeError("non-sequence object can't be repeated")
 
     return obj * num
diff --git a/pypy/module/operator/interp_operator.py 
b/pypy/module/operator/interp_operator.py
--- a/pypy/module/operator/interp_operator.py
+++ b/pypy/module/operator/interp_operator.py
@@ -1,5 +1,6 @@
 from pypy.interpreter.error import OperationError
 from pypy.interpreter.gateway import unwrap_spec
+from pypy.module.__builtin__.interp_classobj import W_InstanceObject
 
 
 def index(space, w_a):
@@ -247,3 +248,33 @@
 @unwrap_spec(default=int)
 def _length_hint(space, w_iterable, default):
     return space.wrap(space.length_hint(w_iterable, default))
+
+
+def isMappingType(space, w_obj):
+    'isMappingType(a) -- Return True if a has a mapping type, False otherwise.'
+    if space.is_oldstyle_instance(w_obj):
+        result = (space.findattr(w_obj, space.wrap('__getitem__')) is not None)
+    else:
+        flag = space.type(w_obj).flag_map_or_seq
+        if flag == 'M':
+            result = True
+        elif flag == 'S':
+            result = False
+        else:
+            result = (space.lookup(w_obj, '__getitem__') is not None and
+                      space.lookup(w_obj, '__getslice__') is None)
+    return space.wrap(result)
+
+def isSequenceType(space, w_obj):
+    'isSequenceType(a) -- Return True if a has a sequence type, False 
otherwise.'
+    if space.is_oldstyle_instance(w_obj):
+        result = (space.findattr(w_obj, space.wrap('__getitem__')) is not None)
+    else:
+        flag = space.type(w_obj).flag_map_or_seq
+        if flag == 'M':
+            result = False
+        elif flag == 'S':
+            result = True
+        else:
+            result = (space.lookup(w_obj, '__getitem__') is not None)
+    return space.wrap(result)
diff --git a/pypy/module/operator/test/test_operator.py 
b/pypy/module/operator/test/test_operator.py
--- a/pypy/module/operator/test/test_operator.py
+++ b/pypy/module/operator/test/test_operator.py
@@ -184,6 +184,19 @@
         class Dict(dict): pass
         assert not operator.isSequenceType(Dict())
 
+    def test_isXxxType_more(self):
+        import operator
+
+        assert not operator.isSequenceType(list)
+        assert not operator.isSequenceType(dict)
+        assert not operator.isSequenceType({})
+        assert not operator.isMappingType(list)
+        assert not operator.isMappingType(dict)
+        assert not operator.isMappingType([])
+        assert not operator.isMappingType(())
+        assert not operator.isNumberType(int)
+        assert not operator.isNumberType(float)
+
     def test_inplace(self):
         import operator
 
diff --git a/pypy/objspace/descroperation.py b/pypy/objspace/descroperation.py
--- a/pypy/objspace/descroperation.py
+++ b/pypy/objspace/descroperation.py
@@ -273,7 +273,8 @@
     def iter(space, w_obj):
         w_descr = space.lookup(w_obj, '__iter__')
         if w_descr is None:
-            w_descr = space.lookup(w_obj, '__getitem__')
+            if space.type(w_obj).flag_map_or_seq != 'M':
+                w_descr = space.lookup(w_obj, '__getitem__')
             if w_descr is None:
                 raise oefmt(space.w_TypeError,
                             "'%T' object is not iterable", w_obj)
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
@@ -90,6 +90,7 @@
             self.builtin_types[typedef.name] = w_type
             setattr(self, 'w_' + typedef.name, w_type)
             self._interplevel_classes[w_type] = cls
+        self.w_dict.flag_map_or_seq = 'M'
         self.builtin_types["NotImplemented"] = self.w_NotImplemented
         self.builtin_types["Ellipsis"] = self.w_Ellipsis
         self.w_basestring = self.builtin_types['basestring'] = \
diff --git a/pypy/objspace/std/typeobject.py b/pypy/objspace/std/typeobject.py
--- a/pypy/objspace/std/typeobject.py
+++ b/pypy/objspace/std/typeobject.py
@@ -126,6 +126,7 @@
                           "flag_cpytype",
                           "flag_abstract?",
                           "flag_sequence_bug_compat",
+                          "flag_map_or_seq",    # '?' or 'M' or 'S'
                           'needsdel',
                           'weakrefable',
                           'hasdict',
@@ -162,6 +163,7 @@
         w_self.flag_cpytype = False
         w_self.flag_abstract = False
         w_self.flag_sequence_bug_compat = False
+        w_self.flag_map_or_seq = '?'   # '?' means "don't know, check 
otherwise"
 
         if overridetypedef is not None:
             assert not force_new_layout
@@ -1096,6 +1098,8 @@
             continue
         w_self.flag_cpytype |= w_base.flag_cpytype
         w_self.flag_abstract |= w_base.flag_abstract
+        if w_self.flag_map_or_seq == '?':
+            w_self.flag_map_or_seq = w_base.flag_map_or_seq
 
     hasoldstylebase = copy_flags_from_bases(w_self, w_bestbase)
     layout = create_all_slots(w_self, hasoldstylebase, w_bestbase,
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to