Skip to content

Commit fc9a1c1

Browse files
authored
Merge pull request #1190 from effigies/mnt/setuptools-distutils-purge
MNT: Finish removing distutils and setuptools dependencies
2 parents 3fabc03 + ad439f5 commit fc9a1c1

File tree

12 files changed

+90
-50
lines changed

12 files changed

+90
-50
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,6 @@ repos:
3737
- pydicom
3838
- numpy
3939
- pyzstd
40+
- importlib_resources
4041
args: ["nibabel"]
4142
pass_filenames: false

nibabel/__init__.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -171,11 +171,16 @@ def bench(label=None, verbose=1, extra_argv=None):
171171
code : ExitCode
172172
Returns the result of running the tests as a ``pytest.ExitCode`` enum
173173
"""
174-
from pkg_resources import resource_filename
174+
try:
175+
from importlib.resources import as_file, files
176+
except ImportError:
177+
from importlib_resources import as_file, files
175178

176-
config = resource_filename('nibabel', 'benchmarks/pytest.benchmark.ini')
177179
args = []
178180
if extra_argv is not None:
179181
args.extend(extra_argv)
180-
args.extend(['-c', config])
181-
return test(label, verbose, extra_argv=args)
182+
183+
config_path = files('nibabel') / 'benchmarks/pytest.benchmark.ini'
184+
with as_file(config_path) as config:
185+
args.extend(['-c', str(config)])
186+
return test(label, verbose, extra_argv=args)

nibabel/cmdline/tests/test_conform.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
import nibabel as nib
1616
from nibabel.cmdline.conform import main
1717
from nibabel.optpkg import optional_package
18-
from nibabel.testing import test_data
18+
from nibabel.testing import get_test_data
1919

2020
_, have_scipy, _ = optional_package('scipy.ndimage')
2121
needs_scipy = unittest.skipUnless(have_scipy, 'These tests need scipy')
2222

2323

2424
@needs_scipy
2525
def test_default(tmpdir):
26-
infile = test_data(fname='anatomical.nii')
26+
infile = get_test_data(fname='anatomical.nii')
2727
outfile = tmpdir / 'output.nii.gz'
2828
main([str(infile), str(outfile)])
2929
assert outfile.isfile()
@@ -41,7 +41,7 @@ def test_default(tmpdir):
4141

4242
@needs_scipy
4343
def test_nondefault(tmpdir):
44-
infile = test_data(fname='anatomical.nii')
44+
infile = get_test_data(fname='anatomical.nii')
4545
outfile = tmpdir / 'output.nii.gz'
4646
out_shape = (100, 100, 150)
4747
voxel_size = (1, 2, 4)

nibabel/cmdline/tests/test_convert.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,11 @@
1313

1414
import nibabel as nib
1515
from nibabel.cmdline import convert
16-
from nibabel.testing import test_data
16+
from nibabel.testing import get_test_data
1717

1818

1919
def test_convert_noop(tmp_path):
20-
infile = test_data(fname='anatomical.nii')
20+
infile = get_test_data(fname='anatomical.nii')
2121
outfile = tmp_path / 'output.nii.gz'
2222

2323
orig = nib.load(infile)
@@ -31,7 +31,7 @@ def test_convert_noop(tmp_path):
3131
assert converted.shape == orig.shape
3232
assert converted.get_data_dtype() == orig.get_data_dtype()
3333

34-
infile = test_data(fname='resampled_anat_moved.nii')
34+
infile = get_test_data(fname='resampled_anat_moved.nii')
3535

3636
with pytest.raises(FileExistsError):
3737
convert.main([str(infile), str(outfile)])
@@ -50,7 +50,7 @@ def test_convert_noop(tmp_path):
5050

5151
@pytest.mark.parametrize('data_dtype', ('u1', 'i2', 'float32', 'float', 'int64'))
5252
def test_convert_dtype(tmp_path, data_dtype):
53-
infile = test_data(fname='anatomical.nii')
53+
infile = get_test_data(fname='anatomical.nii')
5454
outfile = tmp_path / 'output.nii.gz'
5555

5656
orig = nib.load(infile)
@@ -78,7 +78,7 @@ def test_convert_dtype(tmp_path, data_dtype):
7878
],
7979
)
8080
def test_convert_by_extension(tmp_path, ext, img_class):
81-
infile = test_data(fname='anatomical.nii')
81+
infile = get_test_data(fname='anatomical.nii')
8282
outfile = tmp_path / f'output.{ext}'
8383

8484
orig = nib.load(infile)
@@ -102,7 +102,7 @@ def test_convert_by_extension(tmp_path, ext, img_class):
102102
],
103103
)
104104
def test_convert_imgtype(tmp_path, ext, img_class):
105-
infile = test_data(fname='anatomical.nii')
105+
infile = get_test_data(fname='anatomical.nii')
106106
outfile = tmp_path / f'output.{ext}'
107107

108108
orig = nib.load(infile)
@@ -118,7 +118,7 @@ def test_convert_imgtype(tmp_path, ext, img_class):
118118

119119

120120
def test_convert_nifti_int_fail(tmp_path):
121-
infile = test_data(fname='anatomical.nii')
121+
infile = get_test_data(fname='anatomical.nii')
122122
outfile = tmp_path / f'output.nii'
123123

124124
orig = nib.load(infile)

nibabel/gifti/gifti.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -701,8 +701,8 @@ def agg_data(self, intent_code=None):
701701
Consider a surface GIFTI file:
702702
703703
>>> import nibabel as nib
704-
>>> from nibabel.testing import test_data
705-
>>> surf_img = nib.load(test_data('gifti', 'ascii.gii'))
704+
>>> from nibabel.testing import get_test_data
705+
>>> surf_img = nib.load(get_test_data('gifti', 'ascii.gii'))
706706
707707
The coordinate data, which is indicated by the ``NIFTI_INTENT_POINTSET``
708708
intent code, may be retrieved using any of the following equivalent
@@ -754,7 +754,7 @@ def agg_data(self, intent_code=None):
754754
The following image is a GIFTI file with ten (10) data arrays of the same
755755
size, and with intent code 2001 (``NIFTI_INTENT_TIME_SERIES``):
756756
757-
>>> func_img = nib.load(test_data('gifti', 'task.func.gii'))
757+
>>> func_img = nib.load(get_test_data('gifti', 'task.func.gii'))
758758
759759
When aggregating time series data, these arrays are concatenated into
760760
a single, vertex-by-timestep array:

nibabel/gifti/tests/test_gifti.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from ... import load
1515
from ...fileholders import FileHolder
1616
from ...nifti1 import data_type_codes
17-
from ...testing import test_data
17+
from ...testing import get_test_data
1818
from .. import (
1919
GiftiCoordSystem,
2020
GiftiDataArray,
@@ -35,9 +35,9 @@
3535

3636

3737
def test_agg_data():
38-
surf_gii_img = load(test_data('gifti', 'ascii.gii'))
39-
func_gii_img = load(test_data('gifti', 'task.func.gii'))
40-
shape_gii_img = load(test_data('gifti', 'rh.shape.curv.gii'))
38+
surf_gii_img = load(get_test_data('gifti', 'ascii.gii'))
39+
func_gii_img = load(get_test_data('gifti', 'task.func.gii'))
40+
shape_gii_img = load(get_test_data('gifti', 'rh.shape.curv.gii'))
4141
# add timeseries data with intent code ``none``
4242

4343
point_data = surf_gii_img.get_arrays_from_intent('pointset')[0].data
@@ -478,7 +478,7 @@ def test_darray_dtype_coercion_failures():
478478

479479

480480
def test_gifti_file_close(recwarn):
481-
gii = load(test_data('gifti', 'ascii.gii'))
481+
gii = load(get_test_data('gifti', 'ascii.gii'))
482482
with InTemporaryDirectory():
483483
gii.to_filename('test.gii')
484484
assert not any(isinstance(r.message, ResourceWarning) for r in recwarn)

nibabel/pkg_info.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def pkg_commit_hash(pkg_path: str | None = None) -> tuple[str, str]:
101101
return 'archive substitution', COMMIT_HASH
102102
ver = Version(__version__)
103103
if ver.local is not None and ver.local.startswith('g'):
104-
return ver.local[1:8], 'installation'
104+
return 'installation', ver.local[1:8]
105105
# maybe we are in a repository
106106
proc = run(
107107
('git', 'rev-parse', '--short', 'HEAD'),

nibabel/testing/__init__.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,12 @@
77
#
88
### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ### ##
99
"""Utilities for testing"""
10+
from __future__ import annotations
1011

1112
import os
1213
import re
1314
import sys
15+
import typing as ty
1416
import unittest
1517
import warnings
1618
from contextlib import nullcontext
@@ -19,28 +21,38 @@
1921
import numpy as np
2022
import pytest
2123
from numpy.testing import assert_array_equal
22-
from pkg_resources import resource_filename
2324

2425
from .helpers import assert_data_similar, bytesio_filemap, bytesio_round_trip
2526
from .np_features import memmap_after_ufunc
2627

28+
try:
29+
from importlib.abc import Traversable
30+
from importlib.resources import as_file, files
31+
except ImportError: # PY38
32+
from importlib_resources import as_file, files
33+
from importlib_resources.abc import Traversable
2734

28-
def test_data(subdir=None, fname=None):
35+
36+
def get_test_data(
37+
subdir: ty.Literal['gifti', 'nicom', 'externals'] | None = None,
38+
fname: str | None = None,
39+
) -> Traversable:
40+
parts: tuple[str, ...]
2941
if subdir is None:
30-
resource = os.path.join('tests', 'data')
42+
parts = ('tests', 'data')
3143
elif subdir in ('gifti', 'nicom', 'externals'):
32-
resource = os.path.join(subdir, 'tests', 'data')
44+
parts = (subdir, 'tests', 'data')
3345
else:
3446
raise ValueError(f'Unknown test data directory: {subdir}')
3547

3648
if fname is not None:
37-
resource = os.path.join(resource, fname)
49+
parts += (fname,)
3850

39-
return resource_filename('nibabel', resource)
51+
return files('nibabel').joinpath(*parts)
4052

4153

4254
# set path to example data
43-
data_path = test_data()
55+
data_path = get_test_data()
4456

4557

4658
def assert_dt_equal(a, b):

nibabel/tests/test_casting.py

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -233,10 +233,15 @@ def test_best_float():
233233

234234

235235
def test_longdouble_precision_improved():
236-
# Just check that this can only be True on windows, msvc
237-
from numpy.distutils.ccompiler import get_default_compiler
236+
# Just check that this can only be True on Windows
238237

239-
if not (os.name == 'nt' and get_default_compiler() == 'msvc'):
238+
# This previously used distutils.ccompiler.get_default_compiler to check for msvc
239+
# In https://github.com/python/cpython/blob/3467991/Lib/distutils/ccompiler.py#L919-L956
240+
# we see that this was implied by os.name == 'nt', so we can remove this deprecated
241+
# call.
242+
# However, there may be detectable conditions in Windows where we would expect this
243+
# to be False as well.
244+
if os.name != 'nt':
240245
assert not longdouble_precision_improved()
241246

242247

nibabel/tests/test_init.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1+
import pathlib
12
from unittest import mock
23

34
import pytest
4-
from pkg_resources import resource_filename
5+
6+
try:
7+
from importlib.resources import as_file, files
8+
except ImportError:
9+
from importlib_resources import as_file, files
510

611
import nibabel as nib
712

@@ -38,12 +43,11 @@ def test_nibabel_test_errors():
3843

3944

4045
def test_nibabel_bench():
41-
expected_args = ['-c', '--pyargs', 'nibabel']
46+
config_path = files('nibabel') / 'benchmarks/pytest.benchmark.ini'
47+
if not isinstance(config_path, pathlib.Path):
48+
raise unittest.SkipTest('Package is not unpacked; could get temp path')
4249

43-
try:
44-
expected_args.insert(1, resource_filename('nibabel', 'benchmarks/pytest.benchmark.ini'))
45-
except:
46-
raise unittest.SkipTest('Not installed')
50+
expected_args = ['-c', str(config_path), '--pyargs', 'nibabel']
4751

4852
with mock.patch('pytest.main') as pytest_main:
4953
nib.bench(verbose=0)

nibabel/tests/test_testing.py

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
data_path,
1616
error_warnings,
1717
get_fresh_mod,
18+
get_test_data,
1819
suppress_warnings,
19-
test_data,
2020
)
2121

2222

@@ -171,25 +171,27 @@ def test_assert_re_in(regex, entries):
171171

172172

173173
def test_test_data():
174-
assert test_data() == data_path
175-
assert test_data() == os.path.abspath(
174+
assert str(get_test_data()) == str(data_path) # Always get the same result
175+
# Works the same as using __file__ and os.path utilities
176+
assert str(get_test_data()) == os.path.abspath(
176177
os.path.join(os.path.dirname(__file__), '..', 'tests', 'data')
177178
)
179+
# Check action of subdir and that existence checks work
178180
for subdir in ('nicom', 'gifti', 'externals'):
179-
assert test_data(subdir) == os.path.join(data_path[:-10], subdir, 'tests', 'data')
180-
assert os.path.exists(test_data(subdir))
181-
assert not os.path.exists(test_data(subdir, 'doesnotexist'))
181+
assert get_test_data(subdir) == data_path.parent.parent / subdir / 'tests' / 'data'
182+
assert os.path.exists(get_test_data(subdir))
183+
assert not os.path.exists(get_test_data(subdir, 'doesnotexist'))
182184

183185
for subdir in ('freesurfer', 'doesnotexist'):
184186
with pytest.raises(ValueError):
185-
test_data(subdir)
187+
get_test_data(subdir)
186188

187-
assert not os.path.exists(test_data(None, 'doesnotexist'))
189+
assert not os.path.exists(get_test_data(None, 'doesnotexist'))
188190

189191
for subdir, fname in [
190192
('gifti', 'ascii.gii'),
191193
('nicom', '0.dcm'),
192194
('externals', 'example_1.nc'),
193195
(None, 'empty.tck'),
194196
]:
195-
assert os.path.exists(test_data(subdir, fname))
197+
assert os.path.exists(get_test_data(subdir, fname))

pyproject.toml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,11 @@ maintainers = [{ name = "Christopher Markiewicz" }]
1010
readme = "README.rst"
1111
license = { text = "MIT License" }
1212
requires-python = ">=3.8"
13-
dependencies = ["numpy >=1.19", "packaging >=17", "setuptools"]
13+
dependencies = [
14+
"numpy >=1.19",
15+
"packaging >=17",
16+
"importlib_resources; python_version < '3.9'",
17+
]
1418
classifiers = [
1519
"Development Status :: 5 - Production/Stable",
1620
"Environment :: Console",
@@ -68,7 +72,14 @@ test = [
6872
"pytest-httpserver",
6973
"pytest-xdist",
7074
]
71-
typing = ["mypy", "pytest", "types-setuptools", "types-Pillow", "pydicom"]
75+
typing = [
76+
"mypy",
77+
"pytest",
78+
"types-setuptools",
79+
"types-Pillow",
80+
"pydicom",
81+
"importlib_resources",
82+
]
7283
zstd = ["pyzstd >= 0.14.3"]
7384

7485
[tool.hatch.build.targets.sdist]

0 commit comments

Comments
 (0)