[Git][debian-gis-team/cftime][upstream] New upstream version 1.0.4

Bas Couwenberg gitlab at salsa.debian.org
Tue Oct 22 05:32:54 BST 2019



Bas Couwenberg pushed to branch upstream at Debian GIS Project / cftime


Commits:
973f9a06 by Bas Couwenberg at 2019-10-22T04:13:31Z
New upstream version 1.0.4
- - - - -


4 changed files:

- .appveyor.yml
- README.md
- cftime/_cftime.pyx
- test/test_cftime.py


Changes:

=====================================
.appveyor.yml
=====================================
@@ -2,46 +2,44 @@ environment:
   CONDA_INSTALL_LOCN: C:\\Miniconda36-x64
   matrix:
     - TARGET_ARCH: x64
-      NPY: 1.15
+      NPY: 1.16
       PY: 3.6
 
     - TARGET_ARCH: x64
-      NPY: 1.15
+      NPY: 1.16
       PY: 3.7
 
 platform:
     - x64
 
 install:
-    # If there is a newer build queued for the same PR, cancel this one.
-    # The AppVeyor 'rollout builds' option is supposed to serve the same
-    # purpose but it is problematic because it tends to cancel builds pushed
-    # directly to master instead of just PR builds (or the converse).
-    # credits: JuliaLang developers.
-    - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
-         https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
-         Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
-           throw "There are newer queued builds for this pull request, failing early." }
-
-    # Add path, activate `conda` and update conda.
-    - cmd: call %CONDA_INSTALL_LOCN%\Scripts\activate.bat
-    - cmd: conda.exe config --set always_yes yes --set changeps1 no --set show_channel_urls true
-    - cmd: conda.exe update conda
-    - cmd: conda.exe config --remove channels defaults --force
-    - cmd: conda.exe config --add channels conda-forge --force
-
-    - cmd: set PYTHONUNBUFFERED=1
-
-    - cmd: conda.exe install conda-build vs2008_express_vc_python_patch
-    - cmd: call setup_x64
-    - cmd: conda.exe info --all
-    - cmd: conda.exe list
+  # If there is a newer build queued for the same PR, cancel this one.
+  # The AppVeyor 'rollout builds' option is supposed to serve the same
+  # purpose but it is problematic because it tends to cancel builds pushed
+  # directly to master instead of just PR builds (or the converse).
+  # credits: JuliaLang developers.
+  - ps: if ($env:APPVEYOR_PULL_REQUEST_NUMBER -and $env:APPVEYOR_BUILD_NUMBER -ne ((Invoke-RestMethod `
+        https://ci.appveyor.com/api/projects/$env:APPVEYOR_ACCOUNT_NAME/$env:APPVEYOR_PROJECT_SLUG/history?recordsNumber=50).builds | `
+        Where-Object pullRequestId -eq $env:APPVEYOR_PULL_REQUEST_NUMBER)[0].buildNumber) { `
+        throw "There are newer queued builds for this pull request, failing early." }
+
+  # Add path, activate `conda` and update conda.
+  - cmd: call %CONDA_INSTALL_LOCN%\Scripts\activate.bat
+  - cmd: conda config --set always_yes yes --set changeps1 no --set show_channel_urls true
+  - cmd: conda update conda
+  - cmd: conda config --add channels conda-forge --force
+  - cmd: conda config --set channel_priority strict
+  - cmd: set PYTHONUNBUFFERED=1
+  - cmd: conda install conda-build vs2008_express_vc_python_patch
+  - cmd: call setup_x64
+
+  - cmd: conda.exe create --name TEST python=%PY% numpy=%NPY% cython pip pytest pytest-cov
+  - cmd: conda info --all
+  - cmd: conda activate TEST
 
 # Skip .NET project specific build phase.
 build: off
 
 test_script:
-    - conda.exe create --name TEST python=%PY% numpy=%NPY% cython pip pytest pytest-cov
-    - conda activate TEST
     - python -m pip install . --no-deps --ignore-installed --no-cache-dir -vvv
     - py.test -vv test


=====================================
README.md
=====================================
@@ -10,6 +10,8 @@ Time-handling functionality from netcdf4-python
 [![Commits Status](https://img.shields.io/github/commits-since/UniData/cftime/latest.svg)](https://github.com/UniData/cftime/commits/master)
 
 ## News
+10/21/2019:  version 1.0.4 released.
+
 12/05/2018:  version 1.0.3.4 released (just to fix a problem with the source 
 tarball on pypi).
 


=====================================
cftime/_cftime.pyx
=====================================
@@ -17,8 +17,9 @@ try:
 except ImportError:  # python 3.x
     pass
 
+
 microsec_units = ['microseconds','microsecond', 'microsec', 'microsecs']
-millisec_units = ['milliseconds', 'millisecond', 'millisec', 'millisecs']
+millisec_units = ['milliseconds', 'millisecond', 'millisec', 'millisecs', 'msec', 'msecs', 'ms']
 sec_units =      ['second', 'seconds', 'sec', 'secs', 's']
 min_units =      ['minute', 'minutes', 'min', 'mins']
 hr_units =       ['hour', 'hours', 'hr', 'hrs', 'h']
@@ -42,7 +43,7 @@ cdef int[13] _spm_366day = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 33
 _rop_lookup = {Py_LT: '__gt__', Py_LE: '__ge__', Py_EQ: '__eq__',
                Py_GT: '__lt__', Py_GE: '__le__', Py_NE: '__ne__'}
 
-__version__ = '1.0.3.4'
+__version__ = '1.0.4'
 
 # Adapted from http://delete.me.uk/2005/03/iso8601.html
 # Note: This regex ensures that all ISO8601 timezone formats are accepted - but, due to legacy support for other timestrings, not all incorrect formats can be rejected.
@@ -93,7 +94,7 @@ def _dateparse(timestr):
     (units, isostring) = _datesplit(timestr)
 
     # parse the date string.
-    year, month, day, hour, minute, second, utc_offset =\
+    year, month, day, hour, minute, second, microsecond, utc_offset =\
         _parse_date( isostring.strip() )
     if year >= MINYEAR:
         basedate = real_datetime(year, month, day, hour, minute, second)
@@ -121,7 +122,7 @@ cdef _parse_date_and_units(timestr,calendar='standard'):
             raise ValueError(
             "units must be one of 'seconds', 'minutes', 'hours' or 'days' (or singular version of these), got '%s'" % units)
     # parse the date string.
-    year, month, day, hour, minute, second, utc_offset = _parse_date(
+    year, month, day, hour, minute, second, microsecond, utc_offset = _parse_date(
         isostring.strip())
     return units, utc_offset, datetime(year, month, day, hour, minute, second)
 
@@ -182,6 +183,9 @@ def date2num(dates,units,calendar='standard'):
                 ismasked = True
             times = []
             for date in dates.flat:
+                if getattr(date, 'tzinfo') is not None:
+                    date = date.replace(tzinfo=None) - date.utcoffset()
+
                 if ismasked and not date:
                     times.append(None)
                 else:
@@ -408,6 +412,9 @@ def JulianDayFromDate(date, calendar='standard'):
     cdef Py_ssize_t i
     for i in range(i_max):
         d = date[i]
+        if getattr(d, 'tzinfo', None) is not None:
+            d = d.replace(tzinfo=None) - d.utcoffset()
+
         year[i] = d.year
         month[i] = d.month
         day[i] = d.day
@@ -909,7 +916,12 @@ cpdef _parse_date(datestring):
     The timezone is parsed from the date string, assuming UTC
     by default.
 
+    Note that a seconds element with a fractional component
+    (e.g. 12.5) is converted into integer seconds and integer
+    microseconds.
+
     Adapted from pyiso8601 (http://code.google.com/p/pyiso8601/)
+
     """
     if not isinstance(datestring, str) and not isinstance(datestring, unicode):
         raise ValueError("Expecting a string %r" % datestring)
@@ -924,13 +936,14 @@ cpdef _parse_date(datestring):
         groups["minute"] = 0
     if groups["second"] is None:
         groups["second"] = 0
-    # if groups["fraction"] is None:
-    #    groups["fraction"] = 0
-    # else:
-    #    groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
+    if groups["fraction"] is None:
+        groups["fraction"] = 0
+    else:
+        groups["fraction"] = int(float("0.%s" % groups["fraction"]) * 1e6)
     iyear = int(groups["year"])
     return iyear, int(groups["month"]), int(groups["day"]),\
         int(groups["hour"]), int(groups["minute"]), int(groups["second"]),\
+        int(groups["fraction"]),\
         tzoffset_mins
 
 cdef _check_index(indices, times, nctime, calendar, select):
@@ -1220,9 +1233,11 @@ Gregorial calendar.
                 "hour": self.hour,
                 "minute": self.minute,
                 "second": self.second,
-                "microsecond": self.microsecond,
-                "dayofwk": self.dayofwk,
-                "dayofyr": self.dayofyr}
+                "microsecond": self.microsecond}
+
+        if 'dayofyr' in kwargs or 'dayofwk' in kwargs:
+            raise ValueError('Replacing the dayofyr or dayofwk of a datetime is '
+                             'not supported.')
 
         for name, value in kwargs.items():
             args[name] = value
@@ -1246,13 +1261,17 @@ Gregorial calendar.
                              self.microsecond)
 
     def __repr__(self):
-        return "{0}.{1}{2}".format('cftime',
-                                   self.__class__.__name__,
-                                   self._getstate())
+        return "{0}.{1}({2})".format('cftime',
+                                     self.__class__.__name__,
+                                     str(self))
 
     def __str__(self):
-        return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}".format(
-            self.year, self.month, self.day, self.hour, self.minute, self.second)
+        second = '{:02d}'.format(self.second)
+        if self.microsecond:
+            second += '.{}'.format(self.microsecond)
+
+        return "{:04d}-{:02d}-{:02d} {:02d}:{:02d}:{}".format(
+            self.year, self.month, self.day, self.hour, self.minute, second)
 
     def __hash__(self):
         try:


=====================================
test/test_cftime.py
=====================================
@@ -19,6 +19,33 @@ from cftime import (DateFromJulianDay, Datetime360Day, DatetimeAllLeap,
                     DatetimeProlepticGregorian, JulianDayFromDate, _parse_date,
                     date2index, date2num, num2date, utime)
 
+try:
+    from datetime import timezone
+except ImportError: # python2.7
+    from datetime import tzinfo
+    class timezone(tzinfo):
+        """
+        Fixed offset in minutes east from UTC. adapted from
+        python 2.7 docs FixedOffset
+        """
+
+        def __init__(self, offset, name):
+            self.__offset = offset
+            self.__name = name
+
+        def utcoffset(self, dt):
+            return self.__offset
+
+        def tzname(self, dt):
+            return self.__name
+
+        def dst(self, dt):
+            return timedelta(hours=0)
+
+
+utc = timezone(timedelta(hours=0), 'UTC')
+est = timezone(timedelta(hours=-5), 'UTC')
+
 # test cftime module for netCDF time <--> python datetime conversions.
 
 dtime = namedtuple('dtime', ('values', 'units', 'calendar'))
@@ -81,7 +108,20 @@ class cftimeTestCase(unittest.TestCase):
         self.cdftime_noleap_capcal = utime(
             'days since 1600-02-28 00:00:00', calendar='NOLEAP')
 
-    def runTest(self):
+    def test_tz_aware(self):
+        """testing with timezone"""
+        self.assertTrue(self.cdftime_mixed.units == 'hours')
+        d1 = datetime(1582, 10, 4, 23, tzinfo=utc)
+        t1 = self.cdftime_mixed.date2num(d1)
+        d2 = datetime(1582, 10, 4, 18, tzinfo=est)
+        t2 = self.cdftime_mixed.date2num(d2)
+        d3 = d2.replace(tzinfo=None)
+        t3 = self.cdftime_mixed.date2num(d3)
+        assert_almost_equal(t1, 13865687.0)
+        assert_almost_equal(t2, 13865687.0)
+        assert_almost_equal(t3, 13865682.0)
+
+    def test_tz_naive(self):
         """testing cftime"""
         # test mixed julian/gregorian calendar
         # check attributes.
@@ -734,6 +774,15 @@ class TestDate2index(unittest.TestCase):
             values=date2num(dates, units),
             units=units)
 
+    def test_tz_aware(self):
+        """implicit test of date2num"""
+        dutc = datetime(1950, 2, 1, 0, tzinfo=utc)
+        t1 = date2index(dutc, self.standardtime)
+        assert_equal(t1, 31)
+        dest = datetime(1950, 1, 31, 19, tzinfo=est)
+        t2 = date2index(dest, self.standardtime)
+        assert_equal(t2, 31)
+
     def test_simple(self):
         t = date2index(datetime(1950, 2, 1), self.standardtime)
         assert_equal(t, 31)
@@ -1043,8 +1092,6 @@ class DateTime(unittest.TestCase):
         self.assertEqual(self.date1_365_day.replace(minute=3).minute, 3)
         self.assertEqual(self.date1_365_day.replace(second=3).second, 3)
         self.assertEqual(self.date1_365_day.replace(microsecond=3).microsecond, 3)
-        self.assertEqual(self.date1_365_day.replace(dayofwk=3).dayofwk, 3)
-        self.assertEqual(self.date1_365_day.replace(dayofyr=3).dayofyr, 3)
 
     def test_pickling(self):
         "Test reversibility of pickling."
@@ -1186,25 +1233,25 @@ class issue17TestCase(unittest.TestCase):
         "Test timezone parsing in _parse_date"
 
         # these should succeed and are ISO8601 compliant
-        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 60.0)
+        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 60.0)
         for datestr in ("2017-05-01 00:00+01:00", "2017-05-01 00:00+0100", "2017-05-01 00:00+01"):
             d = _parse_date(datestr)
             assert_equal(d, expected_parsed_date)
         # some more tests with non-zero minutes, should all be ISO compliant and work
-        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 85.0)
+        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 85.0)
         for datestr in ("2017-05-01 00:00+01:25", "2017-05-01 00:00+0125"):
             d = _parse_date(datestr)
             assert_equal(d, expected_parsed_date)
         # these are NOT ISO8601 compliant and should not even be parseable but will be parsed with timezone anyway
         # because, due to support of other legacy time formats, they are difficult to reject
         # ATTENTION: only the hours part of this will be parsed, single-digit minutes will be ignored!
-        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 60.0)
+        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 60.0)
         for datestr in ("2017-05-01 00:00+01:0", "2017-05-01 00:00+01:", "2017-05-01 00:00+01:5"):
             d = _parse_date(datestr)
             assert_equal(d, expected_parsed_date)
         # these should not even be parseable as datestrings but are parseable anyway with ignored timezone
         # this is because the module also supports some legacy, non-standard time strings
-        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0.0)
+        expected_parsed_date = (2017, 5, 1, 0, 0, 0, 0, 0.0)
         for datestr in ("2017-05-01 00:00+1",):
             d = _parse_date(datestr)
             assert_equal(d, expected_parsed_date)
@@ -1368,8 +1415,9 @@ def test_valid_julian_gregorian_mixed_dates(date_type, date_args):
 @pytest.mark.parametrize(
     'date_args',
     [(1, 2, 3, 4, 5, 6), (10, 2, 3, 4, 5, 6), (100, 2, 3, 4, 5, 6),
-     (1000, 2, 3, 4, 5, 6)],
-    ids=['1', '10', '100', '1000'])
+     (1000, 2, 3, 4, 5, 6),
+     (2000, 1, 1, 12, 34, 56, 123456)],
+    ids=['1', '10', '100', '1000', '2000'])
 def test_str_matches_datetime_str(date_type, date_args):
     assert str(date_type(*date_args)) == str(datetime(*date_args))
 
@@ -1419,13 +1467,28 @@ def test_num2date_only_use_cftime_datetimes_post_gregorian(
 
 
 def test_repr():
-    expected = 'cftime.datetime(2000, 1, 1, 0, 0, 0, 0, -1, 1)'
-    # dayofwk, dayofyr not set
+    expected = 'cftime.datetime(2000-01-01 00:00:00)'
     assert repr(datetimex(2000, 1, 1)) == expected
 
-    expected = 'cftime.DatetimeGregorian(2000, 1, 1, 0, 0, 0, 0, 5, 1)'
-    # dayofwk, dayofyr are set
-    assert repr(DatetimeGregorian(2000, 1, 1)) == expected
+
+def test_dayofyr_after_replace(date_type):
+    date = date_type(1, 1, 1)
+    assert date.dayofyr == 1
+    assert date.replace(day=2).dayofyr == 2
+
+
+def test_dayofwk_after_replace(date_type):
+    date = date_type(1, 1, 1)
+    original_dayofwk = date.dayofwk
+    expected = (original_dayofwk + 1) % 7
+    result = date.replace(day=2).dayofwk
+    assert result == expected
+
+
+ at pytest.mark.parametrize('argument', ['dayofyr', 'dayofwk'])
+def test_replace_dayofyr_or_dayofwk_error(date_type, argument):
+    with pytest.raises(ValueError):
+        date_type(1, 1, 1).replace(**{argument: 3})
 
 
 if __name__ == '__main__':



View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/commit/973f9a0695131fa7a39d7b6cf4d63bb31af66394

-- 
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/commit/973f9a0695131fa7a39d7b6cf4d63bb31af66394
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20191022/f7ad1854/attachment-0001.html>


More information about the Pkg-grass-devel mailing list