[pypy-commit] pypy py3.5-time: extend structseqtype to allow invisible fields (see struct_time, tm_zone, tm_gmtoff)

plan_rich pypy.commits at gmail.com
Tue Jan 3 07:53:44 EST 2017


Author: Richard Plangger <planrichi at gmail.com>
Branch: py3.5-time
Changeset: r89333:08e4f8a740d5
Date: 2017-01-03 13:52 +0100
http://bitbucket.org/pypy/pypy/changeset/08e4f8a740d5/

Log:	extend structseqtype to allow invisible fields (see struct_time,
	tm_zone, tm_gmtoff)

diff --git a/lib_pypy/_structseq.py b/lib_pypy/_structseq.py
--- a/lib_pypy/_structseq.py
+++ b/lib_pypy/_structseq.py
@@ -41,22 +41,31 @@
                 assert field._index not in fields_by_index
                 fields_by_index[field._index] = field
                 field.__name__ = name
-        dict['n_fields'] = len(fields_by_index)
+        n_fields = len(fields_by_index)
+        dict['n_fields'] = n_fields
 
         extra_fields = sorted(fields_by_index.items())
         n_sequence_fields = 0
-        while extra_fields and extra_fields[0][0] == n_sequence_fields:
-            extra_fields.pop(0)
-            n_sequence_fields += 1
+        extra_off = 0
+        if 'n_sequence_fields' in dict:
+            n_sequence_fields = dict['n_sequence_fields']
+            extra_fields = extra_fields[:n_sequence_fields]
+            extra_off = n_fields - n_sequence_fields
+        else:
+            while extra_fields and extra_fields[0][0] == n_sequence_fields:
+                extra_fields.pop(0)
+                n_sequence_fields += 1
+
         dict['n_sequence_fields'] = n_sequence_fields
         dict['n_unnamed_fields'] = 0     # no fully anonymous fields in PyPy
 
-        extra_fields = [field for index, field in extra_fields]
-        for field in extra_fields:
+        extra_fields = [field for index, field in extra_fields[extra_off:]]
+        for i,field in enumerate(extra_fields):
             field.index = None     # no longer relevant
 
         assert '__new__' not in dict
         dict['_extra_fields'] = tuple(extra_fields)
+        dict['_extra_off'] = extra_off
         dict['__new__'] = structseq_new
         dict['__reduce__'] = structseq_reduce
         dict['__setattr__'] = structseq_setattr
@@ -70,34 +79,40 @@
 def structseq_new(cls, sequence, dict={}):
     sequence = tuple(sequence)
     dict = builtin_dict(dict)
-    N = cls.n_sequence_fields
-    if len(sequence) < N:
-        if N < cls.n_fields:
+    # visible fields
+    visible_count = cls.n_sequence_fields
+    # total fields (unnamed are not yet supported, extra fields not included)
+    real_count = cls.n_fields
+    length = len(sequence)
+    if length < visible_count:
+        if visible_count < real_count:
             msg = "at least"
         else:
             msg = "exactly"
-        raise TypeError("expected a sequence with %s %d items" % (
-            msg, N))
-    if len(sequence) > N:
-        if len(sequence) > cls.n_fields:
-            if N < cls.n_fields:
+        raise TypeError("expected a sequence with %s %d items. has %d 1" % (
+            msg, visible_count, length))
+    if length > visible_count:
+        if length > real_count:
+            if visible_count < real_count:
                 msg = "at most"
             else:
                 msg = "exactly"
-            raise TypeError("expected a sequence with %s %d items" % (
-                msg, cls.n_fields))
-        for field, value in zip(cls._extra_fields, sequence[N:]):
+            raise TypeError("expected a sequence with %s %d items. has %d 2" \
+                            % (msg, real_count, length))
+        for field, value in zip(cls._extra_fields, sequence[visible_count:]):
             name = field.__name__
             if name in dict:
                 raise TypeError("duplicate value for %r" % (name,))
             dict[name] = value
-        sequence = sequence[:N]
+        extra_off = cls._extra_off
+        sequence = sequence[:visible_count+extra_off]
     result = tuple.__new__(cls, sequence)
     object.__setattr__(result, '__dict__', dict)
     for field in cls._extra_fields:
         name = field.__name__
         if name not in dict:
             dict[name] = field._default(result)
+
     return result
 
 def structseq_reduce(self):
diff --git a/pypy/module/time/app_time.py b/pypy/module/time/app_time.py
--- a/pypy/module/time/app_time.py
+++ b/pypy/module/time/app_time.py
@@ -8,17 +8,21 @@
     __module__ = 'time'
     name = 'time.struct_time'
 
-    tm_year   = structseqfield(0)
-    tm_mon    = structseqfield(1)
-    tm_mday   = structseqfield(2)
-    tm_hour   = structseqfield(3)
-    tm_min    = structseqfield(4)
-    tm_sec    = structseqfield(5)
-    tm_wday   = structseqfield(6)
-    tm_yday   = structseqfield(7)
-    tm_isdst  = structseqfield(8)
-    tm_gmtoff = structseqfield(9)
-    tm_zone   = structseqfield(10)
+    n_sequence_fields = 9
+
+    tm_year   = structseqfield(0, "year, for example, 1993")
+    tm_mon    = structseqfield(1, "month of year, range [1, 12]")
+    tm_mday   = structseqfield(2, "day of month, range [1, 31]")
+    tm_hour   = structseqfield(3, "hours, range [0, 23]")
+    tm_min    = structseqfield(4, "minutes, range [0, 59]")
+    tm_sec    = structseqfield(5, "seconds, range [0, 61])")
+    tm_wday   = structseqfield(6, "day of week, range [0, 6], Monday is 0")
+    tm_yday   = structseqfield(7, "day of year, range [1, 366]")
+    tm_isdst  = structseqfield(8, "1 if summer time is in effect, 0 if not"
+                                  ", and -1 if unknown")
+    tm_zone   = structseqfield(9, "abbreviation of timezone name")
+    tm_gmtoff = structseqfield(10,"offset from UTC in seconds")
+
 
 def strptime(string, format="%a %b %d %H:%M:%S %Y"):
     """strptime(string, format) -> struct_time
diff --git a/pypy/module/time/interp_time.py b/pypy/module/time/interp_time.py
--- a/pypy/module/time/interp_time.py
+++ b/pypy/module/time/interp_time.py
@@ -1,8 +1,10 @@
 from rpython.rtyper.tool import rffi_platform as platform
 from rpython.rtyper.lltypesystem import rffi
-from pypy.interpreter.error import OperationError, oefmt, strerror as _strerror, exception_from_saved_errno
+from pypy.interpreter.error import (OperationError, oefmt,
+        strerror as _strerror, exception_from_saved_errno)
 from pypy.interpreter.gateway import unwrap_spec
 from pypy.interpreter import timeutils
+from pypy.interpreter.unicodehelper import decode_utf8, encode_utf8
 from rpython.rtyper.lltypesystem import lltype
 from rpython.rlib.rarithmetic import intmask, r_ulonglong, r_longfloat, widen
 from rpython.rlib.rtime import (GETTIMEOFDAY_NO_TZ, TIMEVAL,
@@ -200,6 +202,8 @@
 
 # XXX: optionally support the 2 additional tz fields
 _STRUCT_TM_ITEMS = 9
+if HAS_TM_ZONE:
+    _STRUCT_TM_ITEMS = 11
 
 class cConfig:
     pass
@@ -542,12 +546,14 @@
         space.wrap(rffi.getintfield(t, 'c_tm_yday') + 1), # want january, 1 == 1
         space.wrap(rffi.getintfield(t, 'c_tm_isdst'))]
 
+    if HAS_TM_ZONE:
+        # CPython calls PyUnicode_DecodeLocale here should we do the same?
+        tm_zone = decode_utf8(space, rffi.charp2str(t.c_tm_zone), allow_surrogates=True)
+        time_tuple.append(space.newunicode(tm_zone))
+        time_tuple.append(space.wrap(rffi.getintfield(t, 'c_tm_gmtoff')))
     w_struct_time = _get_module_object(space, 'struct_time')
     w_time_tuple = space.newtuple(time_tuple)
     w_obj = space.call_function(w_struct_time, w_time_tuple)
-    if HAS_TM_ZONE:
-        space.setattr(w_obj, space.wrap("tm_gmoff"), space.wrap(rffi.getintfield(t, 'c_tm_gmtoff')))
-        space.setattr(w_obj, space.wrap("tm_zone"), space.wrap(rffi.getintfield(t, 'c_tm_zone')))
     return w_obj
 
 def _gettmarg(space, w_tup, allowNone=True):
@@ -568,9 +574,9 @@
         return pbuf
 
     tup_w = space.fixedview(w_tup)
-    if len(tup_w) != 9:
+    if len(tup_w) < 9:
         raise oefmt(space.w_TypeError,
-                    "argument must be sequence of length 9, not %d",
+                    "argument must be sequence of at least length 9, not %d",
                     len(tup_w))
 
     y = space.c_int_w(tup_w[0])
@@ -591,13 +597,16 @@
     rffi.setintfield(glob_buf, 'c_tm_wday', space.c_int_w(tup_w[6]))
     rffi.setintfield(glob_buf, 'c_tm_yday', tm_yday)
     rffi.setintfield(glob_buf, 'c_tm_isdst', space.c_int_w(tup_w[8]))
-    if _POSIX:
-        if _CYGWIN:
-            pass
-        else:
-            # actually never happens, but makes annotator happy
-            glob_buf.c_tm_zone = lltype.nullptr(rffi.CCHARP.TO)
-            rffi.setintfield(glob_buf, 'c_tm_gmtoff', 0)
+    if HAS_TM_ZONE:
+        #tm_zone = encode_utf8(space, space.unicode_w(tup_w[9]), allow_surrogates=True)
+        #glob_buf.c_tm_zone = rffi.str2charp(tm_zone)
+        # TODO using str2charp allocates an object that is never freed
+        # find a solution for this memory leak
+        glob_buf.c_tm_zone = lltype.nullptr(rffi.CCHARP.TO)
+        rffi.setintfield(glob_buf, 'c_tm_gmtoff', space.c_int_w(tup_w[10]))
+    else:
+        glob_buf.c_tm_zone = lltype.nullptr(rffi.CCHARP.TO)
+        rffi.setintfield(glob_buf, 'c_tm_gmtoff', 0)
 
     # tm_wday does not need checking of its upper-bound since taking "%
     #  7" in _gettmarg() automatically restricts the range.


More information about the pypy-commit mailing list