diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index 6dcd163d..754745fd 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -15,13 +15,26 @@ jobs: brew: - hdf5-mpi envs: | - # Main tests + # Tests that don't rely on Fortran binaries + - linux: py39-test-nobinaries + - linux: py310-test-nobinaries + - linux: py311-test-nobinaries + - linux: py312-test-nobinaries + - macos: py39-test-nobinaries + - macos: py310-test-nobinaries + - macos: py311-test-nobinaries + - macos: py312-test-nobinaries + - windows: py39-test-nobinaries + - windows: py310-test-nobinaries + - windows: py311-test-nobinaries + - windows: py312-test-nobinaries + + # Main tests including Fortran binaries - linux: py39-test - # - macos: py310-test-macos - # runs-on: macos-14 + - linux: py310-test - linux: py311-test - # - macos: py312-test-macos - # runs-on: macos-14 + - linux: py312-test + # Bit-level tests - linux: py39-test-bitlevel runs-on: ubuntu-20.04 diff --git a/hyperion/__init__.py b/hyperion/__init__.py index f1feff3c..27f93673 100644 --- a/hyperion/__init__.py +++ b/hyperion/__init__.py @@ -1,71 +1,3 @@ from __future__ import print_function, division from .version import __version__ - -# Set up the test function -_test_runner = None - - -def _get_test_runner(): - from .testing.helper import TestRunner - return TestRunner(__path__[0]) - - -def test(package=None, test_path=None, args=None, plugins=None, - verbose=False, pastebin=None, generate_reference=False, - bit_level_tests=False, coverage=False): - ''' - Run Hyperion tests using py.test. A proper set of arguments is - constructed and passed to `pytest.main`. - - Parameters - ---------- - package : str, optional - The name of a specific package to test, e.g. 'model' or - 'densities'. If nothing is specified all default Hyperion tests - are run. - - test_path : str, optional - Specify location to test by path. May be a single file or - directory. Must be specified absolutely or relative to the - calling directory. - - args : str, optional - Additional arguments to be passed to `pytest.main` in the `args` - keyword argument. - - plugins : list, optional - Plugins to be passed to `pytest.main` in the `plugins` keyword - argument. - - verbose : bool, optional - Convenience option to turn on verbose output from py.test. Passing - True is the same as specifying `-v` in `args`. - - pastebin : {'failed','all',None}, optional - Convenience option for turning on py.test pastebin output. Set to - 'failed' to upload info for failed tests, or 'all' to upload info - for all tests. - - generate_reference : str - Generate reference results for bit-level tests - - bit_level_tests : bool - Run bit-level tests. These are time-consuming tests that check the - exact validity of the output, but they are disabled by default. - - coverage : bool, optional - Generate a test coverage report. The result will be placed in - the directory htmlcov. - - See Also - -------- - pytest.main : py.test function wrapped by `run_tests`. - - ''' - test_runner = _get_test_runner() - return test_runner.run_tests( - package=package, test_path=test_path, args=args, - plugins=plugins, verbose=verbose, pastebin=pastebin, - generate_reference=generate_reference, - bit_level_tests=bit_level_tests, coverage=coverage) diff --git a/hyperion/grid/amr_grid.py b/hyperion/grid/amr_grid.py index 783c999b..ef74c3ba 100644 --- a/hyperion/grid/amr_grid.py +++ b/hyperion/grid/amr_grid.py @@ -608,10 +608,6 @@ def from_yt(cls, ds, quantity_mapping={}): """ import yt - from distutils.version import LooseVersion - - if not LooseVersion(yt.__version__) >= LooseVersion('3'): - raise ImportError("yt 3.0 or later is required") from .yt_wrappers import yt_dataset_to_amr_grid return yt_dataset_to_amr_grid(ds, quantity_mapping=quantity_mapping) diff --git a/hyperion/grid/tests/test_yt.py b/hyperion/grid/tests/test_yt.py index c11220b0..80f61680 100644 --- a/hyperion/grid/tests/test_yt.py +++ b/hyperion/grid/tests/test_yt.py @@ -1,7 +1,6 @@ import os import sys from copy import deepcopy -from distutils.version import LooseVersion import h5py import numpy as np import pytest @@ -21,10 +20,7 @@ except: YT_VERSION = None else: - if LooseVersion(yt.__version__) >= LooseVersion('3'): - YT_VERSION = 3 - else: - YT_VERSION = 2 + YT_VERSION = 3 DATA = os.path.join(os.path.dirname(__file__), 'data') @@ -91,7 +87,8 @@ def test_to_yt(self, tmpdir, grid_type): p.save(tmpdir.join('test.png').strpath) -@pytest.mark.skipif("YT_VERSION is None or YT_VERSION < 3") +@pytest.mark.requires_hyperion_binaries +@pytest.mark.skipif("YT_VERSION is None") def test_from_yt(tmpdir): from yt import load diff --git a/hyperion/grid/yt2_wrappers.py b/hyperion/grid/yt2_wrappers.py deleted file mode 100644 index 4d2f84bd..00000000 --- a/hyperion/grid/yt2_wrappers.py +++ /dev/null @@ -1,373 +0,0 @@ -from __future__ import print_function, division - -import numpy as np - -import yt.frontends.stream.api as stream -from yt.utilities.io_handler import BaseIOHandler -from yt.utilities.definitions import mpc_conversion - - -class HyperionIOHandler(BaseIOHandler): - - def __init__(self, grids, dust_id): - if np.array(grids[0][grids[0].keys()[0]]).ndim == 4: - self.dust_id = dust_id - else: - self.dust_id = None - self.grids = grids - BaseIOHandler.__init__(self) - - def _read_data_set(self, grid, field): - if self.dust_id is None: - return np.array(self.grids[grid.id][field.lower()].transpose()) - else: - return np.array(self.grids[grid.id][field.lower()][self.dust_id].transpose()) - - def _read_data_slice(self, grid, field, axis, coord): - sl = [slice(None), slice(None), slice(None)] - sl[axis] = slice(coord, coord + 1) - return self._read_data_set(grid, field)[sl] - - -class StreamFieldData(object): - - def __init__(self, fields): - self.fields = fields - - @property - def all_fields(self): - return self.fields - - -def almost_equal(a, b): - return a / b < 1. + 1.e-4 and b / a < 1. + 1.e-4 - - -def amr_grid_to_yt_stream(levels, dust_id=0): - - # Try and guess the refinement ratio - if it is not constant, then - # we can't use yt - - if len(levels) == 0 or len(levels[0].grids) == 0: - raise Exception("Need at least one level with one grid to convert to a yt object") - elif len(levels) == 1: - refine = 2 - else: - dx = [] - dy = [] - dz = [] - for ilevel, level in enumerate(levels): - for igrid, grid in enumerate(level.grids): - gdx = (grid.xmax - grid.xmin) / float(grid.nx) - gdy = (grid.ymax - grid.ymin) / float(grid.ny) - gdz = (grid.zmax - grid.zmin) / float(grid.nz) - if igrid == 0: - dx.append(gdx) - dy.append(gdy) - dz.append(gdz) - else: - if not almost_equal(dx[-1], gdx): - raise Exception("dx scale differs between grids in level %i (expected %g and got %g)" % (ilevel, dx[-1], gdx)) - if not almost_equal(dy[-1], gdy): - raise Exception("dy scale differs between grids in level %i (expected %g and got %g)" % (ilevel, dy[-1], gdy)) - if not almost_equal(dz[-1], gdz): - raise Exception("dz scale differs between grids in level %i (expected %g and got %g)" % (ilevel, dz[-1], gdz)) - dx = np.array(dx) - dy = np.array(dy) - dz = np.array(dz) - refine_x = dx[:-1] / dx[1:] - refine_y = dy[:-1] / dy[1:] - refine_z = dz[:-1] / dz[1:] - for i in range(len(levels) - 1): - if abs(refine_x[i] - round(refine_x[i])) > 1.e-5: - raise Exception("refinement ratio is not an integer (%g)" % refine_x[i]) - if abs(refine_y[i] - round(refine_y[i])) > 1.e-5: - raise Exception("refinement ratio is not an integer (%g)" % refine_y[i]) - if abs(refine_z[i] - round(refine_z[i])) > 1.e-5: - raise Exception("refinement ratio is not an integer (%g)" % refine_z[i]) - refine_x = np.round(refine_x).astype(int) - refine_y = np.round(refine_y).astype(int) - refine_z = np.round(refine_z).astype(int) - if not np.all(np.hstack([refine_x, refine_y, refine_z]) == refine_x[0]): - raise Exception("refinement ratio changes between levels and/or directions (x = %s, y = %s, z = %s)" % (str(refine_x), str(refine_y), str(refine_z))) - refine = int(refine_x[0]) - - xmin = ymin = zmin = +np.inf - xmax = ymax = zmax = -np.inf - - left_edge = [] - right_edge = [] - dimensions = [] - level_ids = [] - - n_grids = 0 - grids = [] - - for ilevel, level in enumerate(levels): - - for grid in level.grids: - - left_edge.append([grid.xmin, - grid.ymin, - grid.zmin]) - - right_edge.append([grid.xmax, - grid.ymax, - grid.zmax]) - - dimensions.append([grid.nx, - grid.ny, - grid.nz]) - - level_ids.append(ilevel) - - n_grids += 1 - - xmin = min(xmin, grid.xmin) - xmax = max(xmax, grid.xmax) - ymin = min(ymin, grid.ymin) - ymax = max(ymax, grid.ymax) - zmin = min(zmin, grid.zmin) - zmax = max(zmax, grid.zmax) - - grids.append(grid.quantities) - - left_edge = np.array(left_edge, dtype=np.float64) - right_edge = np.array(right_edge, dtype=np.float64) - dimensions = np.array(dimensions, dtype=np.int32) - - # The reshape is necessary due to a strange Numpy bug - level_ids = np.array(level_ids, dtype=np.int32).reshape((len(level_ids), 1)) - - parent_ids = None - particle_count = np.zeros((n_grids, 1), dtype='int32') - processor_ids = np.zeros(n_grids) - - grid = levels[0].grids[0] - - # Determine domain resolution - - dx = (grid.xmax - grid.xmin) / float(grid.nx) - nx = int(round((xmax - xmin) / dx)) - - dy = (grid.ymax - grid.ymin) / float(grid.ny) - ny = int(round((ymax - ymin) / dy)) - - dz = (grid.zmax - grid.zmin) / float(grid.nz) - nz = int(round((zmax - zmin) / dz)) - - # Determine fields - - fields = grid.quantities.keys() - - # Set up StreamHandler - - handler = stream.StreamHandler( - left_edge[:], - right_edge[:], - dimensions[:], - level_ids[:], - parent_ids, - particle_count[:], - processor_ids[:], - StreamFieldData(fields), - HyperionIOHandler(grids, dust_id), - ) - - handler.name = 'hyperion' - handler.domain_left_edge = np.array([xmin, ymin, zmin]) - handler.domain_right_edge = np.array([xmax, ymax, zmax]) - handler.refine_by = refine - handler.dimensionality = 3 - handler.domain_dimensions = np.array([nx, ny, nz]) - handler.simulation_time = 0.0 - handler.cosmology_simulation = 0 - - spf = stream.StreamStaticOutput(handler) - spf.units["cm"] = 1.0 - spf.units["unitary"] = 1.0 / ((spf.domain_right_edge - spf.domain_left_edge).max()) - spf.units['1'] = 1.0 - box_in_mpc = 1.0 / mpc_conversion['cm'] - for unit in mpc_conversion.keys(): - spf.units[unit] = mpc_conversion[unit] * box_in_mpc - - return spf - - -def edge_list(refined, xmin, xmax, ymin, ymax, zmin, zmax, i=0): - - if not refined[i]: - return [], i - else: - xmid = (xmin + xmax) * 0.5 - ymid = (ymin + ymax) * 0.5 - zmid = (zmin + zmax) * 0.5 - e1, i = edge_list(refined, xmin, xmid, ymin, ymid, zmin, zmid, i=i + 1) - e2, i = edge_list(refined, xmid, xmax, ymin, ymid, zmin, zmid, i=i + 1) - e3, i = edge_list(refined, xmin, xmid, ymid, ymax, zmin, zmid, i=i + 1) - e4, i = edge_list(refined, xmid, xmax, ymid, ymax, zmin, zmid, i=i + 1) - e5, i = edge_list(refined, xmin, xmid, ymin, ymid, zmid, zmax, i=i + 1) - e6, i = edge_list(refined, xmid, xmax, ymin, ymid, zmid, zmax, i=i + 1) - e7, i = edge_list(refined, xmin, xmid, ymid, ymax, zmid, zmax, i=i + 1) - e8, i = edge_list(refined, xmid, xmax, ymid, ymax, zmid, zmax, i=i + 1) - return [(xmin, xmax, ymin, ymax, zmin, zmax)] + e1 + e2 + e3 + e4 + e5 + e6 + e7 + e8, i - - -def decompose_quantity(refined, array, i=0): - subarray = [] - newarray = [] - if refined[i]: - for s in range(8): - i = i + 1 - subarray.append(array[i]) - a, i = decompose_quantity(refined, array, i=i) - newarray += a - subarray = np.array(subarray).reshape((2, 2, 2)) - return [subarray] + newarray, i - else: - return [], i - - -def level_list(refined, i=0, level=0): - if refined[i]: - levels = [level] - for s in range(8): - l, i = level_list(refined, i=i + 1, level=level + 1) - levels = levels + l - return levels, i - else: - return [], i - - -class HyperionIOHandlerOct(BaseIOHandler): - - def __init__(self, quantities): - self.quantities = quantities - BaseIOHandler.__init__(self) - - def _read_data_set(self, grid, field): - return self.quantities[field.lower()][grid.id].transpose() - - def _read_data_slice(self, grid, field, axis, coord): - sl = [slice(None), slice(None), slice(None)] - sl[axis] = slice(coord, coord + 1) - return self._read_data_set(grid, field)[sl] - - -def octree_grid_to_yt_stream(grid, dust_id=0): - - xmin = grid.x - grid.dx - xmax = grid.x + grid.dx - ymin = grid.y - grid.dy - ymax = grid.y + grid.dy - zmin = grid.z - grid.dz - zmax = grid.z + grid.dz - - e, i = edge_list(grid.refined, xmin, xmax, ymin, ymax, zmin, zmax) - - e = np.array(e) - - left_edge = e[:, ::2].astype(np.float64) - right_edge = e[:, 1::2].astype(np.float64) - dimensions = np.ones((np.sum(grid.refined), 3), dtype=np.int32) * 2 - level_ids = np.array(level_list(grid.refined)[0], dtype=np.int32) - level_ids = level_ids.reshape((len(level_ids), 1)) - - n_grids = np.sum(grid.refined) - - parent_ids = None - particle_count = np.zeros((n_grids, 1), dtype='int32') - processor_ids = np.zeros(n_grids) - - # Determine fields - - fields = grid.quantities.keys() - - # Set up StreamHandler - - quantities = {} - for field in grid.quantities: - quantities[field] = decompose_quantity(grid.refined, grid.quantities[field][dust_id])[0] - - handler = stream.StreamHandler( - left_edge[:], - right_edge[:], - dimensions[:], - level_ids[:], - parent_ids, - particle_count[:], - processor_ids[:], - StreamFieldData(fields), - HyperionIOHandlerOct(quantities), - ) - - handler.name = 'hyperion' - handler.domain_left_edge = np.array([xmin, ymin, zmin]) - handler.domain_right_edge = np.array([xmax, ymax, zmax]) - handler.refine_by = 2 - handler.dimensionality = 3 - handler.domain_dimensions = np.array([2, 2, 2]) - handler.simulation_time = 0.0 - handler.cosmology_simulation = 0 - - spf = stream.StreamStaticOutput(handler) - spf.units["cm"] = 1.0 - spf.units["unitary"] = 1.0 / ((spf.domain_right_edge - spf.domain_left_edge).max()) - spf.units['1'] = 1.0 - box_in_mpc = 1.0 / mpc_conversion['cm'] - for unit in mpc_conversion.keys(): - spf.units[unit] = mpc_conversion[unit] * box_in_mpc - - return spf - - -def cartesian_grid_to_yt_stream(grid, xmin, xmax, ymin, ymax, zmin, zmax, dust_id=0): - - nz, ny, nx = grid.shape - - grids = [grid.quantities] - left_edge = np.array([[xmin, ymin, zmin]], dtype=np.float64) - right_edge = np.array([[xmax, ymax, zmax]], dtype=np.float64) - dimensions = np.array([[nx, ny, nz]], dtype=np.int32) - level_ids = np.array([[0]], dtype=np.int32).reshape((1, 1)) - - parent_ids = None - particle_count = np.zeros((1, 1), dtype='int32') - processor_ids = np.zeros(1) - - # Determine fields - - fields = grid.quantities.keys() - - # Set up StreamHandler - - handler = stream.StreamHandler( - left_edge[:], - right_edge[:], - dimensions[:], - level_ids[:], - parent_ids, - particle_count[:], - processor_ids[:], - StreamFieldData(fields), - HyperionIOHandler(grids, dust_id), - ) - - handler.name = 'hyperion' - handler.domain_left_edge = np.array([xmin, ymin, zmin]) - handler.domain_right_edge = np.array([xmax, ymax, zmax]) - handler.refine_by = 2 - handler.dimensionality = 3 - handler.domain_dimensions = np.array([nx, ny, nz]) - handler.simulation_time = 0.0 - handler.cosmology_simulation = 0 - - spf = stream.StreamStaticOutput(handler) - spf.units["cm"] = 1.0 - spf.units["unitary"] = 1.0 / ((spf.domain_right_edge - spf.domain_left_edge).max()) - spf.units['1'] = 1.0 - box_in_mpc = 1.0 / mpc_conversion['cm'] - for unit in mpc_conversion.keys(): - spf.units[unit] = mpc_conversion[unit] * box_in_mpc - - return spf diff --git a/hyperion/grid/yt_wrappers.py b/hyperion/grid/yt_wrappers.py index df701c41..c1eed66a 100644 --- a/hyperion/grid/yt_wrappers.py +++ b/hyperion/grid/yt_wrappers.py @@ -1,8 +1 @@ -from distutils.version import LooseVersion - -import yt - -if LooseVersion(yt.__version__) >= LooseVersion('3'): - from .yt3_wrappers import * -else: - from .yt2_wrappers import * +from .yt3_wrappers import * diff --git a/hyperion/model/tests/test_amr_checks.py b/hyperion/model/tests/test_amr_checks.py index bbc3d976..a7b4409a 100644 --- a/hyperion/model/tests/test_amr_checks.py +++ b/hyperion/model/tests/test_amr_checks.py @@ -1,5 +1,3 @@ -from distutils.version import LooseVersion - import pytest import numpy as np @@ -12,12 +10,10 @@ except: YT_VERSION = None else: - if LooseVersion(yt.__version__) >= LooseVersion('3'): - YT_VERSION = 3 - else: - YT_VERSION = 2 + YT_VERSION = 3 +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('direction'), ['x', 'y', 'z']) def test_amr_differing_widths(tmpdir, direction): @@ -57,6 +53,7 @@ def test_amr_differing_widths(tmpdir, direction): assert ('Grids 1 and 2 in level 1 have differing cell widths in the %s \n direction ( 5.0000E+00 and 5.0250E+00 respectively)' % direction) in open(log_file).read() +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('direction'), ['x', 'y', 'z']) def test_amr_misaligned_grids_same_level(tmpdir, direction): @@ -97,6 +94,7 @@ def test_amr_misaligned_grids_same_level(tmpdir, direction): assert ('Grids 1 and 2 in level 1 have edges that are not separated by \n an integer number of cells in the %s direction' % direction) in open(log_file).read() +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('direction'), ['x', 'y', 'z']) def test_amr_non_integer_refinement(tmpdir, direction): @@ -138,6 +136,7 @@ def test_amr_non_integer_refinement(tmpdir, direction): assert ('Refinement factor in the %s direction between level 1 and \n level 2 is not an integer (1.818)' % direction) in open(log_file).read() +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('direction'), ['x', 'y', 'z']) def test_amr_not_aligned_across_levels(tmpdir, direction): @@ -180,6 +179,7 @@ def test_amr_not_aligned_across_levels(tmpdir, direction): assert ('Grid 1 in level 2 is not aligned with cells in level 1 in the \n %s direction' % direction) in open(log_file).read() +@pytest.mark.requires_hyperion_binaries @pytest.mark.skipif("YT_VERSION is None") def test_shadowing_regression(tmpdir): diff --git a/hyperion/model/tests/test_analytical_yso.py b/hyperion/model/tests/test_analytical_yso.py index 042345f7..73ee9630 100644 --- a/hyperion/model/tests/test_analytical_yso.py +++ b/hyperion/model/tests/test_analytical_yso.py @@ -137,6 +137,7 @@ def test_analytical_yso_add_density(): assert exc.value.args[0] == 'add_density_grid cannot be used for AnalyticalYSOModel' +@pytest.mark.requires_hyperion_binaries def test_analytical_yso_use_quantities_invalid(tmpdir): output_file = tmpdir.join(random_id()).strpath @@ -155,6 +156,7 @@ def test_analytical_yso_use_quantities_invalid(tmpdir): assert exc.value.args[0] == "use_quantities cannot be used for AnalyticalYSOModel" +@pytest.mark.requires_hyperion_binaries def test_analytical_yso_use_geometry_invalid(tmpdir): output_file = tmpdir.join(random_id()).strpath diff --git a/hyperion/model/tests/test_filters.py b/hyperion/model/tests/test_filters.py index 134ea472..fce7a41c 100644 --- a/hyperion/model/tests/test_filters.py +++ b/hyperion/model/tests/test_filters.py @@ -71,24 +71,29 @@ def setup_class(self): def teardown_class(self): shutil.rmtree(self.tmpdir) + @pytest.mark.requires_hyperion_binaries def test_image_wav(self): image = self.m.get_image() np.testing.assert_allclose(image.nu, [2.60689094e+14, 1.39438353e+14]) np.testing.assert_allclose(image.wav, [1.15, 2.15]) + @pytest.mark.requires_hyperion_binaries def test_sed_wav(self): sed = self.m.get_sed() np.testing.assert_allclose(sed.nu, [2.60689094e+14, 1.39438353e+14]) np.testing.assert_allclose(sed.wav, [1.15, 2.15]) + @pytest.mark.requires_hyperion_binaries def test_image_shape(self): image = self.m.get_image() assert image.val.shape == (3, 20, 10, 2) + @pytest.mark.requires_hyperion_binaries def test_sed_shape(self): sed = self.m.get_sed() assert sed.val.shape == (3, 1, 2) + @pytest.mark.requires_hyperion_binaries def test_image_values(self): image = self.m.get_image(units='MJy/sr', distance=1) np.testing.assert_allclose(np.sum(image.val[:, :, :, 0]), 3438.059082285024, rtol=0.1) diff --git a/hyperion/model/tests/test_fortran.py b/hyperion/model/tests/test_fortran.py index d54b22e1..c306ec13 100644 --- a/hyperion/model/tests/test_fortran.py +++ b/hyperion/model/tests/test_fortran.py @@ -9,6 +9,7 @@ from .test_helpers import random_id, get_test_dust +@pytest.mark.requires_hyperion_binaries def test_point_source_outside_grid(tmpdir): dust = get_test_dust() @@ -30,6 +31,7 @@ def test_point_source_outside_grid(tmpdir): assert 'photon was not emitted inside a cell' in open(log_file).read() +@pytest.mark.requires_hyperion_binaries def test_unsorted_spectrum(tmpdir): m = Model() @@ -47,6 +49,7 @@ def test_unsorted_spectrum(tmpdir): assert 'spectrum frequency should be monotonically increasing' in open(log_file).read() +@pytest.mark.requires_hyperion_binaries def test_spectrum_dust_nooverlap(tmpdir): # Set up dust with a narrow frequency range diff --git a/hyperion/model/tests/test_get_quantities.py b/hyperion/model/tests/test_get_quantities.py index b38081b7..52c8ed35 100644 --- a/hyperion/model/tests/test_get_quantities.py +++ b/hyperion/model/tests/test_get_quantities.py @@ -1,3 +1,5 @@ +import pytest + import numpy as np from .. import Model @@ -5,6 +7,7 @@ from .test_helpers import get_test_dust +@pytest.mark.requires_hyperion_binaries def test_no_initial(tmpdir): m = Model() m.set_cartesian_grid([-1., 1.], [-1., 1.], [-1., 1.]) diff --git a/hyperion/model/tests/test_image.py b/hyperion/model/tests/test_image.py index d890201a..81ee17ec 100644 --- a/hyperion/model/tests/test_image.py +++ b/hyperion/model/tests/test_image.py @@ -14,6 +14,7 @@ from .test_helpers import get_test_dust, get_highly_reflective_dust +@pytest.mark.requires_hyperion_binaries class TestImageSimpleModel(object): def setup_class(self): @@ -109,6 +110,7 @@ def test_image_nodistance_units_invalid(self, units): wav, nufnu = self.m.get_image(units=units) +@pytest.mark.requires_hyperion_binaries class TestImageSimpleModelTrackingDetailed(object): def setup_class(self): @@ -197,6 +199,7 @@ def test_image_dust_invalid2(self): assert exc.value.args[0] == 'dust_id should be between 0 and 0' +@pytest.mark.requires_hyperion_binaries class TestImageSimpleModelTrackingScatterings(object): def setup_class(self): @@ -282,6 +285,7 @@ def test_image_n_scat_values(self): assert image.val.sum() == 0. +@pytest.mark.requires_hyperion_binaries class TestSimpleModelInside(object): def setup_class(self): @@ -320,6 +324,7 @@ def test_distance_fail(self): assert e.value.args[0] == 'Cannot specify distance for inside observers' +@pytest.mark.requires_hyperion_binaries def test_regression_depth_bug(tmpdir): """ This is a regression test for issue #21 reported by T. Bowers. If multiple @@ -377,6 +382,7 @@ def test_regression_depth_bug(tmpdir): assert image3.sum() == image2.sum() +@pytest.mark.requires_hyperion_binaries class TestImage(object): def setup_class(self): @@ -504,6 +510,7 @@ def test_unit_conversion(self): assert_array_almost_equal_nulp((ref.val / ref.nu), MJy_per_sr.val * 1.e-17 * MJy_per_sr.pix_area_sr, 10) +@pytest.mark.requires_hyperion_binaries class TestInsideImage(object): def setup_class(self): @@ -625,6 +632,7 @@ def test_unit_conversion(self): assert_array_almost_equal_nulp((ref.val / ref.nu), MJy_per_sr.val * 1.e-17 * MJy_per_sr.pix_area_sr[:, :, np.newaxis], 10) +@pytest.mark.requires_hyperion_binaries def test_flux_preserved_scatterings(tmpdir): # Regression test for issue #102 to ensure that flux is preserved when in # 'scatterings' mode @@ -670,6 +678,7 @@ def test_flux_preserved_scatterings(tmpdir): np.testing.assert_allclose(image1.val, source.val + dust.val) +@pytest.mark.requires_hyperion_binaries class TestImageStokesOption(object): def setup_class(self): diff --git a/hyperion/model/tests/test_minimum_energy.py b/hyperion/model/tests/test_minimum_energy.py index 3691207a..93cfa335 100644 --- a/hyperion/model/tests/test_minimum_energy.py +++ b/hyperion/model/tests/test_minimum_energy.py @@ -22,6 +22,7 @@ def teardown_module(module): shutil.rmtree(module.tmpdir) +@pytest.mark.requires_hyperion_binaries def test_minimum_temperature_scalar(tmpdir): input_file = tmpdir.join(random_id()).strpath @@ -36,6 +37,7 @@ def test_minimum_temperature_scalar(tmpdir): assert_array_almost_equal_nulp(t[0].array[0, 0, 0], 10., 10) +@pytest.mark.requires_hyperion_binaries def test_minimum_temperature_scalar_list(tmpdir): input_file = tmpdir.join(random_id()).strpath @@ -98,6 +100,7 @@ def test_minimum_temperature_scalar_invalid5(tmpdir): assert exc.value.args[0] == 'Number of minimum_temperature values should match number of dust types' +@pytest.mark.requires_hyperion_binaries def test_minimum_temperature_scalar_2(tmpdir): input_file = tmpdir.join(random_id()).strpath @@ -114,6 +117,7 @@ def test_minimum_temperature_scalar_2(tmpdir): assert_array_almost_equal_nulp(t[0].array[0, 0, 0], 10., 10) +@pytest.mark.requires_hyperion_binaries def test_minimum_temperature_scalar_list_2(tmpdir): input_file = tmpdir.join(random_id()).strpath @@ -143,6 +147,7 @@ def test_minimum_temperature_scalar_list_2_invalid(tmpdir): assert exc.value.args[0] == 'Number of minimum_temperature values should match number of dust types' +@pytest.mark.requires_hyperion_binaries def test_minimum_specific_energy_scalar(tmpdir): input_file = tmpdir.join(random_id()).strpath @@ -157,6 +162,7 @@ def test_minimum_specific_energy_scalar(tmpdir): assert_array_almost_equal_nulp(t[0].array[0, 0, 0], 2., 10) +@pytest.mark.requires_hyperion_binaries def test_minimum_specific_energy_scalar_list(tmpdir): input_file = tmpdir.join(random_id()).strpath @@ -219,6 +225,7 @@ def test_minimum_specific_energy_scalar_invalid5(tmpdir): assert exc.value.args[0] == 'Number of minimum_specific_energy values should match number of dust types' +@pytest.mark.requires_hyperion_binaries def test_minimum_specific_energy_scalar_2(tmpdir): input_file = tmpdir.join(random_id()).strpath @@ -235,6 +242,7 @@ def test_minimum_specific_energy_scalar_2(tmpdir): assert_array_almost_equal_nulp(t[0].array[0, 0, 0], 2., 10) +@pytest.mark.requires_hyperion_binaries def test_minimum_specific_energy_scalar_list_2(tmpdir): input_file = tmpdir.join(random_id()).strpath diff --git a/hyperion/model/tests/test_misc.py b/hyperion/model/tests/test_misc.py index a5471a90..b8b5b2a9 100644 --- a/hyperion/model/tests/test_misc.py +++ b/hyperion/model/tests/test_misc.py @@ -1,3 +1,4 @@ +import pytest import numpy as np from .. import Model @@ -5,6 +6,7 @@ from .test_helpers import get_test_model_noimaging, random_id +@pytest.mark.requires_hyperion_binaries def test_monochromatic_wav(tmpdir): model = Model() @@ -23,6 +25,7 @@ def test_monochromatic_wav(tmpdir): model.run(tmpdir.join(random_id()).strpath) +@pytest.mark.requires_hyperion_binaries def test_model_spectrum(tmpdir): model = get_test_model_noimaging() diff --git a/hyperion/model/tests/test_model.py b/hyperion/model/tests/test_model.py index 77db97ab..fa39641d 100644 --- a/hyperion/model/tests/test_model.py +++ b/hyperion/model/tests/test_model.py @@ -115,6 +115,7 @@ def test_mismatch_density_energy_2(self, tmpdir, grid_type): m.write(tmpdir.join(random_id()).strpath) assert exc.value.args[0] == "Not all dust lists in the grid have the same size" + @pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('grid_type'), ['car', 'sph', 'cyl', 'amr', 'oct']) def test_add_density(self, tmpdir, grid_type): m = Model() @@ -127,6 +128,7 @@ def test_add_density(self, tmpdir, grid_type): m.write(tmpdir.join(random_id()).strpath) m.run(tmpdir.join(random_id()).strpath) + @pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('grid_type'), ['car', 'sph', 'cyl', 'amr', 'oct']) def test_add_density_from_grid(self, tmpdir, grid_type): m = Model() @@ -144,6 +146,7 @@ def test_add_density_from_grid(self, tmpdir, grid_type): m.write(tmpdir.join(random_id()).strpath) m.run(tmpdir.join(random_id()).strpath) + @pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('grid_type'), ['car', 'sph', 'cyl', 'amr', 'oct']) def test_merge_density(self, tmpdir, grid_type): m = Model() @@ -275,6 +278,7 @@ def test_merge_object_incomplete(self, grid_type): assert m.grid.n_dust == 2 +@pytest.mark.requires_hyperion_binaries def test_dust_mix(tmpdir): # This is a regression test for a bug which caused the code to crash if # isotropic dust and non-isotropic dust were used together. @@ -299,6 +303,7 @@ def test_dust_mix(tmpdir): m.run(tmpdir.join(random_id()).strpath) +@pytest.mark.requires_hyperion_binaries def test_voronoi_basics(tmpdir): # A test to check the interaction between C++, Fortran and Python, # and to test the internal consistency of the Voronoi gridding. @@ -365,6 +370,7 @@ def test_dust_changed_save(tmpdir): m.write(tmpdir.join(random_id()).strpath, copy=False) +@pytest.mark.requires_hyperion_binaries def test_model_minimal(tmpdir): m = Model() diff --git a/hyperion/model/tests/test_model_copy_link.py b/hyperion/model/tests/test_model_copy_link.py index a54648d3..84759c0d 100644 --- a/hyperion/model/tests/test_model_copy_link.py +++ b/hyperion/model/tests/test_model_copy_link.py @@ -22,6 +22,7 @@ def setup_method(self, method): self.model = get_test_model_noimaging() + @pytest.mark.requires_hyperion_binaries def test_copy_filename(self, tmpdir): dust_file = tmpdir.join(random_id()).strpath self.dust.write(dust_file) @@ -29,11 +30,13 @@ def test_copy_filename(self, tmpdir): self.model.write(tmpdir.join(random_id()).strpath, copy=True) self.model.run(tmpdir.join(random_id()).strpath) + @pytest.mark.requires_hyperion_binaries def test_copy_object(self, tmpdir): self.model.add_density_grid(self.density, self.dust) self.model.write(tmpdir.join(random_id()).strpath, copy=True) self.model.run(tmpdir.join(random_id()).strpath) + @pytest.mark.requires_hyperion_binaries def test_link_filename(self, tmpdir): dust_file = tmpdir.join(random_id()).strpath self.dust.write(dust_file) @@ -48,6 +51,7 @@ def test_link_object(self, tmpdir): assert e.value.args[0] == 'Dust properties are not located in a file, so cannot link. Use copy=True or write the dust properties to a file first' +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('write_copy'), [True, False]) def test_input_link(write_copy, tmpdir): @@ -74,6 +78,7 @@ def test_input_link(write_copy, tmpdir): f.close() +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('write_copy'), [True, False]) def test_input_copy(write_copy, tmpdir): diff --git a/hyperion/model/tests/test_model_io.py b/hyperion/model/tests/test_model_io.py index defc192c..991c1372 100644 --- a/hyperion/model/tests/test_model_io.py +++ b/hyperion/model/tests/test_model_io.py @@ -130,6 +130,7 @@ def test_io_voronoi(tmpdir): m2 = Model.read(filename) +@pytest.mark.requires_hyperion_binaries def test_read_then_monochromatic(tmpdir): # If we run a model then read in the output and make a new model with diff --git a/hyperion/model/tests/test_mono.py b/hyperion/model/tests/test_mono.py index 4c95b405..198a56c6 100644 --- a/hyperion/model/tests/test_mono.py +++ b/hyperion/model/tests/test_mono.py @@ -1,9 +1,12 @@ +import pytest + import numpy as np from ..model import Model from .test_helpers import random_id, get_test_dust +@pytest.mark.requires_hyperion_binaries def test_mono_zero_prob(tmpdir): # Check that when total probability is zero in a given dust type for a given wavelength, the code doesn't crash @@ -32,6 +35,7 @@ def test_mono_zero_prob(tmpdir): m.run(tmpdir.join(random_id()).strpath) +@pytest.mark.requires_hyperion_binaries def test_check_weighting(tmpdir): ''' This is a regression test for a bug that caused incorrect weighting of the diff --git a/hyperion/model/tests/test_mrw.py b/hyperion/model/tests/test_mrw.py index a1b6bc96..7c922e92 100644 --- a/hyperion/model/tests/test_mrw.py +++ b/hyperion/model/tests/test_mrw.py @@ -29,6 +29,7 @@ 21211.08] +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('density_ref', 'temperature_ref'), zip(D_REF, T_REF)) def test_single_temperature(tmpdir, density_ref, temperature_ref): @@ -62,6 +63,7 @@ def test_single_temperature(tmpdir, density_ref, temperature_ref): assert temperature_ref / temperature < 1.1 and temperature / temperature_ref < 1.1 +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('density_ref', 'temperature_ref'), zip(D_REF, T_REF)) def test_multi_temperature(tmpdir, density_ref, temperature_ref): diff --git a/hyperion/model/tests/test_output_physical.py b/hyperion/model/tests/test_output_physical.py index ec975cd0..911f2d47 100644 --- a/hyperion/model/tests/test_output_physical.py +++ b/hyperion/model/tests/test_output_physical.py @@ -7,6 +7,7 @@ from .test_helpers import get_test_model_noimaging, get_test_dust +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('output'), ['density', 'density_diff', 'n_photons', 'specific_energy']) def test_output_grids_exist(tmpdir, output): @@ -36,6 +37,7 @@ def test_output_grids_exist(tmpdir, output): model_out.get_quantities()['temperature'] +@pytest.mark.requires_hyperion_binaries def test_output_grids_density(tmpdir): # Get a dust object diff --git a/hyperion/model/tests/test_propagation.py b/hyperion/model/tests/test_propagation.py index 32750bf0..df681516 100644 --- a/hyperion/model/tests/test_propagation.py +++ b/hyperion/model/tests/test_propagation.py @@ -21,6 +21,7 @@ def any_photons_killed(handle): handle.attrs['killed_photons_int_raytracing'] != 0) +@pytest.mark.requires_hyperion_binaries class TestCartesianBase(object): x = np.linspace(-10., 10., 15) @@ -148,6 +149,7 @@ class TestCartesianSmall(TestCartesianBase): z = np.linspace(-10.e-20, 10.e-20, 15) +@pytest.mark.requires_hyperion_binaries class TestSphericalBase(object): r = np.linspace(0., 10., 15) @@ -350,6 +352,7 @@ class TestSphericalSmall(object): p = np.linspace(0., 2. * np.pi, 15) +@pytest.mark.requires_hyperion_binaries class TestCylindricalBase(object): w = np.linspace(0., 10., 15) diff --git a/hyperion/model/tests/test_sed.py b/hyperion/model/tests/test_sed.py index 2a31f950..7645dd50 100644 --- a/hyperion/model/tests/test_sed.py +++ b/hyperion/model/tests/test_sed.py @@ -16,6 +16,7 @@ from .test_helpers import get_test_dust +@pytest.mark.requires_hyperion_binaries class TestSEDSimpleModel(object): def setup_class(self): @@ -136,6 +137,7 @@ def test_sed_nodistance_units_invalid(self, units): assert exc.value.args[0] == 'Since distance= is not specified, units should be set to ergs/s' +@pytest.mark.requires_hyperion_binaries class TestSEDSimpleModelTrackingDetailed(object): def setup_class(self): @@ -218,6 +220,7 @@ def test_sed_dust_invalid2(self): assert exc.value.args[0] == 'dust_id should be between 0 and 0' +@pytest.mark.requires_hyperion_binaries class TestSEDSimpleModelTrackingScatterings(object): def setup_class(self): @@ -302,6 +305,7 @@ def test_sed_n_scat_values(self): assert sed.val.sum() == 0. +@pytest.mark.requires_hyperion_binaries class TestSED(object): def setup_class(self): @@ -398,6 +402,7 @@ def test_unit_conversion(self): assert_array_almost_equal_nulp((ref.val / ref.nu), mJy.val * 1.e-26, 10) +@pytest.mark.requires_hyperion_binaries class TestSEDStokesOption(object): def setup_class(self): diff --git a/hyperion/model/tests/test_specific_energy_type.py b/hyperion/model/tests/test_specific_energy_type.py index cf7458e6..cd67ba36 100644 --- a/hyperion/model/tests/test_specific_energy_type.py +++ b/hyperion/model/tests/test_specific_energy_type.py @@ -7,6 +7,7 @@ from .test_helpers import random_id, get_test_dust +@pytest.mark.requires_hyperion_binaries class TestSpecificEnergyType(object): def setup_method(self, method): diff --git a/hyperion/model/tests/test_use_grid_from_disk.py b/hyperion/model/tests/test_use_grid_from_disk.py index 001440a2..d537b782 100644 --- a/hyperion/model/tests/test_use_grid_from_disk.py +++ b/hyperion/model/tests/test_use_grid_from_disk.py @@ -1,3 +1,5 @@ +import pytest + import h5py import numpy as np @@ -9,6 +11,7 @@ from ...grid import CartesianGrid +@pytest.mark.requires_hyperion_binaries def test_use_grid_from_file(tmpdir): grid_file = tmpdir.join(random_id()).strpath diff --git a/hyperion/model/tests/test_use_previous.py b/hyperion/model/tests/test_use_previous.py index 2912092b..43a30ea5 100644 --- a/hyperion/model/tests/test_use_previous.py +++ b/hyperion/model/tests/test_use_previous.py @@ -11,6 +11,7 @@ from .test_helpers import get_test_dust +@pytest.mark.requires_hyperion_binaries @pytest.mark.parametrize(('grid_type', 'copy'), list(product(['car', 'sph', 'cyl', 'amr', 'oct'], [(True, True), (False, True), (False, False)]))) def test_use_quantities(tmpdir, grid_type, copy): diff --git a/hyperion/testing/__init__.py b/hyperion/testing/__init__.py index d533863b..e69de29b 100644 --- a/hyperion/testing/__init__.py +++ b/hyperion/testing/__init__.py @@ -1 +0,0 @@ -from . import helper diff --git a/hyperion/testing/helper.py b/hyperion/testing/helper.py deleted file mode 100644 index 9f086f70..00000000 --- a/hyperion/testing/helper.py +++ /dev/null @@ -1,191 +0,0 @@ -''' -This module prvoides the tools used to internally run the Hyperion test suite -from the installed version. It makes use of the `pytest` testing framework, -and is adapted from Astropy. -''' - -import shlex -import sys -import os -import subprocess -import tempfile -import shutil - -from distutils.core import Command - - -class TestRunner(object): - - def __init__(self, base_path): - self.base_path = base_path - - def run_tests(self, package=None, test_path=None, args=None, plugins=None, - verbose=False, pastebin=None, generate_reference=False, - bit_level_tests=False, coverage=False): - - if package is None: - package_path = self.base_path - else: - package_path = os.path.join(self.base_path, - package.replace('.', os.path.sep)) - - if not os.path.isdir(package_path): - raise ValueError('Package not found: {0}'.format(package)) - - if test_path: - package_path = os.path.join(package_path, - os.path.abspath(test_path)) - - all_args = package_path - - # add any additional args entered by the user - if args is not None: - all_args += ' {0}'.format(args) - - # add verbosity flag - if verbose: - all_args += ' -v' - - # turn on pastebin output - if pastebin is not None: - if pastebin in ['failed', 'all']: - all_args += ' --pastebin={0}'.format(pastebin) - else: - raise ValueError("pastebin should be 'failed' or 'all'") - - if generate_reference: - if generate_reference.startswith('-'): - raise Exception("Need to specify output directory for generating reference files") - if not generate_reference.startswith('/'): - raise Exception("Need to specify output directory for generating reference files as an absolute path") - all_args += ' --generate-reference={0}'.format(generate_reference) - all_args += ' --enable-bit-level-tests' - - if bit_level_tests: - all_args += ' --enable-bit-level-tests' - - if coverage: - - try: - - import pytest_cov - - except ImportError: - - raise ImportError( - 'Coverage reporting requires pytest-cov plugin: ' - 'http://pypi.python.org/pypi/pytest-cov') - - else: - - coveragerc = os.path.join( - os.path.dirname(__file__), 'coveragerc') - - with open(coveragerc, 'r') as fd: - coveragerc_content = fd.read() - if sys.version_info[0] >= 3: - ignore_python_version = '2' - else: - ignore_python_version = '3' - coveragerc_content = coveragerc_content.replace( - "{ignore_python_version}", ignore_python_version) - with tempfile.NamedTemporaryFile(delete=False) as tmp: - tmp.write(coveragerc_content.encode('utf-8')) - - all_args += ( - ' --cov-report html --cov hyperion' - ' --cov-config {0}'.format(tmp.name)) - - tmpdir = tempfile.mkdtemp() - all_args += ' --basetemp={0}'.format(tmpdir) - - all_args = shlex.split(all_args, - posix=not sys.platform.startswith('win')) - - import pytest - - try: - return pytest.main(args=all_args, plugins=plugins) - finally: - shutil.rmtree(tmpdir) - if coverage: - if not tmp.closed: - tmp.close() - os.remove(tmp.name) - - -class HyperionTest(Command, object): - - user_options = [ - ('package=', 'P', - "The name of a specific package to test, e.g. 'model' or " - "'densities'. If nothing is specified all default Hyperion tests " - "are run."), - ('test-path=', 't', 'Specify a test location by path. Must be ' - 'specified absolutely or relative to the current directory. ' - 'May be a single file or directory.'), - ('verbose', 'V', - 'Turn on verbose output from pytest. Same as specifying `-v` in ' - '`args`.'), - ('plugins=', 'p', - 'Plugins to enable when running pytest. Same as specifying `-p` in ' - '`args`.'), - ('pastebin=', 'b', - "Enable pytest pastebin output. Either 'all' or 'failed'."), - ('generate-reference=', 'g', "generate reference results for bit-level tests"), - ('enable-bit-level-tests', 'l', "enable bit-level tests"), - ('args=', 'a', 'Additional arguments to be passed to pytest'), - ('coverage', 'c', 'Create a coverage report. Requires the pytest-cov ' - 'plugin is installed') - ] - - def __init__(self, dist): - Command.__init__(self, dist) - self.verbose = False # __init__ sets verbose to True after calling initialize_options - - def initialize_options(self): - self.package = None - self.test_path = None - self.verbose = False - self.plugins = None - self.pastebin = None - self.generate_reference = False - self.enable_bit_level_tests = False - self.args = None - self.coverage = False - - def finalize_options(self): - pass - - def run(self): - self.reinitialize_command('build') - self.run_command('build') - build_cmd = self.get_finalized_command('build') - new_path = os.path.abspath(build_cmd.build_lib) - - if self.generate_reference: - self.generate_reference = os.path.abspath(self.generate_reference) - if not os.path.exists(self.generate_reference): - raise IOError("Directory {0} does not exist".format(self.generate_reference)) - - # Run the tests in a subprocess--this is necessary since new extension - # modules may have appeared, and this is the easiest way to set up a - # new environment - cmd = ('import hyperion, sys; sys.exit(hyperion.test({0!r}, {1!r}, ' - '{2!r}, {3!r}, {4!r}, {5!r}, {6!r}, {7!r}, {8!r}))') - cmd = cmd.format(self.package, self.test_path, self.args, - self.plugins, self.verbose, self.pastebin, - self.generate_reference, self.enable_bit_level_tests, - self.coverage) - - retcode = subprocess.call([sys.executable, '-c', cmd], - cwd=new_path, close_fds=False) - - if self.coverage and retcode == 0: - # Copy the htmlcov from build/lib.../htmlcov to a more - # obvious place - if os.path.exists('htmlcov'): - shutil.rmtree('htmlcov') - shutil.copytree(os.path.join(new_path, 'htmlcov'), 'htmlcov') - - raise SystemExit(retcode) diff --git a/scripts/hyperion b/scripts/hyperion index bd98b99f..9ad645ed 100755 --- a/scripts/hyperion +++ b/scripts/hyperion @@ -4,10 +4,7 @@ import sys import subprocess import os -if sys.version_info[0] == 2: - from ConfigParser import SafeConfigParser -else: - from configparser import SafeConfigParser +from configparser import ConfigParser if (sys.version_info[0] == 2 and sys.version_info[1] < 7) or \ (sys.version_info[0] == 3 and sys.version_info[1] <= 1): @@ -66,7 +63,7 @@ if n_cores is None: else: filename = os.path.expanduser('~/.hyperionrc') - config = SafeConfigParser() + config = ConfigParser() config.read(filename) if config.has_option('mpi', 'command'): diff --git a/setup.cfg b/setup.cfg index 5e4bbf7c..1a823804 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,3 +37,8 @@ pytest11 = [bdist_wheel] py_limited_api = cp39 + +[tool:pytest] +markers = + requires_hyperion_binaries: test that requires the hyperion_* binaries to be compiled + bitlevel: bit-level comparison tests diff --git a/setup.py b/setup.py index a307da12..07f0b70a 100755 --- a/setup.py +++ b/setup.py @@ -1,24 +1,25 @@ #!/usr/bin/env python import numpy +import sys from setuptools import setup, Extension +kwargs = {} +kwargs['py_limited_api'] = True +kwargs['include_dirs'] = [numpy.get_include()] +if sys.platform != "win32": + kwargs['extra_compile_args'] = ['-Wno-error=declaration-after-statement'] + ext_modules = [Extension("hyperion.util._integrate_core", ['hyperion/util/_integrate_core.c'], - py_limited_api=True, - include_dirs=[numpy.get_include()], - extra_compile_args=['-Wno-error=declaration-after-statement']), - Extension("hyperion.util._interpolate_core", + **kwargs), + Extension("hyperion.util._interpolate_core", ['hyperion/util/_interpolate_core.c'], - py_limited_api=True, - include_dirs=[numpy.get_include()], - extra_compile_args=['-Wno-error=declaration-after-statement']), - Extension("hyperion.importers._discretize_sph", + **kwargs), + Extension("hyperion.importers._discretize_sph", ['hyperion/importers/_discretize_sph.c'], - py_limited_api=True, - include_dirs=[numpy.get_include()], - extra_compile_args=['-Wno-error=declaration-after-statement']), - Extension("hyperion.grid._voronoi_core", + **kwargs), + Extension("hyperion.grid._voronoi_core", ['hyperion/grid/_voronoi_core.c', 'hyperion/grid/voropp_wrap.cc', 'hyperion/grid/voro++/c_loops.cc', @@ -31,10 +32,8 @@ 'hyperion/grid/voro++/v_base.cc', 'hyperion/grid/voro++/v_compute.cc', 'hyperion/grid/voro++/wall.cc'], - py_limited_api=True, - include_dirs=[numpy.get_include()], - extra_compile_args = ['-O2', '-Wno-error=declaration-after-statement'], - extra_link_args=['-lstdc++'])] + extra_link_args=['-lstdc++'], + **kwargs)] setup(scripts=['scripts/hyperion', 'scripts/hyperion2fits'], ext_modules=ext_modules) diff --git a/tox.ini b/tox.ini index 3502ae5d..b7684f2e 100644 --- a/tox.ini +++ b/tox.ini @@ -1,6 +1,6 @@ [tox] envlist = - py{36,37,38,39,310}-test{,-bitlevel}{,-macos} + py{36,37,38,39,310}-test{,-bitlevel}{,-macos}{,-nobinaries} requires = setuptools >= 30.3.0 pip >= 19.3.1 @@ -13,6 +13,8 @@ setenv = # so we need to override the default Fortran compiler. macos: HDF5_FC=gfortran-13 macos: HDF5_FLINKER=gfortran-13 + bitlevel: BITFLAGS= --enable-bit-level-tests + nobinaries: BINFLAGS= -m "not requires_hyperion_binaries" deps = bitlevel: numpy==1.21.* extras = @@ -23,9 +25,8 @@ allowlist_externals = mv commands = pip freeze - {toxinidir}/configure --prefix={envdir} - mv Makefile {toxinidir}/ - make -C {toxinidir} serial - make -C {toxinidir} install - !bitlevel: pytest --pyargs hyperion - bitlevel: pytest --pyargs hyperion --enable-bit-level-tests + !nobinaries: {toxinidir}/configure --prefix={envdir} + !nobinaries: mv Makefile {toxinidir}/ + !nobinaries: make -C {toxinidir} serial + !nobinaries: make -C {toxinidir} install + pytest --pyargs hyperion {env:BITFLAGS} {env:BINFLAGS} {posargs}