[Git][debian-gis-team/cftime][master] 4 commits: New upstream version 1.6.4

Bas Couwenberg (@sebastic) gitlab at salsa.debian.org
Fri Jun 7 05:28:39 BST 2024



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


Commits:
67cdd86b by Bas Couwenberg at 2024-06-07T06:21:44+02:00
New upstream version 1.6.4
- - - - -
66798918 by Bas Couwenberg at 2024-06-07T06:21:44+02:00
Update upstream source from tag 'upstream/1.6.4'

Update to upstream version '1.6.4'
with Debian dir f90eaa59c313b7992d69ccdcac6394ab28cba501
- - - - -
e0698797 by Bas Couwenberg at 2024-06-07T06:22:04+02:00
New upstream release.

- - - - -
01e1d54f by Bas Couwenberg at 2024-06-07T06:23:05+02:00
Set distribution to unstable.

- - - - -


12 changed files:

- + .github/dependabot.yml
- .github/workflows/cibuildwheel.yml
- .github/workflows/deploy-docs.yml
- .github/workflows/publish.yml
- .github/workflows/tests_conda.yml
- .github/workflows/tests_latest.yml
- Changelog
- README.md
- debian/changelog
- pyproject.toml
- src/cftime/_cftime.pyx
- test/test_cftime.py


Changes:

=====================================
.github/dependabot.yml
=====================================
@@ -0,0 +1,13 @@
+# Keep GitHub Actions up to date with GitHub's Dependabot...
+# https://docs.github.com/en/code-security/dependabot/working-with-dependabot/keeping-your-actions-up-to-date-with-dependabot
+# https://docs.github.com/en/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file#package-ecosystem
+version: 2
+updates:
+  - package-ecosystem: github-actions
+    directory: /
+    groups:
+      github-actions:
+        patterns:
+          - "*"  # Group all Actions updates into a single larger pull request
+    schedule:
+      interval: weekly


=====================================
.github/workflows/cibuildwheel.yml
=====================================
@@ -2,7 +2,8 @@ name: Wheels
 
 on:
   pull_request:
-
+    branches:
+      - master
   push:
     tags:
       - "v*"
@@ -11,58 +12,97 @@ jobs:
   build_bdist:
     name: "Build ${{ matrix.os }} (${{ matrix.arch }}) wheels"
     runs-on: ${{ matrix.os }}
+    timeout-minutes: 60  # should be long enough even on tags, but let's prevent hangs
     strategy:
       fail-fast: false
       matrix:
-        os: ["ubuntu-latest", "windows-latest", "macos-latest"]
-        arch: ["x86_64", "arm64", "AMD64"]
-        exclude:
-        - os: ubuntu-latest
-          arch: arm64
-        - os: ubuntu-latest
-          arch: AMD64
-        - os: windows-latest
-          arch: arm64
-        - os: windows-latest
-          arch: x86_64
-        - os: macos-latest
-          arch: AMD64
+        include:
+          - os: ubuntu-22.04
+            arch: x86_64
+          - os: ubuntu-22.04
+            arch: aarch64
+          - os: windows-2022
+            arch: AMD64
+          - os: macos-14
+            arch: arm64
+          - os: macos-13
+            arch: x86_64
 
     steps:
     - uses: actions/checkout at v4
       with:
         fetch-depth: 0
 
+    # For aarch64 support
+    # https://cibuildwheel.pypa.io/en/stable/faq/#emulation
+    - uses: docker/setup-qemu-action at v3
+      with:
+        platforms: all
+      if: runner.os == 'Linux' && matrix.arch == 'aarch64'
+
+    - name: Build just oldest and newest on PRs, all on tags
+      shell: bash
+      # - Always omit musl 3.8 b/c NumPy does not provide wheels for it
+      # - Always omit musllinux_aarch64 because it's slow and niche
+      # - On PPs, omit musllinux for speed
+      # - On PRs, run just oldest and newest Python versions (and omit 3.8 aarch64)
+      run: |
+        if [[ "${{ github.event_name }}" == "pull_request" ]]; then
+          CIBW_SKIP="pp* cp36-* cp37-* cp38-musllinux* cp39-* cp310-* cp311-* cp38-*_aarch64 *musllinux*"
+        else
+          CIBW_SKIP="pp* cp36-* cp37-* cp38-musllinux* *musllinux_aarch64"
+        fi
+        echo "CIBW_SKIP=$CIBW_SKIP" >> $GITHUB_ENV
+        echo "Setting CIBW_SKIP=$CIBW_SKIP"
+
     - name: "Building ${{ matrix.os }} (${{ matrix.arch }}) wheels"
-      uses: pypa/cibuildwheel at v2.15.0
+      uses: pypa/cibuildwheel at v2.18.1
       env:
-        # Skips pypy and musllinux for now.
-        CIBW_SKIP: "pp* cp36-* cp37-* *-musllinux*"
+        # Skips pypy py36,37
+        CIBW_SKIP: ${{ env.CIBW_SKIP }}
         CIBW_ARCHS: ${{ matrix.arch }}
-        CIBW_BUILD_FRONTEND: build
         CIBW_MANYLINUX_X86_64_IMAGE: manylinux2014
-        CIBW_TEST_SKIP: "*_arm64"
+        CIBW_MANYLINUX_AARCH64_IMAGE: manylinux2014
+        # Emulated testing is slow, so trust that the Python 3.12 test is good enough on aarch64
+        # (takes about 5 minutes per wheel to build, and 5 minutes to test)
+        CIBW_TEST_SKIP: "cp38-*_aarch64 cp39-*_aarch64 cp310-*_aarch64 cp311-*_aarch64"
         CIBW_TEST_REQUIRES: pytest
         CIBW_TEST_COMMAND: >
           python -c "import cftime; print(f'cftime v{cftime.__version__}')" &&
-          python -m pip install -r {package}/requirements-dev.txt &&
+          python -m pip install check-manifest cython pytest pytest-cov &&
           python -m pytest -vv {package}/test
 
-    - uses: actions/upload-artifact at v3
+    - uses: actions/upload-artifact at v4
       with:
-        name: pypi-artifacts
+        name: pypi-artifacts-${{ matrix.os }}-${{ matrix.arch }}
         path: ${{ github.workspace }}/wheelhouse/*.whl
 
 
+  build_sdist:
+    name: Build source distribution
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout at v4
+
+      - name: Build sdist
+        run: >
+          pip install build
+          && python -m build --sdist . --outdir dist
+
+      - uses: actions/upload-artifact at v4
+        with:
+          path: dist/*.tar.gz
+
   show-artifacts:
-    needs: [build_bdist]
+    needs: [build_bdist, build_sdist]
     name: "Show artifacts"
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-22.04
     steps:
-    - uses: actions/download-artifact at v3
+    - uses: actions/download-artifact at v4
       with:
-        name: pypi-artifacts
+        pattern: pypi-artifacts*
         path: ${{ github.workspace }}/dist
+        merge-multiple: true
 
     - shell: bash
       run: |
@@ -70,16 +110,17 @@ jobs:
 
 
   publish-artifacts-pypi:
-    needs: [build_bdist]
+    needs: [build_bdist, build_sdist]
     name: "Publish to PyPI"
-    runs-on: ubuntu-latest
+    runs-on: ubuntu-22.04
     # upload to PyPI for every tag starting with 'v'
     if: github.event_name == 'push' && startsWith(github.event.ref, 'refs/tags/v')
     steps:
-    - uses: actions/download-artifact at v3
+    - uses: actions/download-artifact at v4
       with:
-        name: pypi-artifacts
+        pattern: pypi-artifacts*
         path: ${{ github.workspace }}/dist
+        merge-multiple: true
 
     - uses: pypa/gh-action-pypi-publish at release/v1
       with:


=====================================
.github/workflows/deploy-docs.yml
=====================================
@@ -43,7 +43,7 @@ jobs:
         popd
 
     - name: Deploy
-      uses: peaceiris/actions-gh-pages at v3
+      uses: peaceiris/actions-gh-pages at v4
       with:
         github_token: ${{ secrets.GITHUB_TOKEN }}
         publish_dir: docs/_build/html


=====================================
.github/workflows/publish.yml
=====================================
@@ -9,7 +9,7 @@ jobs:
     - uses: actions/checkout at v4
 
     - name: Set up Python
-      uses: actions/setup-python at v4
+      uses: actions/setup-python at v5
       with:
         python-version: 3.x
 


=====================================
.github/workflows/tests_conda.yml
=====================================
@@ -8,15 +8,21 @@ on:
 jobs:
   run:
     runs-on: ${{ matrix.os }}
+    continue-on-error: ${{ matrix.experimental }}
     strategy:
       fail-fast: false
       matrix:
         python-version: [ "3.8", "3.9", "3.10", "3.11", "3.12" ]
         os: [windows-latest, ubuntu-latest, macos-latest]
         platform: [x64, x32]
+        experimental: [false]
         exclude:
           - os: macos-latest  
             platform: x32
+        include:
+          - python-version: "3.12"
+            os: "ubuntu-latest"
+            experimental: true
 
     steps:
     - uses: actions/checkout at v4
@@ -31,11 +37,22 @@ jobs:
           cython>=0.29.20
           pytest
           pytest-cov
+    - name: Install unstable dependencies
+      if: matrix.experimental == true
+      shell: bash -l {0}
+      run: |
+        python -m pip install \
+        --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/ \
+        --trusted-host pypi.anaconda.org \
+        --no-deps --pre --upgrade \
+        numpy;
+        python -m pip install -v -e  . --no-deps --no-build-isolation --force-reinstall
 
     - name: Install cftime
+      if: matrix.experimental != true
       shell: bash -l {0}
       run: |
-        pip install -v -e  . --no-deps --force-reinstall
+        python -m pip install -v -e  . --no-deps --force-reinstall
 
     - name: Run Tests
       shell: bash -l {0}


=====================================
.github/workflows/tests_latest.yml
=====================================
@@ -6,13 +6,13 @@ jobs:
     runs-on: ubuntu-latest
     strategy:
       matrix:
-        python-version: ["3.12-dev"]
+        python-version: ["3.13.0-alpha.1"]
     steps:
 
     - uses: actions/checkout at v4
 
     - name: Set up Python ${{ matrix.python-version }}
-      uses: actions/setup-python at v4
+      uses: actions/setup-python at v5
       with:
         python-version: ${{ matrix.python-version }}
 
@@ -20,9 +20,15 @@ jobs:
       run: |
         python -m pip install --upgrade pip
 
-    - name: Install cftime dependencies via pip
+    - name: Install unstable cftime dependencies via pip
       run: |
-        python -m pip install -r requirements-dev.txt
+        python -m pip install --pre -r requirements-dev.txt
+        # get nightly wheels for numpy
+        python -m pip install \
+          --index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple/ \
+          --trusted-host pypi.anaconda.org \
+          --no-deps --pre --upgrade \
+          numpy
 
     - name: Install cftime
       run: |


=====================================
Changelog
=====================================
@@ -1,3 +1,11 @@
+version 1.6.4 (release tag v1.6.4rel)
+=====================================
+ * build aarch64 linux wheels (issue #333).
+ * build musllinux wheels (issue #307).
+ * return empty array if one provided to date2num (issue #315).
+ * numpy 2.0 compatibility (issue #325).
+ * handle nan/inf in num2date (issue #328).
+
 version 1.6.3 (release tag v1.6.3rel)
 =====================================
  * add support for formats without separators in strptime (e.g. '20200229', issue #301).


=====================================
README.md
=====================================
@@ -12,6 +12,8 @@ Time-handling functionality from netcdf4-python
 ## News
 For details on the latest updates, see the [Changelog](https://github.com/Unidata/cftime/blob/master/Changelog).
 
+6/7/2024:  Version 1.6.4 release.  Wheels for muslinux and aarch64, numpy 2.0 compatibility. 
+
 10/20/2023:  Version 1.6.3 released.  Support for python 3.12, cython 3.0, strptime formats without separators.
  
 9/18/2022:  Version 1.6.2 released.  strptime method added, fix for num2date failure on


=====================================
debian/changelog
=====================================
@@ -1,3 +1,9 @@
+cftime (1.6.4-1) unstable; urgency=medium
+
+  * New upstream release.
+
+ -- Bas Couwenberg <sebastic at debian.org>  Fri, 07 Jun 2024 06:22:55 +0200
+
 cftime (1.6.3-1) unstable; urgency=medium
 
   * New upstream release.


=====================================
pyproject.toml
=====================================
@@ -1,3 +1,9 @@
 [build-system]
-requires = ["setuptools>=41.2", "cython>=0.29.20", "wheel", "oldest-supported-numpy; python_version<'3.12.0.rc1'", "numpy>=1.26.0b1; python_version>='3.12.0.rc1'"]
+requires = [
+    "setuptools>=41.2",
+    "cython>=0.29.20",
+    "wheel",
+    "oldest-supported-numpy ; python_version < '3.9'",
+    "numpy>=2.0.0rc1,<3 ; python_version >= '3.9'",
+]
 build-backend = "setuptools.build_meta"


=====================================
src/cftime/_cftime.pyx
=====================================
@@ -7,6 +7,7 @@ from cpython.object cimport (PyObject_RichCompare, Py_LT, Py_LE, Py_EQ,
 from numpy cimport int64_t, int32_t
 import cython
 import numpy as np
+cimport numpy as np
 import re
 import time
 from datetime import datetime as datetime_python
@@ -14,6 +15,8 @@ from datetime import timedelta, MINYEAR, MAXYEAR
 import warnings
 from ._strptime import _strptime
 
+np.import_array()
+
 microsec_units = ['microseconds','microsecond', 'microsec', 'microsecs']
 millisec_units = ['milliseconds', 'millisecond', 'millisec', 'millisecs', 'msec', 'msecs', 'ms']
 sec_units =      ['second', 'seconds', 'sec', 'secs', 's']
@@ -37,7 +40,7 @@ cdef int[12] _dayspermonth_leap = [31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 3
 cdef int[13] _cumdayspermonth = [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365]
 cdef int[13] _cumdayspermonth_leap = [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366]
 
-__version__ = '1.6.3'
+__version__ = '1.6.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.
@@ -193,7 +196,11 @@ def date2num(dates, units, calendar=None, has_year_zero=None, longdouble=False):
     try:
         dates[0]
     except:
-        isscalar = True
+        if not dates:
+            # if empty list or array input, return empty array (issue #315)
+            return np.array([],dtype=float)
+        else:
+            isscalar = True
 
     # masked array input?
     ismasked = False
@@ -613,6 +620,9 @@ def num2date(
     factor = UNIT_CONVERSION_FACTORS[unit]
     times = np.asanyarray(times)  # Allow list as input
     times = upcast_times(times)
+    # convert to masked array if any nan or inf values present
+    if not np.isfinite(times).all():
+        times = np.ma.masked_invalid(times)
     scaled_times = scale_times(times, factor)
     scaled_times = cast_to_int(scaled_times,units=unit)
 


=====================================
test/test_cftime.py
=====================================
@@ -6,8 +6,6 @@ from cftime import (Datetime360Day, DatetimeAllLeap,
                     DatetimeProlepticGregorian, _parse_date,
                     date2index, date2num, num2date,  UNIT_CONVERSION_FACTORS)
 import copy
-import operator
-import sys
 import unittest
 import warnings
 from collections import namedtuple
@@ -330,17 +328,17 @@ class cftimeTestCase(unittest.TestCase):
         for t in (733498.999, 733498.9999, 733498.99999, 733498.999999, 733498.9999999):
             d = num2date(t, units='days since 0001-01-01 00:00:00')
             t2 = date2num(d, units='days since 0001-01-01 00:00:00')
-            assert(abs(t2 - t) < 1e-5)  # values should be less than second
+            assert abs(t2 - t) < 1e-5  # values should be less than second
         # Check equality testing
         d1 = datetimex(1979, 6, 21, 9, 23, 12, calendar='standard')
         d2 = datetime(1979, 6, 21, 9, 23, 12)
-        assert(d1 == d2)
+        assert d1 == d2
         # check timezone offset
         d = datetime(2012, 2, 29, 15)
         # mixed_tz is -6 hours from UTC, mixed is UTC so
         # difference in elapsed time is -6 hours.
-        assert(self.cdftime_mixed_tz.date2num(
-            d) - self.cdftime_mixed.date2num(d) == -6)
+        assert self.cdftime_mixed_tz.date2num(
+            d) - self.cdftime_mixed.date2num(d) == -6
 
         # Check comparisons with Python datetime types
 
@@ -402,7 +400,7 @@ class cftimeTestCase(unittest.TestCase):
 
         #issue 344
         units = 'hours since 2013-12-12T12:00:00'
-        assert(1.0 == date2num(num2date(1.0, units), units))
+        assert 1.0 == date2num(num2date(1.0, units), units)
 
         # test roundtrip accuracy
         # also tests error found in issue #349
@@ -416,43 +414,49 @@ class cftimeTestCase(unittest.TestCase):
             times2 = date2num(dates1,units,calendar=calendar)
             dates2 = num2date(times2,units,calendar=calendar)
             err = np.abs(times1 - times2)
-            assert(np.all(err<eps))
+            assert np.all(err<eps)
             dates1 = [date.strftime(dateformat) for date in dates1]
             dates2 = [date.strftime(dateformat) for date in dates2]
-            assert(dates1==dates2)
+            assert dates1==dates2
             return err.max()
         for calendar in calendars:
-            eps = 1.; delta = 1.
+            eps = 1.
+            delta = 1.
             units = 'microseconds since 2000-01-30 01:01:01'
             maxerr = roundtrip(eps,delta,units)
             if verbose:
                 print('calendar = %s max abs err (microsecs) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 0.001; delta = 0.001
+            eps = 0.001
+            delta = 0.001
             units = 'milliseconds since 2000-01-30 01:01:01'
             maxerr = roundtrip(eps,delta,units)
             if verbose:
                 print('calendar = %s max abs err (millisecs) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-5; delta = 0.1
+            eps = 1.e-5
+            delta = 0.1
             units = 'seconds since 0001-01-30 01:01:01'
             maxerr = roundtrip(eps,delta,units)
             if verbose:
                 print('calendar = %s max abs err (secs) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-6; delta = 0.01
+            eps = 1.e-6
+            delta = 0.01
             units = 'minutes since 0001-01-30 01:01:01'
             maxerr = roundtrip(eps,delta,units)
             if verbose:
                 print('calendar = %s max abs err (mins) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-8; delta = 0.001
+            eps = 1.e-8
+            delta = 0.001
             units = 'hours since 0001-01-30 01:01:01'
             maxerr = roundtrip(eps,delta,units)
             if verbose:
                 print('calendar = %s max abs err (hours) = %s eps = %s' % \
                      (calendar,maxerr,eps))
-            eps = 1.e-9; delta = 0.00001
+            eps = 1.e-9
+            delta = 0.00001
             units = 'days since 0001-01-30 01:01:01'
             maxerr = roundtrip(eps,delta,units)
             if verbose:
@@ -460,20 +464,19 @@ class cftimeTestCase(unittest.TestCase):
                      (calendar,maxerr,eps))
 
         # issue 353
-        assert (num2date(0, 'hours since 2000-01-01 0') ==
-                datetime(2000,1,1,0))
+        assert num2date(0, 'hours since 2000-01-01 0') == datetime(2000,1,1,0)
 
         # issue 354
         num1 = np.array([[0, 1], [2, 3]])
         num2 = np.array([[0, 1], [2, 3]])
         dates1 = num2date(num1, 'days since 0001-01-01')
         dates2 = num2date(num2, 'days since 2001-01-01')
-        assert( dates1.shape == (2,2) )
-        assert( dates2.shape == (2,2) )
+        assert  dates1.shape == (2,2)
+        assert  dates2.shape == (2,2)
         num1b = date2num(dates1, 'days since 0001-01-01')
         num2b = date2num(dates2, 'days since 2001-01-01')
-        assert( num1b.shape == (2,2) )
-        assert( num2b.shape == (2,2) )
+        assert  num1b.shape == (2,2)
+        assert  num2b.shape == (2,2)
         assert_almost_equal(num1,num1b)
         assert_almost_equal(num2,num2b)
 
@@ -484,8 +487,8 @@ class cftimeTestCase(unittest.TestCase):
         date = datetime(1682,10,15) # assumed UTC
         num = date2num(date,units)
         # UTC is 7 hours ahead of units, so num should be -7
-        assert (num == -7)
-        assert (num2date(num, units) == date)
+        assert num == -7
+        assert num2date(num, units) == date
         units = 'hours since 1482-10-15 -07:00 UTC'
         # date before gregorian switch, cftime datetime used
         date = datetime(1482,10,15)
@@ -541,7 +544,7 @@ class cftimeTestCase(unittest.TestCase):
             d = num2date(0, units, calendar="standard")
             #self.assertEqual(d, utc_date)
             # tolerance of 1.e-3 secs
-            assert(np.abs((d-utc_date).total_seconds()) < 1.e-3)
+            assert np.abs((d-utc_date).total_seconds()) < 1.e-3
             # also test with negative values to cover 2nd code path
             d = num2date(-1, units, calendar="standard")
             self.assertEqual(d, utc_date - timedelta(hours=1))
@@ -608,7 +611,7 @@ class cftimeTestCase(unittest.TestCase):
         # cftime issue #134
         d = num2date(-657071, units, calendar='proleptic_gregorian',
                      only_use_cftime_datetimes=False,only_use_python_datetimes=True)
-        assert(d==datetime(1,1,1,0))
+        assert d==datetime(1,1,1,0)
         self.assertRaises(ValueError, num2date, \
         -657072, units, calendar='proleptic_gregorian',
                      only_use_cftime_datetimes=False,only_use_python_datetimes=True)
@@ -619,11 +622,11 @@ class cftimeTestCase(unittest.TestCase):
         utc_date = datetime(2000,1,1,18,30)
         for units in ("hours since 2000-01-01 22:30+04:00", "hours since 2000-01-01 11:30-07:00", "hours since 2000-01-01 15:00-03:30"):
             d = num2date(0, units, calendar="standard")
-            assert(np.abs((d-utc_date).total_seconds()) < 1.e-3)
+            assert np.abs((d-utc_date).total_seconds()) < 1.e-3
             # also test with negative values to cover 2nd code path
             d = num2date(-1, units, calendar="standard")
-            assert(np.abs((d - \
-                (utc_date-timedelta(hours=1))).total_seconds()) < 1.e-3)
+            assert np.abs((d - \
+                (utc_date-timedelta(hours=1))).total_seconds()) < 1.e-3
 
             n = date2num(utc_date, units, calendar="standard")
             # n should always be 0 as all units refer to the same point in time
@@ -698,7 +701,7 @@ class cftimeTestCase(unittest.TestCase):
         d1 = datetimex(2020, 5, 20, dayofwk=8, dayofyr=9, calendar='')
         assert (d1.dayofwk == 8)
         assert (d1.dayofyr == 9)
-        
+
         # issue 71: negative reference years
         # https://coastwatch.pfeg.noaa.gov/erddap/convert/time.html
         # gives 2446433 (365 days more - is it counting year 0?)
@@ -722,17 +725,17 @@ class cftimeTestCase(unittest.TestCase):
         self.assertRaises(ValueError, utime, \
             'months since 01-01-01', calendar='standard')
         # issue #78 - extra digits due to roundoff
-        assert(cftime.date2num(cftime.datetime(1, 12, 1, 0, 0, 0, 0, -1, 1), units='days since 01-01-01',calendar='noleap')  == 334.0)
-        assert(cftime.date2num(cftime.num2date(1.0,units='days since 01-01-01',calendar='noleap'),units='days since 01-01-01',calendar='noleap') == 1.0)
-        assert(cftime.date2num(cftime.DatetimeNoLeap(1980, 1, 1, 0, 0, 0, 0, 6, 1),'days since 1970-01-01','noleap') == 3650.0)
+        assert cftime.date2num(cftime.datetime(1, 12, 1, 0, 0, 0, 0, -1, 1), units='days since 01-01-01',calendar='noleap')  == 334.0
+        assert cftime.date2num(cftime.num2date(1.0,units='days since 01-01-01',calendar='noleap'),units='days since 01-01-01',calendar='noleap') == 1.0
+        assert cftime.date2num(cftime.DatetimeNoLeap(1980, 1, 1, 0, 0, 0, 0, 6, 1),'days since 1970-01-01','noleap') == 3650.0
         # issue #126
         d = cftime.DatetimeProlepticGregorian(1, 1, 1)
-        assert(cftime.date2num(d, 'days since 0001-01-01',\
-            'proleptic_gregorian') == 0.0)
+        assert cftime.date2num(d, 'days since 0001-01-01',\
+            'proleptic_gregorian') == 0.0
         # issue #140 (fractional seconds in reference date)
         d = datetime.strptime('2018-01-23 09:27:10.950000',"%Y-%m-%d %H:%M:%S.%f")
         units = 'seconds since 2018-01-23 09:31:42.94'
-        assert(float(cftime.date2num(d, units)) == -271.99)
+        assert float(cftime.date2num(d, units)) == -271.99
         # issue 143 - same answer for arrays vs scalars.
         units = 'seconds since 1970-01-01 00:00:00'
         times_in = [1261440000.0, 1261440001.0, 1261440002.0, 1261440003.0,
@@ -743,34 +746,34 @@ class cftimeTestCase(unittest.TestCase):
             times_out2.append(cftime.num2date(time_in, units))
         dates1 = [str(d) for d in times_out1]
         dates2 = [str(d) for d in times_out2]
-        assert(dates1 == dates2)
+        assert dates1 == dates2
         # issue #143 formatting of microseconds
         d = cftime.num2date(1261440000.015625,units)
         # on windows only 100 ms precision
-        assert(str(d)[0:24] == '2009-12-22 00:00:00.0156')
+        assert str(d)[0:24] == '2009-12-22 00:00:00.0156'
         # issue #152 add isoformat()
-        assert(d.isoformat()[0:24] == '2009-12-22T00:00:00.0156')
-        assert(d.isoformat(sep=' ')[0:24] == '2009-12-22 00:00:00.0156')
-        assert(d.isoformat(sep=' ',timespec='milliseconds') == '2009-12-22 00:00:00.016')
-        assert(d.isoformat(sep=' ',timespec='seconds') == '2009-12-22 00:00:00')
-        assert(d.isoformat(sep=' ',timespec='minutes') == '2009-12-22 00:00')
-        assert(d.isoformat(sep=' ',timespec='hours') == '2009-12-22 00')
+        assert d.isoformat()[0:24] == '2009-12-22T00:00:00.0156'
+        assert d.isoformat(sep=' ')[0:24] == '2009-12-22 00:00:00.0156'
+        assert d.isoformat(sep=' ',timespec='milliseconds') == '2009-12-22 00:00:00.016'
+        assert d.isoformat(sep=' ',timespec='seconds') == '2009-12-22 00:00:00'
+        assert d.isoformat(sep=' ',timespec='minutes') == '2009-12-22 00:00'
+        assert d.isoformat(sep=' ',timespec='hours') == '2009-12-22 00'
         # issue #165: make sure python datetime returned
         d=num2date(0,units="seconds since 2000-01-01 00:00:00",only_use_cftime_datetimes=False)
         assert isinstance(d, datetime)
         # issue #169: cftime.datetime has no calendar attribute, causing dayofwk,dayofyr methods
         # to fail.
         c = cftime.datetime(*cftime._parse_date('7480-01-01 00:00:00'),calendar='standard')
-        assert(c.strftime() == '7480-01-01 00:00:00')
+        assert c.strftime() == '7480-01-01 00:00:00'
         # issue #175: masked values not treated properly in num2date
         times = np.ma.masked_array([-3956.7499999995343,-999999999999],mask=[False,True])
         units='days since 1858-11-17 00:00:00'
         dates = num2date(times, units=units, calendar='standard',\
         only_use_cftime_datetimes=False, only_use_python_datetimes=True)
         test = dates == np.ma.masked_array([datetime(1848, 1, 17, 6, 0, 0, 40), None],mask=[0,1])
-        assert(test.all())
+        assert test.all()
         dates = num2date(times, units=units, calendar='standard')
-        assert(str(dates)=="[cftime.DatetimeGregorian(1848, 1, 17, 6, 0, 0, 40, has_year_zero=False)\n --]")
+        assert str(dates)=="[cftime.DatetimeGregorian(1848, 1, 17, 6, 0, 0, 40, has_year_zero=False)\n --]"
 #  check that time range of 200,000 + years can be represented accurately
         calendar='standard'
         _MAX_INT64 = np.iinfo("int64").max
@@ -779,13 +782,13 @@ class cftimeTestCase(unittest.TestCase):
             units = '%s since 01-01-01' % unit
             time = 292471*365*86400*(1000000//int(UNIT_CONVERSION_FACTORS[unit])) + 1000000//int(UNIT_CONVERSION_FACTORS[unit])
             date = num2date(time,units,calendar=calendar)
-            assert(date == refdate)
+            assert date == refdate
             # check round-trip
             time2 = date2num(date,units,calendar=calendar)
             date2 = num2date(time2,units,calendar=calendar)
             # windows doesn't have a longdouble type (it defaults to float64)
             if np.finfo(np.longdouble).precision == 18:
-                assert(date2 == refdate)
+                assert date2 == refdate
 # microsecond roundtrip accuracy preserved over time ranges of 286 years
 # (float64 can only represent integers exactly up to 2**53-1)
         refdate=DatetimeGregorian(286,6,3,23,47,34,740992)
@@ -793,11 +796,11 @@ class cftimeTestCase(unittest.TestCase):
             units = '%s since 01-01-01' % unit
             time = (2**53 - 1)*(1/UNIT_CONVERSION_FACTORS[unit]) + 1/UNIT_CONVERSION_FACTORS[unit]
             date = num2date(time,units,calendar=calendar)
-            assert(date == refdate)
+            assert date == refdate
             # check round-trip
             time2 = date2num(date,units,calendar=calendar)
             date2 = num2date(time2,units,calendar=calendar)
-            assert(date2 == refdate)
+            assert date2 == refdate
 # issue #185: date2num should work the numpy scalar array of dates (1.2.0 regression)
         dates = np.array(datetime(2010, 2, 2, 0, 0))
         assert (date2num(dates, units="hours since 2010-02-01 00:00:00") == 24.)
@@ -808,7 +811,7 @@ class cftimeTestCase(unittest.TestCase):
             units = 'days since -4713-01-01 12:00'
             dt2 = num2date(date2num(dt1, units), units, calendar='proleptic_gregorian')
             dt2 = num2date(date2num(dt1, units, calendar='standard'), units)
-            assert(dt1 == dt2)
+            assert dt1 == dt2
 # issue #189 - leap years calculated incorrectly for negative years in proleptic_gregorian calendar
         dt1 = datetime(2020, 4, 24, 16, 15, 10) # python datetime
         units = 'days since -4713-01-01 12:00'
@@ -817,32 +820,32 @@ class cftimeTestCase(unittest.TestCase):
             warnings.simplefilter("ignore",category=cftime.CFWarning)
             dt2 = num2date(date2num(dt1, units, cal, has_year_zero=False), units,
                                     cal, has_year_zero=False)
-        assert(dt1 == dt2)
+        assert dt1 == dt2
 # issue #198 - cftime.datetime creates calendar specific datetimes that
 # support addition/subtraction of timedeltas.
         for cal in calendars:
             dt = cftime.datetime(2020, 1, 1, calendar=cal)
             dt += timedelta(hours=1)
-            assert(str(dt) == '2020-01-01 01:00:00')
+            assert str(dt) == '2020-01-01 01:00:00'
 # issue #193 - years with more than four digits in reference date
-        assert(cftime.date2num(cftime.datetime(18000, 12, 1, 0, 0), 'days since 18000-1-1', '360_day') == 330.0)
+        assert cftime.date2num(cftime.datetime(18000, 12, 1, 0, 0), 'days since 18000-1-1', '360_day') == 330.0
         # julian day not including year zero
         d = cftime.datetime(2020, 12, 1, 12, calendar='julian')
         with warnings.catch_warnings():
             warnings.simplefilter("ignore",category=cftime.CFWarning)
             units = 'days since -4713-1-1 12:00'
             jd = cftime.date2num(d,units,calendar='julian')
-            assert(jd == 2459198.0)
+            assert jd == 2459198.0
             # if calendar=None, use input date to determine calendar
             jd = cftime.date2num(d,units,calendar=None)
-            assert(jd == 2459198.0)
+            assert jd == 2459198.0
             # if no calendar specified, use calendar associated with datetime
             # instance.
             jd = cftime.date2num(d,units)
-            assert(jd == 2459198.0)
+            assert jd == 2459198.0
             # use 'standard' calendar
             jd = cftime.date2num(d,units,calendar='standard')
-            assert(jd == 2459185.0)
+            assert jd == 2459185.0
 
         # issue #211
         # (masked array handling in date2num - AttributeError:
@@ -850,7 +853,7 @@ class cftimeTestCase(unittest.TestCase):
         m = np.ma.asarray(
             [cftime.DatetimeGregorian(2014, 8, 1, 12, 0, 0, 0)]
             )
-        assert(
+        assert (
              cftime.date2num(m, units="seconds since 2000-1-1")==[4.602096e+08]
               )
 
@@ -869,11 +872,11 @@ class cftimeTestCase(unittest.TestCase):
                 else:
                     raise AssertionError # fail if ValueError not raised
                 d2 = cftime.datetime(1,1,1,0,has_year_zero=has_year_zero,calendar=calendar)
-                assert((d2-d).days==366) # 1-1-1 is 366 days after -1-1-1 if no year zero.
+                assert (d2-d).days==366 # 1-1-1 is 366 days after -1-1-1 if no year zero.
                 has_year_zero=True
                 d = cftime.datetime(0,1,1,0,has_year_zero=has_year_zero,calendar=calendar)
                 d2 = cftime.datetime(1,1,1,0,has_year_zero=has_year_zero,calendar=calendar)
-                assert((d2-d).days==366) # 1-1-1 is 366 days after 0-1-1 if year zero allowed.
+                assert (d2-d).days==366 # 1-1-1 is 366 days after 0-1-1 if year zero allowed.
                 for has_year_zero in [True,False]:
                     if calendar == 'julian':
                         d = cftime.datetime(1858,11, 4,12,has_year_zero=has_year_zero,calendar=calendar)
@@ -890,40 +893,56 @@ class cftimeTestCase(unittest.TestCase):
                         else:
                             d0 = cftime.datetime(-4713, 1, 1,12,has_year_zero=has_year_zero,calendar=calendar)
                     jd = d.toordinal()
-                    assert((d-d0).days == jdref)
-                    assert(jd == jdref)
-                    assert(d.toordinal() == jdref)
+                    assert (d-d0).days == jdref
+                    assert jd == jdref
+                    assert d.toordinal() == jdref
                     d2 = cftime.datetime.fromordinal(jd,calendar=calendar,has_year_zero=has_year_zero)
-                    assert(d2 == d)
+                    assert d2 == d
         # issue #248.  Set has_year_zero=True if year zero requested
         # on instance creation, or by using replace method.
         d=cftime.datetime(0, 0, 0, calendar=None)
-        assert(d.has_year_zero==True)
+        assert d.has_year_zero is True
         d=cftime.datetime(1, 0, 0, calendar=None)
-        assert(d.has_year_zero==False)
+        assert d.has_year_zero is False
         d = d.replace(year=0)
-        assert(d.has_year_zero==True)
+        assert d.has_year_zero is True
         # this should raise a warning, since the default has_year_zero
         # is changed if year specified as zero. (issue #248, PR #249)
         self.assertWarns(UserWarning, cftime.datetime, 0, 1, 1,\
                 calendar='standard')
         # check that for idealized calendars has_year_zero is always True
         d=cftime.datetime(0, 1, 1, calendar='360_day')
-        assert(d.has_year_zero==True)
+        assert d.has_year_zero is True
         d=cftime.datetime(1, 1, 1, calendar='360_day')
-        assert(d.has_year_zero==True)
+        assert d.has_year_zero is True
         d = d.replace(year=0)
-        assert(d.has_year_zero==True)
+        assert d.has_year_zero is True
         # test leap year function
-        assert(cftime.is_leap_year(2000,calendar='standard'))
-        assert(cftime.is_leap_year(-1,calendar='standard'))
-        assert(cftime.is_leap_year(0,calendar='standard',has_year_zero=True))
-        assert(not cftime.is_leap_year(1,calendar='standard',has_year_zero=True))
-        assert(not cftime.is_leap_year(1,calendar='365_day'))
-        assert(cftime.is_leap_year(1,calendar='366_day'))
+        assert cftime.is_leap_year(2000,calendar='standard')
+        assert cftime.is_leap_year(-1,calendar='standard')
+        assert cftime.is_leap_year(0,calendar='standard',has_year_zero=True)
+        assert not cftime.is_leap_year(1,calendar='standard',has_year_zero=True)
+        assert not cftime.is_leap_year(1,calendar='365_day')
+        assert cftime.is_leap_year(1,calendar='366_day')
         # num2date should not fail on an empty int array (issue #287)
         d = cftime.num2date(np.array([], dtype="int64"), "days since 1970-01-01",\
             calendar="proleptic_gregorian", only_use_cftime_datetimes=True)
+        # date2num should return an empty array if given one (issue #315)
+        d = cftime.date2num([], 'seconds since 2000-01-01 12:00:00')
+        assert d.size==0
+        # issue #328:  handle nan/inf in num2date.
+        times = np.array([1,2,3,np.nan],dtype=np.float64)
+        expected = np.array([DatetimeGregorian(2000, 1, 2, 0, 0, 0, 0),
+                             DatetimeGregorian(2000, 1, 3, 0, 0, 0, 0),
+                             DatetimeGregorian(2000, 1, 4, 0, 0, 0, 0),
+                             DatetimeGregorian(2000, 1, 5, 0, 0, 0, 0)])
+        mask = [False, False, False, True]
+        expected = np.ma.masked_array(expected, mask=mask)
+        result = cftime.num2date(times, 'days since 2000-01-01', 'standard')
+        np.testing.assert_equal(result, expected)
+        times = np.array([1,2,3,np.inf],dtype=np.float64)
+        result = cftime.num2date(times, 'days since 2000-01-01', 'standard')
+        np.testing.assert_equal(result, expected)
 
 
 class TestDate2index(unittest.TestCase):
@@ -1034,7 +1053,7 @@ class TestDate2index(unittest.TestCase):
         nutime = self.TestTime(datetime(1950, 1, 1), 366, 24,
                                'hours since 1900-01-01', 'standard')
         try:
-            t = date2index(datetime(1949, 2, 1), nutime)
+            _t = date2index(datetime(1949, 2, 1), nutime)
         except ValueError:
             pass
         else:
@@ -1113,7 +1132,7 @@ class TestDate2index(unittest.TestCase):
         dates2 = num2date(times2, units)
         datediff = abs(dates-dates2)
         for diff in datediff:
-            assert(diff.microseconds < 100) # tolerance of 100 ms
+            assert diff.microseconds < 100 # tolerance of 100 ms
 
     def test_issue444(self):
         # make sure integer overflow not causing error in
@@ -1122,19 +1141,19 @@ class TestDate2index(unittest.TestCase):
         query_time = datetime(2037, 1, 3, 21, 12)
         index = date2index(query_time, self.time_vars['time3'],
                            select='nearest')
-        assert(index == 11)
+        assert index == 11
 
     def test_issue272(self):
         timeArray = self.issue272time
         date = datetime(2020, 2, 22, 13)
-        assert(date2index(date, timeArray, calendar="gregorian",
-            select="exact")==3)
-        assert(date2index(date, timeArray, calendar="gregorian",
-            select="before")==2)
-        assert(date2index(date, timeArray, calendar="gregorian",
-            select="after")==4)
-        assert(date2index(date, timeArray, calendar="gregorian",
-            select="nearest")==3)
+        assert date2index(date, timeArray, calendar="gregorian",
+            select="exact")==3
+        assert date2index(date, timeArray, calendar="gregorian",
+            select="before")==2
+        assert date2index(date, timeArray, calendar="gregorian",
+            select="after")==4
+        assert date2index(date, timeArray, calendar="gregorian",
+            select="nearest")==3
 
 
 class issue584TestCase(unittest.TestCase):
@@ -1353,7 +1372,7 @@ class DateTime(unittest.TestCase):
         deserialized = pickle.loads(pickle.dumps(date))
         self.assertEqual(date, deserialized)
         self.assertEqual(type(date), type(deserialized))
-        
+
     def test_misc(self):
         "Miscellaneous tests."
         # make sure repr succeeds
@@ -1511,7 +1530,7 @@ def days_per_month_leap_year(date_type, month):
 
 
 def test_zero_year(date_type):
-    # Year 0 is valid in the 360,365 and 366 day and 
+    # Year 0 is valid in the 360,365 and 366 day and
     # Proleptic Gregorian calendars by default.
     with warnings.catch_warnings():
         warnings.simplefilter("ignore",category=cftime.CFWarning)
@@ -1520,7 +1539,7 @@ def test_zero_year(date_type):
             date_type(0, 1, 1)
         else:
             d=date_type(0,1,1) # has_year_zero=True set if year 0 specified
-            assert(d.has_year_zero) # (issue #248) 
+            assert d.has_year_zero # (issue #248)
             with pytest.raises(ValueError):
                 date_type(0, 1, 1, has_year_zero=False)
 
@@ -1707,21 +1726,21 @@ def test_string_format2():
 
 def test_strptime():
     d = cftime.datetime.strptime('24/Aug/2004:17:57:26 +0200', '%d/%b/%Y:%H:%M:%S %z',calendar='julian',has_year_zero=True)
-    assert(repr(d) == "cftime.datetime(2004, 8, 24, 15, 57, 26, 0, calendar='julian', has_year_zero=True)")
+    assert repr(d) == "cftime.datetime(2004, 8, 24, 15, 57, 26, 0, calendar='julian', has_year_zero=True)"
     d = cftime.datetime.strptime("0000-02-30",\
              "%Y-%m-%d",calendar='360_day',has_year_zero=True)
-    assert(repr(d) == "cftime.datetime(0, 2, 30, 0, 0, 0, 0, calendar='360_day', has_year_zero=True)")
+    assert repr(d) == "cftime.datetime(0, 2, 30, 0, 0, 0, 0, calendar='360_day', has_year_zero=True)"
     d = cftime.datetime.strptime('-9999-02-29 10:18:32.926',\
              '%Y-%m-%d %H:%M:%S.%f',calendar='366_day')
-    assert(repr(d) == "cftime.datetime(-9999, 2, 29, 10, 18, 32, 926000, calendar='all_leap', has_year_zero=True)")
+    assert repr(d) == "cftime.datetime(-9999, 2, 29, 10, 18, 32, 926000, calendar='all_leap', has_year_zero=True)"
     d = cftime.datetime.strptime("20200230", "%Y%m%d", "360_day") # no separator, issue #301
-    assert(repr(d) == "cftime.datetime(2020, 2, 30, 0, 0, 0, 0, calendar='360_day', has_year_zero=True)")
+    assert repr(d) == "cftime.datetime(2020, 2, 30, 0, 0, 0, 0, calendar='360_day', has_year_zero=True)"
     d = cftime.datetime.strptime('24/Aug/-4712:17:57:26', '%d/%b/%Y:%H:%M:%S',calendar='julian')
-    assert(repr(d) == "cftime.datetime(-4712, 8, 24, 17, 57, 26, 0, calendar='julian', has_year_zero=False)")
+    assert repr(d) == "cftime.datetime(-4712, 8, 24, 17, 57, 26, 0, calendar='julian', has_year_zero=False)"
     d = cftime.datetime.strptime('24/August/-4712:17:57:26', '%d/%B/%Y:%H:%M:%S',calendar='julian')
-    assert(repr(d) == "cftime.datetime(-4712, 8, 24, 17, 57, 26, 0, calendar='julian', has_year_zero=False)")
+    assert repr(d) == "cftime.datetime(-4712, 8, 24, 17, 57, 26, 0, calendar='julian', has_year_zero=False)"
     d = cftime.datetime.strptime("-4712", "%Y", calendar="julian")
-    assert(repr(d) == "cftime.datetime(-4712, 1, 1, 0, 0, 0, 0, calendar='julian', has_year_zero=False)")
+    assert repr(d) == "cftime.datetime(-4712, 1, 1, 0, 0, 0, 0, calendar='julian', has_year_zero=False)"
     # should fail with KeyError
     try:
         d=cftime.datetime.strptime("2000-45-3", "%G-%V-%u", calendar="noleap")
@@ -2072,19 +2091,19 @@ def test_num2date_integer_upcast_required():
 
 
 @pytest.mark.parametrize(
-    "encoding_units", 
+    "encoding_units",
     ["microseconds", "milliseconds", "seconds", "minutes", "hours", "days"]
 )
 @pytest.mark.parametrize(
     "freq",
     [
         timedelta(microseconds=1),
-        timedelta(microseconds=1000), 
-        timedelta(seconds=1), 
-        timedelta(minutes=1), 
-        timedelta(hours=1), 
+        timedelta(microseconds=1000),
+        timedelta(seconds=1),
+        timedelta(minutes=1),
+        timedelta(hours=1),
         timedelta(days=1)
-    ], 
+    ],
     ids=lambda x: f"{x!r}"
 )
 def test_date2num_num2date_roundtrip(encoding_units, freq, calendar):
@@ -2092,8 +2111,8 @@ def test_date2num_num2date_roundtrip(encoding_units, freq, calendar):
     lengthy_timedelta = timedelta(days=291000 * 360)
     times = np.array(
         [
-            date_type(1, 1, 1), 
-            date_type(1, 1, 1) + lengthy_timedelta, 
+            date_type(1, 1, 1),
+            date_type(1, 1, 1) + lengthy_timedelta,
             date_type(1, 1, 1) + lengthy_timedelta + freq
         ]
     )
@@ -2140,7 +2159,7 @@ def test_date2num_missing_data():
 
 def test_num2date_preserves_shape():
     # The optimized num2date algorithm operates on a flattened array.  This
-    # check ensures that the original shape of the times is restored in the 
+    # check ensures that the original shape of the times is restored in the
     # result.
     a = np.array([[0, 1, 2], [3, 4, 5]])
     result = num2date(a, units="days since 2000-01-01", calendar="standard")



View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/compare/6b43c18db17058cb374a495d808f6a048ea243de...01e1d54f3e2ddec92acf278907af334abf4f7be1

-- 
This project does not include diff previews in email notifications.
View it on GitLab: https://salsa.debian.org/debian-gis-team/cftime/-/compare/6b43c18db17058cb374a495d808f6a048ea243de...01e1d54f3e2ddec92acf278907af334abf4f7be1
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/20240607/5fcd6c08/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list