diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a81b133409..c37a2b1bd8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -21,7 +21,7 @@ jobs: matrix: # First all python versions in basic linux os: [ ubuntu-latest ] - py: [ 3.8, 3.9, "3.10", 3.11, 3.12, 3.13 ] + py: [ 3.9, "3.10", 3.11, 3.12, 3.13 ] CC: [ gcc ] CXX: [ g++ ] FFTW_DIR: [ "/usr/local/lib" ] @@ -107,7 +107,7 @@ jobs: # Easier if eigen is installed with apt-get, but on at least one system, check that # it gets downloaded and installed properly if it isn't installed. - if ${{ matrix.py != 3.8 }}; then sudo -H apt install -y libeigen3-dev; fi + if ${{ matrix.py != 3.12 }}; then sudo -H apt install -y libeigen3-dev; fi # Need this for the mpeg tests sudo -H apt install -y ffmpeg @@ -148,7 +148,7 @@ jobs: # Do these three first to clarify potential conflicts pip install -U numpy - pip install -U "setuptools<72" + pip install -U setuptools pip install -U wheel # Standard dependencies @@ -160,13 +160,13 @@ jobs: pip install -U matplotlib - - name: Install astroplan - # astroplan isn't yet numpy 2.0 compatible. So don't install that on 3.9+. - # I'm also getting errors with starlink, which I think may be related to - # numpy 2.0. - if: (matrix.py == 3.8) || (matrix.py == 3.9) - run: | pip install -U astroplan + + # Starlink doesn't seem to work with 3.10+. + # Revisit from time to time to see if they fix it. + - name: Install starlink + if: matrix.py == 3.9 + run: | pip install -U starlink-pyast - name: Install CPython-only dependencies @@ -189,7 +189,7 @@ jobs: FFTW_DIR=$FFTW_DIR pip install -vvv . - name: Check download_cosmos only if it changed. (And only on 1 runner) - if: matrix.py == 3.8 && github.base_ref != '' + if: matrix.py == 3.12 && github.base_ref != '' run: | git status git --no-pager log --graph --pretty=oneline --abbrev-commit | head -50 @@ -206,11 +206,11 @@ jobs: # Less confusing to include it explicitly. - name: Check shared library - if: matrix.py == 3.8 + if: matrix.py == 3.12 run: | - # On a few systems (arbitrarily py3.8, where we also check mac and clang), + # On a few systems (arbitrarily py3.12, where we also check mac and clang), # check the shared library creation and link. - GALSIM_BUILD_SHARED=1 python setup.py install + GALSIM_BUILD_SHARED=1 python setup.py build # These directories/files should now exist. echo "Look for the shared library:" @@ -218,15 +218,8 @@ jobs: ls build/shared_clib ls build/shared_clib/libgalsim.* - # For now, use `python setup.py test` to test linking to the shared library. - # We run some C++ tests there, and we use the shared library for linking. - # Caveats: - # 1. The C++ tests are not very comprehensive, so it won't catch all errors. - # 2. Apparently setup.py test is deprecated. I'm getting warnings when I run - # it that indicate it may go away at some point. So whenever that happens, - # we'll have to revisit this test to find another way to build and run - # the C++ tests. - GALSIM_TEST_PY=0 python setup.py test + # Build and run the C++ test suite + GALSIM_RUN_TEST=1 python setup.py build - name: Upload coverage to codecov if: matrix.py != 'pypy-3.7' diff --git a/.github/workflows/wheels.yml b/.github/workflows/wheels.yml index 1919b8d2ed..5220011c43 100644 --- a/.github/workflows/wheels.yml +++ b/.github/workflows/wheels.yml @@ -128,7 +128,7 @@ jobs: - name: Install dependencies run: | python -m pip install -U pip - pip install -U numpy "setuptools<72" + pip install -U numpy setuptools pip install -U -r requirements.txt - name: Download wheels diff --git a/conda_requirements.txt b/conda_requirements.txt index a0dfb6ccd3..5e87ca011e 100644 --- a/conda_requirements.txt +++ b/conda_requirements.txt @@ -1,6 +1,6 @@ # The requirements packages that can be installed with # conda install -y -c conda-forge --file conda_requirements.txt -setuptools>=38,<72 +setuptools>=38 numpy>=1.17 astropy>=2.0 pybind11>=2.2 diff --git a/galsim/image.py b/galsim/image.py index 747c97a46b..d192a2b3e9 100644 --- a/galsim/image.py +++ b/galsim/image.py @@ -1789,7 +1789,10 @@ def __pow__(self, other): def __ipow__(self, other): if not isinstance(other, int) and not isinstance(other, float): raise TypeError("Can only raise an image to a float or int power!") - self.array[:,:] **= other + if not self.isinteger or isinstance(other, int): + self.array[:,:] **= other + else: + self.array[:,:] = self._safe_cast(self.array ** other) return self def __neg__(self): diff --git a/pyproject.toml b/pyproject.toml index a7d1740add..ce74a191b1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,2 +1,7 @@ [build-system] -requires = ["setuptools>=38,<72", "wheel", "pybind11>=2.2"] +requires = ["setuptools>=38", "wheel", "pybind11>=2.2"] + +[tool.pytest.ini_options] +testpaths = [ + "tests", +] diff --git a/requirements.txt b/requirements.txt index 52b065502d..cc9a22fe01 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,7 +4,7 @@ # These are in conda_requirements.txt. If using that, you may prefer to do # conda install -c conda-forge --file conda_requirements.txt # prior to running pip install -r requirements.txt -setuptools>=38,<72 +setuptools>=38 numpy>=1.17 astropy>=2.0 pybind11>=2.2 diff --git a/setup.py b/setup.py index d18d9332ca..6412d173f7 100644 --- a/setup.py +++ b/setup.py @@ -102,10 +102,12 @@ def all_files_from(dir, ext=''): } # If we build with debug, undefine NDEBUG flag -# Note: setuptools stopped allowing --debug, so if we need this, we'll need to find another -# mechanism. +# Note: setuptools stopped allowing --debug, so edit this manually if you want debugging. +# (The other debug variable is just for verbose output to debug this setup script.) +full_debug = False + undef_macros = [] -if "--debug" in sys.argv: +if full_debug: undef_macros+=['NDEBUG'] for name in copt.keys(): if name != 'unknown': @@ -118,7 +120,7 @@ def all_files_from(dir, ext=''): # Verbose is the default for setuptools logging, but if it's on the command line, we take it # to mean that we should also be verbose. -if "--debug" in sys.argv or "--verbose" in sys.argv: +if full_debug or "--verbose" in sys.argv: debug = True local_tmp = 'tmp' @@ -1196,6 +1198,9 @@ def run(self): if int(os.environ.get('GALSIM_BUILD_SHARED', 0)): self.run_command("build_shared_clib") + if int(os.environ.get('GALSIM_RUN_TEST', 0)): + self.run_command("run_cpp_test") + class my_install(install): user_options = install.user_options + [('njobs=', 'j', "Number of jobs to use for compiling")] @@ -1227,24 +1232,9 @@ def run(self): install_scripts.run(self) self.distribution.script_install_dir = self.install_dir -class my_test(test): - # TODO: setuptools v72 deprecated python setup.py test, so we need to figure out another - # way to test the C++ code. For now, we are pinning setuptools to <72. - - # cf. https://pytest.readthedocs.io/en/2.7.3/goodpractises.html - user_options = [('njobs=', 'j', "Number of jobs to use in py.test")] - - def initialize_options(self): - test.initialize_options(self) - self.pytest_args = None - self.njobs = None - - def finalize_options(self): - test.finalize_options(self) - self.test_args = [] - self.test_suite = True +class my_run_cpp_test(my_build_ext): - def run_cpp_tests(self): + def run(self): builder = self.distribution.get_command_obj('build_ext') compiler = builder.compiler cflags, lflags = fix_compiler(compiler, 1) @@ -1306,32 +1296,6 @@ def run_cpp_tests(self): raise RuntimeError("C++ tests failed") print("All C++ tests passed.") - def run_tests(self): - - if int(os.environ.get('GALSIM_TEST_PY', 1)): - njobs = parse_njobs(self.njobs, 'pytest', 'test') - pytest_args = ['-n=%d'%njobs, '--timeout=60'] - original_dir = os.getcwd() - os.chdir('tests') - test_files = glob.glob('test*.py') - - import pytest - pytest.main(['--version']) - errno = pytest.main(pytest_args + test_files) - py_err = errno != 0 - - os.chdir(original_dir) - - # Build and run the C++ tests - if int(os.environ.get('GALSIM_TEST_CPP', 1)): - self.run_cpp_tests() - - if int(os.environ.get('GALSIM_TEST_PY', 1)): - if py_err: - raise RuntimeError("Some Python tests failed") - else: - print("All python tests passed.") - lib=("galsim", {'sources' : cpp_sources, 'depends' : headers + inst, @@ -1343,7 +1307,7 @@ def run_tests(self): undef_macros = undef_macros, extra_link_args = ["-lfftw3"]) -build_dep = ['setuptools>=38,<72', 'pybind11>=2.2', 'numpy>=1.17'] +build_dep = ['setuptools>=38', 'pybind11>=2.2', 'numpy>=1.17'] run_dep = ['astropy', 'LSSTDESC.Coord'] test_dep = ['pytest', 'pytest-xdist', 'pytest-timeout', 'scipy', 'pyyaml'] @@ -1366,7 +1330,7 @@ def run_tests(self): print('GalSim version is %s'%(galsim_version)) # Write a Version.h file that has this information for people using the C++ library. -vi = re.split('\.|-',galsim_version) +vi = re.split(r'\.|-',galsim_version) version_info = tuple([int(x) for x in vi if x.isdigit()]) if len(version_info) == 2: version_info = version_info + (0,) @@ -1432,7 +1396,7 @@ def run_tests(self): 'install': my_install, 'install_scripts': my_install_scripts, 'easy_install': my_easy_install, - 'test': my_test, + 'run_cpp_test': my_run_cpp_test, }, entry_points = {'console_scripts' : [ 'galsim = galsim.__main__:run_main', diff --git a/tests/conftest.py b/tests/conftest.py index 2ecbe8f384..3fc11db2d0 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,11 @@ +import pytest + def pytest_addoption(parser): parser.addoption( "--run_slow", action="store_true", default=False, help="Run slow tests" ) + +# cf. https://stackoverflow.com/questions/62044541/change-pytest-working-directory-to-test-case-directory +@pytest.fixture(autouse=True) +def change_test_dir(request, monkeypatch): + monkeypatch.chdir(request.fspath.dirname) diff --git a/tests/run_examples.py b/tests/run_examples.py index f99c0b18db..413960a49d 100644 --- a/tests/run_examples.py +++ b/tests/run_examples.py @@ -26,6 +26,7 @@ import sys import logging import shutil +import pytest import galsim @@ -65,8 +66,10 @@ def check_same(f1, f2): logging.basicConfig(format="%(message)s", stream=sys.stdout) +@pytest.fixture(scope="module", autouse=True) @in_examples def setup(): + print("Removing output dirs") remove_dir('output') remove_dir('output_yaml') remove_dir('output_json')