Author: Matti Picus <matti.pi...@gmail.com>
Branch: 
Changeset: r93684:8a1cb5467f77
Date: 2018-01-19 11:10 +0200
http://bitbucket.org/pypy/pypy/changeset/8a1cb5467f77/

Log:    merge cpyext-datetime2 which adds a tzinfo field to datetime C-API
        types

diff --git a/lib_pypy/datetime.py b/lib_pypy/datetime.py
--- a/lib_pypy/datetime.py
+++ b/lib_pypy/datetime.py
@@ -17,10 +17,13 @@
 """
 
 from __future__ import division
-import time as _time
+import time as _timemodule
 import math as _math
 import struct as _struct
 
+# for cpyext, use these as base classes
+from __pypy__._pypydatetime import dateinterop, deltainterop, timeinterop
+
 _SENTINEL = object()
 
 def _cmp(x, y):
@@ -179,7 +182,7 @@
 def _build_struct_time(y, m, d, hh, mm, ss, dstflag):
     wday = (_ymd2ord(y, m, d) + 6) % 7
     dnum = _days_before_month(y, m) + d
-    return _time.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))
+    return _timemodule.struct_time((y, m, d, hh, mm, ss, wday, dnum, dstflag))
 
 def _format_time(hh, mm, ss, us):
     # Skip trailing microseconds when us==0.
@@ -247,7 +250,7 @@
         else:
             push(ch)
     newformat = "".join(newformat)
-    return _time.strftime(newformat, timetuple)
+    return _timemodule.strftime(newformat, timetuple)
 
 # Just raise TypeError if the arg isn't None or a string.
 def _check_tzname(name):
@@ -433,7 +436,7 @@
     raise TypeError("unsupported type for timedelta %s component: %s" %
                     (tag, type(num)))
 
-class timedelta(object):
+class timedelta(deltainterop):
     """Represent the difference between two datetime objects.
 
     Supported operators:
@@ -489,7 +492,7 @@
         if not -_MAX_DELTA_DAYS <= d <= _MAX_DELTA_DAYS:
             raise OverflowError("days=%d; must have magnitude <= %d" % (d, 
_MAX_DELTA_DAYS))
 
-        self = object.__new__(cls)
+        self = deltainterop.__new__(cls)
         self._days = d
         self._seconds = s
         self._microseconds = us
@@ -667,7 +670,7 @@
 timedelta.max = timedelta(_MAX_DELTA_DAYS, 24*3600-1, 1000000-1)
 timedelta.resolution = timedelta(microseconds=1)
 
-class date(object):
+class date(dateinterop):
     """Concrete date type.
 
     Constructors:
@@ -707,12 +710,12 @@
         if month is None and isinstance(year, bytes) and len(year) == 4 and \
                 1 <= ord(year[2]) <= 12:
             # Pickle support
-            self = object.__new__(cls)
+            self = dateinterop.__new__(cls)
             self.__setstate(year)
             self._hashcode = -1
             return self
         year, month, day = _check_date_fields(year, month, day)
-        self = object.__new__(cls)
+        self = dateinterop.__new__(cls)
         self._year = year
         self._month = month
         self._day = day
@@ -724,13 +727,13 @@
     @classmethod
     def fromtimestamp(cls, t):
         "Construct a date from a POSIX timestamp (like time.time())."
-        y, m, d, hh, mm, ss, weekday, jday, dst = _time.localtime(t)
+        y, m, d, hh, mm, ss, weekday, jday, dst = _timemodule.localtime(t)
         return cls(y, m, d)
 
     @classmethod
     def today(cls):
         "Construct a date from time.time()."
-        t = _time.time()
+        t = _timemodule.time()
         return cls.fromtimestamp(t)
 
     @classmethod
@@ -1061,7 +1064,7 @@
 
 _tzinfo_class = tzinfo
 
-class time(object):
+class time(timeinterop):
     """Time with time zone.
 
     Constructors:
@@ -1097,14 +1100,14 @@
         """
         if isinstance(hour, bytes) and len(hour) == 6 and ord(hour[0]) < 24:
             # Pickle support
-            self = object.__new__(cls)
+            self = timeinterop.__new__(cls)
             self.__setstate(hour, minute or None)
             self._hashcode = -1
             return self
         hour, minute, second, microsecond = _check_time_fields(
             hour, minute, second, microsecond)
         _check_tzinfo_arg(tzinfo)
-        self = object.__new__(cls)
+        self = timeinterop.__new__(cls)
         self._hour = hour
         self._minute = minute
         self._second = second
@@ -1408,7 +1411,7 @@
         if isinstance(year, bytes) and len(year) == 10 and \
                 1 <= ord(year[2]) <= 12:
             # Pickle support
-            self = object.__new__(cls)
+            self = dateinterop.__new__(cls)
             self.__setstate(year, month)
             self._hashcode = -1
             return self
@@ -1416,7 +1419,7 @@
         hour, minute, second, microsecond = _check_time_fields(
             hour, minute, second, microsecond)
         _check_tzinfo_arg(tzinfo)
-        self = object.__new__(cls)
+        self = dateinterop.__new__(cls)
         self._year = year
         self._month = month
         self._day = day
@@ -1461,7 +1464,7 @@
         A timezone info object may be passed in as well.
         """
         _check_tzinfo_arg(tz)
-        converter = _time.localtime if tz is None else _time.gmtime
+        converter = _timemodule.localtime if tz is None else _timemodule.gmtime
         self = cls._from_timestamp(converter, timestamp, tz)
         if tz is not None:
             self = tz.fromutc(self)
@@ -1470,7 +1473,7 @@
     @classmethod
     def utcfromtimestamp(cls, t):
         "Construct a UTC datetime from a POSIX timestamp (like time.time())."
-        return cls._from_timestamp(_time.gmtime, t, None)
+        return cls._from_timestamp(_timemodule.gmtime, t, None)
 
     @classmethod
     def _from_timestamp(cls, converter, timestamp, tzinfo):
@@ -1493,13 +1496,13 @@
     @classmethod
     def now(cls, tz=None):
         "Construct a datetime from time.time() and optional time zone info."
-        t = _time.time()
+        t = _timemodule.time()
         return cls.fromtimestamp(t, tz)
 
     @classmethod
     def utcnow(cls):
         "Construct a UTC datetime from time.time()."
-        t = _time.time()
+        t = _timemodule.time()
         return cls.utcfromtimestamp(t)
 
     @classmethod
diff --git a/pypy/doc/whatsnew-head.rst b/pypy/doc/whatsnew-head.rst
--- a/pypy/doc/whatsnew-head.rst
+++ b/pypy/doc/whatsnew-head.rst
@@ -10,3 +10,7 @@
 Big refactoring of some cpyext code, which avoids a lot of nonsense when
 calling C from Python and vice-versa: the result is a big speedup in
 function/method calls, up to 6 times faster.
+
+.. branch: cpyext-datetime2
+
+Support ``tzinfo`` field on C-API datetime objects, fixes latest pandas HEAD
diff --git a/pypy/module/__pypy__/__init__.py b/pypy/module/__pypy__/__init__.py
--- a/pypy/module/__pypy__/__init__.py
+++ b/pypy/module/__pypy__/__init__.py
@@ -58,6 +58,14 @@
     }
 
 
+class PyPyDateTime(MixedModule):
+    appleveldefs = {}
+    interpleveldefs = {
+        'dateinterop': 'interp_pypydatetime.W_DateTime_Date',
+        'timeinterop'    : 'interp_pypydatetime.W_DateTime_Time',
+        'deltainterop'   : 'interp_pypydatetime.W_DateTime_Delta',
+    }
+
 class Module(MixedModule):
     """ PyPy specific "magic" functions. A lot of them are experimental and
     subject to change, many are internal. """
@@ -108,6 +116,7 @@
         "thread": ThreadModule,
         "intop": IntOpModule,
         "os": OsModule,
+        '_pypydatetime': PyPyDateTime,
     }
 
     def setup_after_space_initialization(self):
diff --git a/pypy/module/__pypy__/interp_pypydatetime.py 
b/pypy/module/__pypy__/interp_pypydatetime.py
new file mode 100644
--- /dev/null
+++ b/pypy/module/__pypy__/interp_pypydatetime.py
@@ -0,0 +1,24 @@
+from pypy.interpreter.baseobjspace import W_Root
+from pypy.interpreter.typedef import TypeDef
+from pypy.interpreter.gateway import interp2app
+from rpython.tool.sourcetools import func_with_new_name
+
+def create_class(name):
+    class W_Class(W_Root):
+        'builtin base clasee for datetime.%s to allow interop with cpyext' % 
name
+        def descr_new__(space, w_type):
+            return space.allocate_instance(W_Class, w_type)
+
+    W_Class.typedef = TypeDef(name,
+        __new__ = interp2app(func_with_new_name(
+                                    W_Class.descr_new__.im_func,
+                                    '%s_new' % (name,))),
+        )
+    W_Class.typedef.acceptable_as_base_class = True
+    return W_Class
+
+W_DateTime_Time = create_class('pypydatetime_time')
+W_DateTime_Date = create_class('pypydatetime_date')
+W_DateTime_Delta = create_class('pypydatetime_delta')
+
+
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
@@ -1,43 +1,28 @@
 from rpython.rtyper.lltypesystem import rffi, lltype
 from rpython.rtyper.annlowlevel import llhelper
-from pypy.module.cpyext.pyobject import PyObject, make_ref
+from rpython.rlib.rarithmetic import widen
+from pypy.module.cpyext.pyobject import (PyObject, make_ref, make_typedescr,
+    decref)
 from pypy.module.cpyext.api import (cpython_api, CANNOT_FAIL, cpython_struct,
-    PyObjectFields)
+    PyObjectFields, cts, parse_dir, bootstrap_function, slot_function)
 from pypy.module.cpyext.import_ import PyImport_Import
 from pypy.module.cpyext.typeobject import PyTypeObjectPtr
 from pypy.interpreter.error import OperationError
+from pypy.module.__pypy__.interp_pypydatetime import (W_DateTime_Date,
+    W_DateTime_Time, W_DateTime_Delta)
 from rpython.tool.sourcetools import func_renamer
 
-# API import function
+cts.parse_header(parse_dir / 'cpyext_datetime.h')
 
-PyDateTime_CAPI = cpython_struct(
-    'PyDateTime_CAPI',
-    (('DateType', PyTypeObjectPtr),
-     ('DateTimeType', PyTypeObjectPtr),
-     ('TimeType', PyTypeObjectPtr),
-     ('DeltaType', PyTypeObjectPtr),
-     ('TZInfoType', PyTypeObjectPtr),
 
-     ('Date_FromDate', lltype.Ptr(lltype.FuncType(
-         [rffi.INT_real, rffi.INT_real, rffi.INT_real, PyTypeObjectPtr],
-         PyObject))),
-     ('Time_FromTime', lltype.Ptr(lltype.FuncType(
-         [rffi.INT_real, rffi.INT_real, rffi.INT_real, rffi.INT_real,
-          PyObject, PyTypeObjectPtr],
-         PyObject))),
-     ('DateTime_FromDateAndTime', lltype.Ptr(lltype.FuncType(
-         [rffi.INT_real, rffi.INT_real, rffi.INT_real,
-          rffi.INT_real, rffi.INT_real, rffi.INT_real, rffi.INT_real,
-          PyObject, PyTypeObjectPtr],
-         PyObject))),
-     ('Delta_FromDelta', lltype.Ptr(lltype.FuncType(
-         [rffi.INT_real, rffi.INT_real, rffi.INT_real, rffi.INT_real,
-          PyTypeObjectPtr],
-         PyObject))),
-     ))
+PyDateTime_CAPI = cts.gettype('PyDateTime_CAPI')
+
+datetimeAPI_global = []
 
 @cpython_api([], lltype.Ptr(PyDateTime_CAPI))
 def _PyDateTime_Import(space):
+    if len(datetimeAPI_global) >0:
+        return datetimeAPI_global[0]
     datetimeAPI = lltype.malloc(PyDateTime_CAPI, flavor='raw',
                                 track_allocation=False)
 
@@ -76,21 +61,13 @@
         _PyDelta_FromDelta.api_func.functype,
         _PyDelta_FromDelta.api_func.get_wrapper(space))
 
+    datetimeAPI_global.append(datetimeAPI)
     return datetimeAPI
 
-PyDateTime_DateStruct = lltype.ForwardReference()
-PyDateTime_TimeStruct = lltype.ForwardReference()
-PyDateTime_DateTimeStruct = lltype.ForwardReference()
-cpython_struct("PyDateTime_Date", PyObjectFields, PyDateTime_DateStruct)
-PyDateTime_Date = lltype.Ptr(PyDateTime_DateStruct)
-cpython_struct("PyDateTime_Time", PyObjectFields, PyDateTime_TimeStruct)
-PyDateTime_Time = lltype.Ptr(PyDateTime_TimeStruct)
-cpython_struct("PyDateTime_DateTime", PyObjectFields, 
PyDateTime_DateTimeStruct)
-PyDateTime_DateTime = lltype.Ptr(PyDateTime_DateTimeStruct)
-
-PyDeltaObjectStruct = lltype.ForwardReference()
-cpython_struct("PyDateTime_Delta", PyObjectFields, PyDeltaObjectStruct)
-PyDateTime_Delta = lltype.Ptr(PyDeltaObjectStruct)
+PyDateTime_Time = cts.gettype('PyDateTime_Time*')
+PyDateTime_DateTime = cts.gettype('PyDateTime_DateTime*')
+PyDateTime_Date = cts.gettype('PyDateTime_Date*')
+PyDateTime_Delta = cts.gettype('PyDateTime_Delta*')
 
 # Check functions
 
@@ -102,6 +79,8 @@
             return space.is_true(
                 space.appexec([w_obj], """(obj):
                     from datetime import %s as datatype
+                    if not isinstance(obj, datatype):
+                        print datatype
                     return isinstance(obj, datatype)
                     """ % (type_name,)))
         except OperationError:
@@ -129,6 +108,72 @@
 PyTZInfo_Check, PyTZInfo_CheckExact = make_check_function(
     "PyTZInfo_Check", "tzinfo")
 
+@bootstrap_function
+def init_datetime(space):
+    # no realize functions since there are no getters
+    make_typedescr(W_DateTime_Time.typedef,
+                   basestruct=PyDateTime_Time.TO,
+                   attach=type_attach,
+                   dealloc=type_dealloc,
+                  )
+
+    # why do we need date_dealloc? Since W_DateTime_Date is the base class for
+    # app level datetime.date. If a c-extension class uses datetime.date for 
its
+    # base class and defines a tp_dealloc, we will get this:
+    # c_class->tp_dealloc == tp_dealloc_func
+    # c_class->tp_base == datetime.date, 
+    #                     datetime.date->tp_dealloc = _PyPy_subtype_dealloc
+    # datetime.date->tp_base = W_DateTime_Date
+    #                    W_DateTime_Date->tp_dealloc = _PyPy_subtype_dealloc
+    # but _PyPy_subtype_dealloc will call tp_dealloc_func, which can call its
+    # base's tp_dealloc and we get recursion. So break the recursion by setting
+    # W_DateTime_Date->tp_dealloc
+    make_typedescr(W_DateTime_Date.typedef,
+                   basestruct=PyDateTime_DateTime.TO,
+                   dealloc=date_dealloc,
+                  )
+
+    make_typedescr(W_DateTime_Delta.typedef,
+                   basestruct=PyDateTime_Delta.TO,
+                   attach=timedeltatype_attach,
+                  )
+
+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
+    '''
+    py_datetime = rffi.cast(PyDateTime_Time, py_obj)
+    w_tzinfo = space.getattr(w_obj, space.newtext('tzinfo'))
+    if space.is_none(w_tzinfo):
+        py_datetime.c_hastzinfo = cts.cast('unsigned char', 0)
+        py_datetime.c_tzinfo = lltype.nullptr(PyObject.TO)
+    else:
+        py_datetime.c_hastzinfo = cts.cast('unsigned char', 1)
+        py_datetime.c_tzinfo = make_ref(space, w_tzinfo)
+
+@slot_function([PyObject], lltype.Void)
+def type_dealloc(space, py_obj):
+    py_datetime = rffi.cast(PyDateTime_Time, py_obj)
+    if (widen(py_datetime.c_hastzinfo) != 0):
+        decref(space, py_datetime.c_tzinfo)
+    from pypy.module.cpyext.object import _dealloc
+    _dealloc(space, py_obj)
+
+@slot_function([PyObject], lltype.Void)
+def date_dealloc(space, py_obj):
+    from pypy.module.cpyext.object import _dealloc
+    _dealloc(space, py_obj)
+
+def timedeltatype_attach(space, py_obj, w_obj, w_userdata=None):
+    "Fills a newly allocated py_obj from the w_obj"
+    py_delta = rffi.cast(PyDateTime_Delta, py_obj)
+    days = space.int_w(space.getattr(w_obj, space.newtext('days')))
+    py_delta.c_days = cts.cast('int', days)
+    seconds = space.int_w(space.getattr(w_obj, space.newtext('seconds')))
+    py_delta.c_seconds = cts.cast('int', seconds)
+    microseconds = space.int_w(space.getattr(w_obj, 
space.newtext('microseconds')))
+    py_delta.c_microseconds = cts.cast('int', microseconds)
+
 # Constructors. They are better used as macros.
 
 @cpython_api([rffi.INT_real, rffi.INT_real, rffi.INT_real, PyTypeObjectPtr],
diff --git a/pypy/module/cpyext/include/datetime.h 
b/pypy/module/cpyext/include/datetime.h
--- a/pypy/module/cpyext/include/datetime.h
+++ b/pypy/module/cpyext/include/datetime.h
@@ -4,49 +4,11 @@
 extern "C" {
 #endif
 
-/* Define structure for C API. */
-typedef struct {
-    /* type objects */
-    PyTypeObject *DateType;
-    PyTypeObject *DateTimeType;
-    PyTypeObject *TimeType;
-    PyTypeObject *DeltaType;
-    PyTypeObject *TZInfoType;
-
-    /* constructors */
-    PyObject *(*Date_FromDate)(int, int, int, PyTypeObject*);
-    PyObject *(*DateTime_FromDateAndTime)(int, int, int, int, int, int, int,
-        PyObject*, PyTypeObject*);
-    PyObject *(*Time_FromTime)(int, int, int, int, PyObject*, PyTypeObject*);
-    PyObject *(*Delta_FromDelta)(int, int, int, int, PyTypeObject*);
-} PyDateTime_CAPI;
+#include "cpyext_datetime.h"
 
 PyAPI_DATA(PyDateTime_CAPI*) PyDateTimeAPI;
-#define PyDateTime_IMPORT                           \
-    do {                                            \
-        if(PyDateTimeAPI==NULL)                     \
-            PyDateTimeAPI = _PyDateTime_Import();   \
-    } while (0)
 
-typedef struct {
-    PyObject_HEAD
-} PyDateTime_Delta;
-
-typedef struct {
-    PyObject_HEAD
-} PyDateTime_Date;
-
-typedef struct {
-    PyObject_HEAD
-} PyDateTime_Time;
-
-typedef struct {
-    PyObject_HEAD
-} PyDateTime_DateTime;
-
-typedef struct {
-    PyObject_HEAD
-} PyDateTime_TZInfo;
+#define PyDateTime_IMPORT (PyDateTimeAPI = _PyDateTime_Import())
 
 /* Macros for accessing constructors in a simplified fashion. */
 #define PyDate_FromDate(year, month, day) \
diff --git a/pypy/module/cpyext/parse/cpyext_datetime.h 
b/pypy/module/cpyext/parse/cpyext_datetime.h
new file mode 100644
--- /dev/null
+++ b/pypy/module/cpyext/parse/cpyext_datetime.h
@@ -0,0 +1,52 @@
+/* Define structure for C API. */
+typedef struct {
+    /* type objects */
+    PyTypeObject *DateType;
+    PyTypeObject *DateTimeType;
+    PyTypeObject *TimeType;
+    PyTypeObject *DeltaType;
+    PyTypeObject *TZInfoType;
+
+    /* constructors */
+    PyObject *(*Date_FromDate)(int, int, int, PyTypeObject*);
+    PyObject *(*DateTime_FromDateAndTime)(int, int, int, int, int, int, int,
+        PyObject*, PyTypeObject*);
+    PyObject *(*Time_FromTime)(int, int, int, int, PyObject*, PyTypeObject*);
+    PyObject *(*Delta_FromDelta)(int, int, int, int, PyTypeObject*);
+} PyDateTime_CAPI;
+
+typedef struct
+{
+    PyObject_HEAD
+    int days;                   /* -MAX_DELTA_DAYS <= days <= MAX_DELTA_DAYS */
+    int seconds;                /* 0 <= seconds < 24*3600 is invariant */
+    int microseconds;           /* 0 <= microseconds < 1000000 is invariant */
+} PyDateTime_Delta;
+
+/* The datetime and time types have an optional tzinfo member,
+ * PyNone if hastzinfo is false.
+ */
+typedef struct
+{
+    PyObject_HEAD
+    unsigned char hastzinfo;
+    PyObject *tzinfo;
+} PyDateTime_Time;
+
+typedef struct
+{
+    PyObject_HEAD
+    unsigned char hastzinfo;
+    PyObject *tzinfo;
+} PyDateTime_DateTime;
+
+
+typedef struct {
+    PyObject_HEAD
+} PyDateTime_Date;
+
+
+typedef struct {
+    PyObject_HEAD
+} PyDateTime_TZInfo;
+
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to