From 9eab7818a9e3a67cfca922667fb758fd54c0217b Mon Sep 17 00:00:00 2001 From: "P. L. Lim" <2090236+pllim@users.noreply.github.com> Date: Wed, 11 Oct 2023 20:25:01 -0400 Subject: [PATCH] Bump minversions, update CI (#240) * Bump minversions, update CI * Lots of clean-up --- .github/workflows/spherical_geometry.yml | 61 +++-- CHANGES.rst | 10 +- MANIFEST.in | 5 +- .../tests/coveragerc => coveragerc | 0 licenses/LICENSE.rst | 2 +- pyproject.toml | 2 +- setup.cfg | 19 +- setup.py | 9 +- spherical_geometry/__init__.py | 8 +- spherical_geometry/graph.py | 4 +- spherical_geometry/great_circle_arc.py | 3 - spherical_geometry/polygon.py | 1 - spherical_geometry/tests/setup_package.py | 3 - spherical_geometry/tests/test_basic.py | 2 - spherical_geometry/tests/test_intersection.py | 5 +- spherical_geometry/tests/test_union.py | 5 +- spherical_geometry/utils/__init__.py | 4 - spherical_geometry/utils/compat/__init__.py | 4 - spherical_geometry/utils/compat/weakref.py | 19 -- spherical_geometry/utils/compat/weakrefset.py | 213 ------------------ spherical_geometry/vector.py | 2 - 21 files changed, 76 insertions(+), 305 deletions(-) rename spherical_geometry/tests/coveragerc => coveragerc (100%) delete mode 100755 spherical_geometry/tests/setup_package.py delete mode 100755 spherical_geometry/utils/__init__.py delete mode 100644 spherical_geometry/utils/compat/__init__.py delete mode 100644 spherical_geometry/utils/compat/weakref.py delete mode 100644 spherical_geometry/utils/compat/weakrefset.py diff --git a/.github/workflows/spherical_geometry.yml b/.github/workflows/spherical_geometry.yml index a9c7ffc..debd505 100644 --- a/.github/workflows/spherical_geometry.yml +++ b/.github/workflows/spherical_geometry.yml @@ -1,7 +1,5 @@ name: Spherical Geometry CI -# Change 'on' section and add code coverage. - on: push: branches: @@ -9,23 +7,30 @@ on: tags: - "*" pull_request: - branches: schedule: # Run every Monday at 6am UTC - cron: '0 6 * * 1' +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +permissions: + contents: read + jobs: build: name: Python Testing ${{ matrix.python-version }} runs-on: ubuntu-latest strategy: + fail-fast: false matrix: - python-version: ['3.8', '3.9', '3.10', '3.11'] + python-version: ['3.9', '3.10', '3.11', '3.12'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -35,6 +40,28 @@ jobs: python -m pip install -e .[test] - name: Test with pytest run: | + pip freeze + pytest + + devdeps: + name: Python Testing with dev versions of dependencies + runs-on: ubuntu-latest + + steps: + - uses: actions/checkout@v4 + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + - name: Install dependencies + run: | + python -m pip install --upgrade pip + python -m pip install --pre --upgrade --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy + python -m pip install --pre --upgrade --extra-index-url https://pypi.anaconda.org/astropy/simple astropy + python -m pip install -e .[test] + - name: Test with pytest + run: | + pip freeze pytest doc_build: @@ -42,11 +69,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: '3.11' - name: Install dependencies run: | sudo apt-get install graphviz texlive-latex-extra dvipng @@ -54,6 +81,7 @@ jobs: python -m pip install -e .[docs] - name: Build documents with sphinx run: | + pip freeze sphinx-build docs build/docs code_coverage: @@ -61,20 +89,21 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.9 + python-version: '3.11' - name: Install dependencies run: | python -m pip install --upgrade pip python -m pip install -e .[test] - name: Test with pytest and code coverage run: | + pip freeze pytest --cov-report=xml --cov=. --cov-config=setup.cfg - name: Upload coverage to codecoverage - uses: codecov/codecov-action@v2 + uses: codecov/codecov-action@v3 with: file: ./coverage.xml @@ -83,11 +112,11 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python - uses: actions/setup-python@v2 + uses: actions/setup-python@v4 with: - python-version: 3.8 + python-version: '3.11' - name: Install Dependencies run: | python -m pip install --upgrade pip diff --git a/CHANGES.rst b/CHANGES.rst index fac152a..6c047aa 100644 --- a/CHANGES.rst +++ b/CHANGES.rst @@ -4,8 +4,8 @@ Release Notes ============= -1.2.24 (unreleased) -=================== +1.3 (unreleased) +================ - Add documentation to ``polygon.py`` for the ``SphericalPolygon`` method ``multi_union`` has exponential time behavior and cannot @@ -18,6 +18,12 @@ Release Notes https://github.com/spacetelescope/spherical_geometry/issues/232 is resolved. [#233] +- Removed unused ``spherical_geometry.utils`` module. [#239] + +- Minimum supported version for Python is now 3.9. [#239] + +- Minimum supported version for NumPy is now 1.20. [#239] + 1.2.23 (10-October-2022) ======================== diff --git a/MANIFEST.in b/MANIFEST.in index 5c119e6..524eba4 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -2,12 +2,11 @@ include README.rst include CHANGES.rst include setup.cfg +include pyproject.toml -recursive-include *.pyx *.c *.pxd +recursive-include *.c recursive-include docs * recursive-include licenses * -recursive-include cextern * -recursive-include scripts * prune build prune docs/_build diff --git a/spherical_geometry/tests/coveragerc b/coveragerc similarity index 100% rename from spherical_geometry/tests/coveragerc rename to coveragerc diff --git a/licenses/LICENSE.rst b/licenses/LICENSE.rst index 7edcf72..021d379 100644 --- a/licenses/LICENSE.rst +++ b/licenses/LICENSE.rst @@ -1,4 +1,4 @@ -Copyright (C) 2011,2014 Association of Universities for Research in +Copyright (C) 2011-2023 Association of Universities for Research in Astronomy (AURA) Redistribution and use in source and binary forms, with or without diff --git a/pyproject.toml b/pyproject.toml index 07ba7aa..5939726 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,2 @@ [build-system] -requires = ["setuptools>=38.2.5", "setuptools_scm", "wheel", "oldest-supported-numpy"] +requires = ["setuptools>=38.2.5", "setuptools_scm", "numpy>=1.25,<2"] diff --git a/setup.cfg b/setup.cfg index d6543d3..8d570af 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,16 +1,12 @@ -[build_sphinx] -source-dir = docs -build-dir = docs/_build -all_files = 1 - -[upload_docs] -upload-dir = docs/_build/html -show-response = 1 - -[tools:pytest] -minversion = 4.2 +[tool:pytest] +minversion = 6 junit_family = xunit2 norecursedirs = .git build docs/_build +xfail_strict = true +filterwarnings = + error + ignore:numpy\.ndarray size changed:RuntimeWarning + ignore:numpy\.ufunc size changed:RuntimeWarning [metadata] package_name = spherical_geometry @@ -32,4 +28,3 @@ omit = spherical_geometry/tests/* # And list again for running against installed versions */spherical_geometry/tests/* - diff --git a/setup.py b/setup.py index df5e1e2..f98fb9c 100644 --- a/setup.py +++ b/setup.py @@ -68,10 +68,7 @@ def qd_config(arg): exit(1) # Get some values from the setup.cfg -try: - from ConfigParser import ConfigParser -except ImportError: - from configparser import ConfigParser +from configparser import ConfigParser conf = ConfigParser() conf.read(['setup.cfg']) metadata = dict(conf.items('metadata')) @@ -134,9 +131,9 @@ def qd_config(arg): description=DESCRIPTION, install_requires=[ 'astropy>=5.0.4', - 'numpy>=1.18', + 'numpy>=1.20', ], - python_requiers='>=3.8', + python_requiers='>=3.9', extras_require={ 'test': [ 'pytest', diff --git a/spherical_geometry/__init__.py b/spherical_geometry/__init__.py index 873e2c3..5a8b56b 100755 --- a/spherical_geometry/__init__.py +++ b/spherical_geometry/__init__.py @@ -1,6 +1,4 @@ -from pkg_resources import get_distribution, DistributionNotFound try: - __version__ = get_distribution(__name__).version -except DistributionNotFound: - # package is not installed - __version__ = 'unknown' + from .version import version as __version__ +except ImportError: + __version__ = '' diff --git a/spherical_geometry/graph.py b/spherical_geometry/graph.py index 0be7e00..d5111be 100644 --- a/spherical_geometry/graph.py +++ b/spherical_geometry/graph.py @@ -6,16 +6,14 @@ This contains the code that does the actual unioning of regions. """ # TODO: Weak references for memory management problems? -from __future__ import absolute_import, division, unicode_literals, print_function # STDLIB -import itertools +import weakref # THIRD-PARTY import numpy as np # LOCAL -from .utils.compat import weakref from . import great_circle_arc as gca from . import vector from .polygon import (SingleSphericalPolygon, SphericalPolygon, diff --git a/spherical_geometry/great_circle_arc.py b/spherical_geometry/great_circle_arc.py index 9a57c76..39c9361 100644 --- a/spherical_geometry/great_circle_arc.py +++ b/spherical_geometry/great_circle_arc.py @@ -11,9 +11,6 @@ section of those circles between two points on the unit sphere. """ -from __future__ import with_statement, division, absolute_import, unicode_literals - -import math from .vector import two_d # THIRD-PARTY diff --git a/spherical_geometry/polygon.py b/spherical_geometry/polygon.py index 12ae9eb..4dfd246 100644 --- a/spherical_geometry/polygon.py +++ b/spherical_geometry/polygon.py @@ -6,7 +6,6 @@ The `spherical_geometry.polygon` module defines the `SphericalPolygon` class for managing polygons on the unit sphere. """ -from __future__ import division, print_function, unicode_literals, absolute_import # STDLIB from copy import copy, deepcopy diff --git a/spherical_geometry/tests/setup_package.py b/spherical_geometry/tests/setup_package.py deleted file mode 100755 index a7670e4..0000000 --- a/spherical_geometry/tests/setup_package.py +++ /dev/null @@ -1,3 +0,0 @@ -def get_package_data(): - return { - _ASTROPY_PACKAGE_NAME_ + '.tests': ['coveragerc', 'data/*']} diff --git a/spherical_geometry/tests/test_basic.py b/spherical_geometry/tests/test_basic.py index c7a95a5..6eb99bf 100644 --- a/spherical_geometry/tests/test_basic.py +++ b/spherical_geometry/tests/test_basic.py @@ -1,5 +1,3 @@ -from __future__ import absolute_import - import os import os.path import math diff --git a/spherical_geometry/tests/test_intersection.py b/spherical_geometry/tests/test_intersection.py index 3331474..40cd8c4 100644 --- a/spherical_geometry/tests/test_intersection.py +++ b/spherical_geometry/tests/test_intersection.py @@ -1,5 +1,3 @@ -from __future__ import print_function, absolute_import - # STDLIB import codecs import functools @@ -8,10 +6,10 @@ import os import random import sys -import pytest # THIRD-PARTY import numpy as np +import pytest from numpy.testing import assert_array_almost_equal # LOCAL @@ -121,6 +119,7 @@ def test3(): return [poly1, poly3] +@pytest.mark.filterwarnings("ignore:CPERROR.*") def test4(): from astropy.io import fits from astropy import wcs as pywcs diff --git a/spherical_geometry/tests/test_union.py b/spherical_geometry/tests/test_union.py index 1c5dea1..2868f32 100644 --- a/spherical_geometry/tests/test_union.py +++ b/spherical_geometry/tests/test_union.py @@ -1,5 +1,3 @@ -from __future__ import print_function, absolute_import - # STDLIB import codecs import functools @@ -11,6 +9,7 @@ # THIRD-PARTY import numpy as np +import pytest from numpy.testing import assert_array_almost_equal # LOCAL @@ -113,6 +112,7 @@ def test2(): return [poly1, poly2, poly3, poly4, poly5, poly6] +@pytest.mark.filterwarnings("ignore:CPERROR.*") def test5(): from astropy.io import fits from astropy import wcs as pywcs @@ -139,6 +139,7 @@ def test6(): null_union = chipA1.union(chipA2) +@pytest.mark.filterwarnings("ignore:CPERROR.*") @union_test(0, 90) def test7(): from astropy.io import fits diff --git a/spherical_geometry/utils/__init__.py b/spherical_geometry/utils/__init__.py deleted file mode 100755 index c750873..0000000 --- a/spherical_geometry/utils/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -# This sub-module is destined for common non-package specific utility -# functions that will ultimately be merged into `astropy.utils` diff --git a/spherical_geometry/utils/compat/__init__.py b/spherical_geometry/utils/compat/__init__.py deleted file mode 100644 index 6c2ccc0..0000000 --- a/spherical_geometry/utils/compat/__init__.py +++ /dev/null @@ -1,4 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst - -# This sub-module contains packages added for compatability with -# versions of Python other than 2.7 diff --git a/spherical_geometry/utils/compat/weakref.py b/spherical_geometry/utils/compat/weakref.py deleted file mode 100644 index 8688472..0000000 --- a/spherical_geometry/utils/compat/weakref.py +++ /dev/null @@ -1,19 +0,0 @@ -# Licensed under a 3-clause BSD style license - see LICENSE.rst -""" -A replacement wrapper around the weakref module that adds WeakSet for -versions of python that are missing it. - -Instead of importing weakref, other modules should use this as follows:: - - from .utils.compat import weakref - -""" -from __future__ import absolute_import - -import weakref - -# python2.7 and later provide a WeakSet class -if not hasattr(weakref, 'WeakSet'): - from .weakrefset import WeakSet - -from weakref import * diff --git a/spherical_geometry/utils/compat/weakrefset.py b/spherical_geometry/utils/compat/weakrefset.py deleted file mode 100644 index 072c453..0000000 --- a/spherical_geometry/utils/compat/weakrefset.py +++ /dev/null @@ -1,213 +0,0 @@ -from __future__ import with_statement - -from _weakref import ref - -__all__ = ['WeakSet'] - -__version__ = '1.0.0' - - - -class _IterationGuard(object): - # This context manager registers itself in the current iterators of the - # weak container, such as to delay all removals until the context manager - # exits. - # This technique should be relatively thread-safe (since sets are). - - def __init__(self, weakcontainer): - # Don't create cycles - self.weakcontainer = ref(weakcontainer) - - def __enter__(self): - w = self.weakcontainer() - if w is not None: - w._iterating.add(self) - return self - - def __exit__(self, e, t, b): - w = self.weakcontainer() - if w is not None: - s = w._iterating - s.remove(self) - if not s: - w._commit_removals() - - -class WeakSet(object): - def __init__(self, data=None): - self.data = set() - def _remove(item, selfref=ref(self)): - self = selfref() - if self is not None: - if self._iterating: - self._pending_removals.append(item) - else: - self.data.discard(item) - self._remove = _remove - # A list of keys to be removed - self._pending_removals = [] - self._iterating = set() - if data is not None: - self.update(data) - - def _commit_removals(self): - l = self._pending_removals - discard = self.data.discard - while l: - discard(l.pop()) - - def __iter__(self): - with _IterationGuard(self): - for itemref in self.data: - item = itemref() - if item is not None: - yield item - - def __len__(self): - return sum(x() is not None for x in self.data) - - def __contains__(self, item): - return ref(item) in self.data - - def __reduce__(self): - return (self.__class__, (list(self),), - getattr(self, '__dict__', None)) - - __hash__ = None - - def add(self, item): - if self._pending_removals: - self._commit_removals() - self.data.add(ref(item, self._remove)) - - def clear(self): - if self._pending_removals: - self._commit_removals() - self.data.clear() - - def copy(self): - return self.__class__(self) - - def pop(self): - if self._pending_removals: - self._commit_removals() - while True: - try: - itemref = self.data.pop() - except KeyError: - raise KeyError('pop from empty WeakSet') - item = itemref() - if item is not None: - return item - - def remove(self, item): - if self._pending_removals: - self._commit_removals() - self.data.remove(ref(item)) - - def discard(self, item): - if self._pending_removals: - self._commit_removals() - self.data.discard(ref(item)) - - def update(self, other): - if self._pending_removals: - self._commit_removals() - if isinstance(other, self.__class__): - self.data.update(other.data) - else: - for element in other: - self.add(element) - - def __ior__(self, other): - self.update(other) - return self - - # Helper functions for simple delegating methods. - def _apply(self, other, method): - if not isinstance(other, self.__class__): - other = self.__class__(other) - newdata = method(other.data) - newset = self.__class__() - newset.data = newdata - return newset - - def difference(self, other): - return self._apply(other, self.data.difference) - __sub__ = difference - - def difference_update(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.difference_update(ref(item) for item in other) - def __isub__(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.difference_update(ref(item) for item in other) - return self - - def intersection(self, other): - return self._apply(other, self.data.intersection) - __and__ = intersection - - def intersection_update(self, other): - if self._pending_removals: - self._commit_removals() - self.data.intersection_update(ref(item) for item in other) - def __iand__(self, other): - if self._pending_removals: - self._commit_removals() - self.data.intersection_update(ref(item) for item in other) - return self - - def issubset(self, other): - return self.data.issubset(ref(item) for item in other) - __lt__ = issubset - - def __le__(self, other): - return self.data <= set(ref(item) for item in other) - - def issuperset(self, other): - return self.data.issuperset(ref(item) for item in other) - __gt__ = issuperset - - def __ge__(self, other): - return self.data >= set(ref(item) for item in other) - - def __eq__(self, other): - if not isinstance(other, self.__class__): - return NotImplemented - return self.data == set(ref(item) for item in other) - - def symmetric_difference(self, other): - return self._apply(other, self.data.symmetric_difference) - __xor__ = symmetric_difference - - def symmetric_difference_update(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.symmetric_difference_update(ref(item) for item in other) - def __ixor__(self, other): - if self._pending_removals: - self._commit_removals() - if self is other: - self.data.clear() - else: - self.data.symmetric_difference_update(ref(item) for item in other) - return self - - def union(self, other): - return self._apply(other, self.data.union) - __or__ = union - - def isdisjoint(self, other): - return len(self.intersection(other)) == 0 diff --git a/spherical_geometry/vector.py b/spherical_geometry/vector.py index 87c42c7..1b7a07d 100644 --- a/spherical_geometry/vector.py +++ b/spherical_geometry/vector.py @@ -7,8 +7,6 @@ vectors and converting them to and from other representations. """ -from __future__ import unicode_literals - # THIRD-PARTY import numpy as np