[pypy-commit] pypy py3.5: merged py3.5-time changes

plan_rich pypy.commits at gmail.com
Mon Jan 9 09:09:30 EST 2017


Author: Richard Plangger <planrichi at gmail.com>
Branch: py3.5
Changeset: r89438:f58422e7ce44
Date: 2017-01-09 15:07 +0100
http://bitbucket.org/pypy/pypy/changeset/f58422e7ce44/

Log:	merged py3.5-time changes

diff --git a/lib_pypy/_structseq.py b/lib_pypy/_structseq.py
--- a/lib_pypy/_structseq.py
+++ b/lib_pypy/_structseq.py
@@ -41,18 +41,25 @@
                 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
+        invis_fields = []
+        if 'n_sequence_fields' in dict:
+            n_sequence_fields = dict['n_sequence_fields']
+            extra_fields = extra_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:
+        for i,field in enumerate(extra_fields):
             field.index = None     # no longer relevant
 
         assert '__new__' not in dict
@@ -70,34 +77,39 @@
 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" % (
+            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" \
+                            % (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]
+        sequence = sequence[:visible_count]
     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):
@@ -109,9 +121,11 @@
 
 def structseq_repr(self):
     fields = {}
+    visible_count = self.n_sequence_fields
     for field in type(self).__dict__.values():
-        if isinstance(field, structseqfield):
+        if isinstance(field, structseqfield) and \
+           field._index <= visible_count:
             fields[field._index] = field
     parts = ["%s=%r" % (fields[index].__name__, value)
-             for index, value in enumerate(self)]
+            for index, value in enumerate(self[:visible_count])]
     return "%s(%s)" % (self._name, ", ".join(parts))
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
@@ -3,19 +3,26 @@
 from _structseq import structseqtype, structseqfield
 from types import SimpleNamespace
 import time
+
 class struct_time(metaclass=structseqtype):
     __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)
+    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,
@@ -164,6 +166,8 @@
     CLOCKS_PER_SEC = platform.ConstantInteger("CLOCKS_PER_SEC")
     has_gettimeofday = platform.Has('gettimeofday')
 
+HAS_TM_ZONE = False
+
 if _POSIX:
     calling_conv = 'c'
     CConfig.timeval = platform.Struct("struct timeval",
@@ -180,6 +184,9 @@
             ("tm_mon", rffi.INT), ("tm_year", rffi.INT), ("tm_wday", rffi.INT),
             ("tm_yday", rffi.INT), ("tm_isdst", rffi.INT), ("tm_gmtoff", rffi.LONG),
             ("tm_zone", rffi.CCHARP)])
+
+        HAS_TM_ZONE = True
+
 elif _WIN:
     calling_conv = 'win'
     CConfig.tm = platform.Struct("struct tm", [("tm_sec", rffi.INT),
@@ -195,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
@@ -537,9 +546,18 @@
         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)
+        extra = [space.newunicode(tm_zone),
+                 space.wrap(rffi.getintfield(t, 'c_tm_gmtoff'))]
+        w_time_tuple = space.newtuple(time_tuple + extra)
+    else:
+        w_time_tuple = space.newtuple(time_tuple)
     w_struct_time = _get_module_object(space, 'struct_time')
-    w_time_tuple = space.newtuple(time_tuple)
-    return space.call_function(w_struct_time, w_time_tuple)
+    w_obj = space.call_function(w_struct_time, w_time_tuple)
+    return w_obj
 
 def _gettmarg(space, w_tup, allowNone=True):
     if space.is_none(w_tup):
@@ -559,9 +577,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])
@@ -582,13 +600,23 @@
     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)
+    #
+    old_tm_zone = glob_buf.c_tm_zone
+    glob_buf.c_tm_zone = lltype.nullptr(rffi.CCHARP.TO)
+    rffi.setintfield(glob_buf, 'c_tm_gmtoff', 0)
+    if HAS_TM_ZONE :
+        if len(tup_w) >= 10:
+            # NOTE this is not cleanly solved!
+            # it saves the string that is later deleted when this
+            # function is called again. A refactoring of this module
+            # could remove this
+            tm_zone = encode_utf8(space, space.unicode_w(tup_w[9]), allow_surrogates=True)
+            malloced_str = rffi.str2charp(tm_zone, track_allocation=False)
+            if old_tm_zone != lltype.nullptr(rffi.CCHARP.TO):
+                rffi.free_charp(old_tm_zone, track_allocation=False)
+            glob_buf.c_tm_zone = malloced_str
+        if len(tup_w) >= 11:
+            rffi.setintfield(glob_buf, 'c_tm_gmtoff', space.c_int_w(tup_w[10]))
 
     # tm_wday does not need checking of its upper-bound since taking "%
     #  7" in _gettmarg() automatically restricts the range.
diff --git a/pypy/module/time/test/test_time.py b/pypy/module/time/test/test_time.py
--- a/pypy/module/time/test/test_time.py
+++ b/pypy/module/time/test/test_time.py
@@ -254,6 +254,22 @@
                 del os.environ['TZ']
             time.tzset()
 
+    def test_localtime_timezone(self):
+        import os, time
+        org_TZ = os.environ.get('TZ', None)
+        try:
+            os.environ['TZ'] = 'Europe/Kiev'
+            time.tzset()
+            localtm = time.localtime(0)
+            assert localtm.tm_zone == "MSK"
+            assert localtm.tm_gmtoff == 10800
+        finally:
+            if org_TZ is not None:
+                os.environ['TZ'] = org_TZ
+            elif 'TZ' in os.environ:
+                del os.environ['TZ']
+            time.tzset()
+
     def test_strftime(self):
         import time
         import os, sys


More information about the pypy-commit mailing list