[pypy-commit] pypy default: test, fix for tzinfo. datetime inherits from date, but date has no tzinfo, monkeypatch alloc appropriately

mattip pypy.commits at gmail.com
Sat Feb 17 15:20:26 EST 2018


Author: Matti Picus <matti.picus at gmail.com>
Branch: 
Changeset: r93827:c7ea666563e3
Date: 2018-02-17 22:19 +0200
http://bitbucket.org/pypy/pypy/changeset/c7ea666563e3/

Log:	test, fix for tzinfo. datetime inherits from date, but date has no
	tzinfo, monkeypatch alloc appropriately

diff --git a/pypy/module/cpyext/cdatetime.py b/pypy/module/cpyext/cdatetime.py
--- a/pypy/module/cpyext/cdatetime.py
+++ b/pypy/module/cpyext/cdatetime.py
@@ -2,9 +2,10 @@
 from rpython.rtyper.annlowlevel import llhelper
 from rpython.rlib.rarithmetic import widen
 from pypy.module.cpyext.pyobject import (PyObject, make_ref, make_typedescr,
-    decref)
+    decref, as_pyobj, incref)
 from pypy.module.cpyext.api import (cpython_api, CANNOT_FAIL, cpython_struct,
-    PyObjectFields, cts, parse_dir, bootstrap_function, slot_function)
+    PyObjectFields, cts, parse_dir, bootstrap_function, slot_function,
+    Py_TPFLAGS_HEAPTYPE)
 from pypy.module.cpyext.import_ import PyImport_Import
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 from pypy.interpreter.error import OperationError
@@ -128,6 +129,8 @@
     # W_DateTime_Date->tp_dealloc
     make_typedescr(W_DateTime_Date.typedef,
                    basestruct=PyDateTime_DateTime.TO,
+                   alloc=date_or_datetime_allocate,
+                   attach=type_attach,
                    dealloc=date_dealloc,
                   )
 
@@ -136,10 +139,33 @@
                    attach=timedeltatype_attach,
                   )
 
+def date_or_datetime_allocate(self, space, w_type, itemcount=0, immortal=False):
+    # allocates a date or datetime object. datetime has a tzinfo field, date does not
+    pytype = as_pyobj(space, w_type)
+    pytype = rffi.cast(PyTypeObjectPtr, pytype)
+    if w_type.name == 'date':
+        # XXX we should do this where the 'date' and 'datetime' type is instantiated
+        pytype.c_tp_basicsize = rffi.sizeof(PyObject.TO)
+    size = pytype.c_tp_basicsize
+    incref(space, pytype)
+    assert size >= rffi.sizeof(PyObject.TO)
+    buf = lltype.malloc(rffi.VOIDP.TO, size,
+                        flavor='raw', zero=True,
+                        add_memory_pressure=True, immortal=immortal)
+    pyobj = rffi.cast(PyObject, buf)
+    pyobj.c_ob_refcnt = 1
+    #pyobj.c_ob_pypy_link should get assigned very quickly
+    pyobj.c_ob_type = pytype
+    return pyobj
+
 def type_attach(space, py_obj, w_obj, w_userdata=None):
     '''Fills a newly allocated py_obj from the w_obj
-       Can be called with a datetime, or a time
     '''
+    if space.type(w_obj).name == 'date':
+        # No tzinfo
+        return
+    # just make sure, should be removed
+    assert py_obj.c_ob_type.c_tp_basicsize > rffi.sizeof(PyObject.TO)
     py_datetime = rffi.cast(PyDateTime_Time, py_obj)
     w_tzinfo = space.getattr(w_obj, space.newtext('tzinfo'))
     if space.is_none(w_tzinfo):
diff --git a/pypy/module/cpyext/test/test_datetime.py b/pypy/module/cpyext/test/test_datetime.py
--- a/pypy/module/cpyext/test/test_datetime.py
+++ b/pypy/module/cpyext/test/test_datetime.py
@@ -82,16 +82,6 @@
         date = datetime.datetime.fromtimestamp(0)
         assert space.unwrap(space.str(w_date)) == str(date)
 
-    def test_tzinfo(self, space):
-        w_tzinfo = space.appexec(
-            [], """():
-            from datetime import tzinfo
-            return tzinfo()
-        """)
-        assert PyTZInfo_Check(space, w_tzinfo)
-        assert PyTZInfo_CheckExact(space, w_tzinfo)
-        assert not PyTZInfo_Check(space, space.w_None)
-
 class AppTestDatetime(AppTestCpythonExtensionBase):
     def test_CAPI(self):
         module = self.import_extension('foo', [
@@ -272,3 +262,81 @@
             2000, 6, 6, 6, 6, 6, 6)
         assert module.test_time_macros() == datetime.time(6, 6, 6, 6)
         assert module.test_delta_macros() == datetime.timedelta(6, 6, 6)
+
+    def test_tzinfo(self):
+        module = self.import_extension('foo', [
+            ("time_with_tzinfo", "METH_O",
+             """ PyDateTime_IMPORT;
+                 return PyDateTimeAPI->Time_FromTime(
+                    6, 6, 6, 6, args, PyDateTimeAPI->TimeType);
+             """),
+            ("datetime_with_tzinfo", "METH_O",
+             """ 
+                 PyObject * obj;
+                 int tzrefcnt = args->ob_refcnt; 
+                 PyDateTime_IMPORT;
+                 obj = PyDateTimeAPI->DateTime_FromDateAndTime(
+                    2000, 6, 6, 6, 6, 6, 6, args,
+                    PyDateTimeAPI->DateTimeType);
+                if (!((PyDateTime_DateTime*)obj)->hastzinfo)
+                {
+                    Py_DECREF(obj);
+                    PyErr_SetString(PyExc_ValueError, "missing tzinfo");
+                    return NULL;
+                }
+                if (((PyDateTime_DateTime*)obj)->tzinfo->ob_refcnt <= tzrefcnt)
+                {
+                    Py_DECREF(obj);
+                    PyErr_SetString(PyExc_ValueError, "tzinfo refcnt not incremented");
+                    return NULL;
+                }
+                return obj;
+                
+             """),
+        ], prologue='#include "datetime.h"\n')
+        from datetime import tzinfo, datetime, timedelta, time
+        # copied from datetime documentation
+        class GMT1(tzinfo):
+           def utcoffset(self, dt):
+               return timedelta(hours=1) + self.dst(dt)
+           def dst(self, dt):
+               return timedelta(0)
+           def tzname(self,dt):
+                return "GMT +1"
+        gmt1 = GMT1()
+        dt1 = module.time_with_tzinfo(gmt1)
+        assert dt1 == time(6, 6, 6, 6, gmt1)
+        assert '+01' in str(dt1)
+        assert module.datetime_with_tzinfo(gmt1) == datetime(
+            2000, 6, 6, 6, 6, 6, 6, gmt1)
+
+    def test_checks(self):
+        module = self.import_extension('foo', [
+            ("checks", "METH_O",
+             """ PyDateTime_IMPORT;
+                 return PyTuple_Pack(10,
+                    PyBool_FromLong(PyDateTime_Check(args)),
+                    PyBool_FromLong(PyDateTime_CheckExact(args)),
+                    PyBool_FromLong(PyDate_Check(args)),
+                    PyBool_FromLong(PyDate_CheckExact(args)),
+                    PyBool_FromLong(PyTime_Check(args)),
+                    PyBool_FromLong(PyTime_CheckExact(args)),
+                    PyBool_FromLong(PyDelta_Check(args)),
+                    PyBool_FromLong(PyDelta_CheckExact(args)),
+                    PyBool_FromLong(PyTZInfo_Check(args)),
+                    PyBool_FromLong(PyTZInfo_CheckExact(args))
+                );
+             """),
+        ], prologue='#include "datetime.h"\n')
+        from datetime import tzinfo, datetime, timedelta, time, date
+        o = date(1, 1, 1)
+        assert module.checks(o) == (False,) * 2 + (True,) * 2 + (False,) * 6
+        o = time(1, 1, 1)
+        assert module.checks(o) == (False,) * 4 + (True,) * 2 + (False,) * 4
+        o = timedelta(1, 1, 1)
+        assert module.checks(o) == (False,) * 6 + (True,) * 2 + (False,) * 2
+        o = datetime(1, 1, 1)
+        assert module.checks(o) == (True,) * 3 + (False,) * 7 # isinstance(datetime, date)
+        o = tzinfo()
+        assert module.checks(o) == (False,) * 8 + (True,) * 2
+        


More information about the pypy-commit mailing list