From 9c3da687664cd29fb95b93a39edadfad13dda913 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sun, 5 Sep 2021 22:39:20 -0400 Subject: [PATCH 1/3] MNT: Set minimum pydicom to 1.0.1 Pydicom<1 used the setuptools' ``use_2to3`` option to achieve Python 3 compatiblity. Setuptools has removed lib2to3 in https://github.com/pypa/setuptools/pull/2731, which causes our minimum requirements tests to fail. --- setup.cfg | 2 +- tools/ci/env.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.cfg b/setup.cfg index e4a706467f..a8bb725a6f 100644 --- a/setup.cfg +++ b/setup.cfg @@ -37,7 +37,7 @@ packages = find: [options.extras_require] dicom = - pydicom >=0.9.9 + pydicom >=1.0.0 dicomfs = %(dicom)s pillow diff --git a/tools/ci/env.sh b/tools/ci/env.sh index a03b57b0a9..a2eaa64a4b 100644 --- a/tools/ci/env.sh +++ b/tools/ci/env.sh @@ -9,7 +9,7 @@ DEFAULT_OPT_DEPENDS="scipy matplotlib pillow pydicom h5py indexed_gzip" # pydicom has skipped some important pre-releases, so enable a check against master PYDICOM_MASTER="git+https://github.com/pydicom/pydicom.git@master" # Minimum versions of optional requirements -MIN_OPT_DEPENDS="matplotlib==1.5.3 pydicom==0.9.9 pillow==2.6" +MIN_OPT_DEPENDS="matplotlib==1.5.3 pydicom==1.0.1 pillow==2.6" # Numpy and scipy upload nightly/weekly/intermittent wheels NIGHTLY_WHEELS="https://pypi.anaconda.org/scipy-wheels-nightly/simple" From 920ba700c91d3aa5949c216ea71073ddabda95de Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Sun, 5 Sep 2021 22:46:43 -0400 Subject: [PATCH 2/3] RF: Remove pydicom<1 conditions in pydicom_compat --- nibabel/pydicom_compat.py | 27 +++++++-------------------- 1 file changed, 7 insertions(+), 20 deletions(-) diff --git a/nibabel/pydicom_compat.py b/nibabel/pydicom_compat.py index 9cca2a293d..c0b2f57532 100644 --- a/nibabel/pydicom_compat.py +++ b/nibabel/pydicom_compat.py @@ -30,30 +30,17 @@ pydicom = read_file = tag_for_keyword = Sequence = None try: - import dicom as pydicom + import pydicom except ImportError: - try: - import pydicom - except ImportError: - have_dicom = False - else: # pydicom module available - from pydicom.dicomio import read_file - from pydicom.sequence import Sequence - # Values not imported by default - import pydicom.values -else: # dicom module available + have_dicom = False +else: # pydicom module available + from pydicom.dicomio import read_file + from pydicom.sequence import Sequence # Values not imported by default - import dicom.values - from dicom.sequence import Sequence - read_file = pydicom.read_file + import pydicom.values if have_dicom: - try: - # Versions >= 1.0 - tag_for_keyword = pydicom.datadict.tag_for_keyword - except AttributeError: - # Versions < 1.0 - also has more search options. - tag_for_keyword = pydicom.datadict.tag_for_name + tag_for_keyword = pydicom.datadict.tag_for_keyword @deprecate_with_version("dicom_test has been moved to nibabel.nicom.tests", From f1983bafbe4aba3afa55c0e7c25f872eba622e35 Mon Sep 17 00:00:00 2001 From: "Christopher J. Markiewicz" Date: Wed, 8 Sep 2021 09:16:55 -0400 Subject: [PATCH 3/3] RF: Eliminate use of pydicom_compat --- nibabel/dft.py | 7 ++++--- nibabel/nicom/dicomwrappers.py | 14 +++++++------- nibabel/nicom/tests/__init__.py | 6 ++++-- nibabel/nicom/tests/test_csareader.py | 3 +-- nibabel/nicom/tests/test_dicomreaders.py | 14 +++++--------- nibabel/nicom/tests/test_dicomwrappers.py | 12 +++++------- nibabel/nicom/tests/test_utils.py | 7 +++---- nibabel/nifti1.py | 4 +++- nibabel/tests/test_dft.py | 3 +-- nibabel/tests/test_nifti1.py | 5 +++-- 10 files changed, 36 insertions(+), 39 deletions(-) diff --git a/nibabel/dft.py b/nibabel/dft.py index 4caf55da75..f87002379e 100644 --- a/nibabel/dft.py +++ b/nibabel/dft.py @@ -24,8 +24,9 @@ from io import BytesIO from .nifti1 import Nifti1Header +from nibabel.optpkg import optional_package -from .pydicom_compat import pydicom, read_file +pydicom = optional_package("pydicom")[0] logger = logging.getLogger('nibabel.dft') @@ -241,7 +242,7 @@ def __getattribute__(self, name): return val def dicom(self): - return read_file(self.files[0]) + return pydicom.read_file(self.files[0]) class _db_nochange: @@ -386,7 +387,7 @@ def _update_dir(c, dir, files, studies, series, storage_instances): def _update_file(c, path, fname, studies, series, storage_instances): try: - do = read_file(f'{path}/{fname}') + do = pydicom.read_file(f'{path}/{fname}') except pydicom.filereader.InvalidDicomError: logger.debug(' not a DICOM file') return None diff --git a/nibabel/nicom/dicomwrappers.py b/nibabel/nicom/dicomwrappers.py index 2f494893f6..c05f4f2a1b 100755 --- a/nibabel/nicom/dicomwrappers.py +++ b/nibabel/nicom/dicomwrappers.py @@ -17,13 +17,15 @@ import numpy as np +from nibabel.optpkg import optional_package from . import csareader as csar from .dwiparams import B2q, nearest_pos_semi_def, q2bg from ..openers import ImageOpener from ..onetime import auto_attr as one_time -from ..pydicom_compat import tag_for_keyword, Sequence from ..deprecated import deprecate_with_version +pydicom = optional_package("pydicom")[0] + class WrapperError(Exception): pass @@ -52,10 +54,8 @@ def wrapper_from_file(file_like, *args, **kwargs): dcm_w : ``dicomwrappers.Wrapper`` or subclass DICOM wrapper corresponding to DICOM data type """ - from ..pydicom_compat import read_file - with ImageOpener(file_like) as fobj: - dcm_data = read_file(fobj, *args, **kwargs) + dcm_data = pydicom.read_file(fobj, *args, **kwargs) return wrapper_from_data(dcm_data) @@ -520,7 +520,7 @@ def image_shape(self): if hasattr(first_frame, 'get') and first_frame.get([0x18, 0x9117]): # DWI image may include derived isotropic, ADC or trace volume try: - self.frames = Sequence( + self.frames = pydicom.Sequence( frame for frame in self.frames if frame.MRDiffusionSequence[0].DiffusionDirectionality != 'ISOTROPIC' @@ -550,7 +550,7 @@ def image_shape(self): # Determine if one of the dimension indices refers to the stack id dim_seq = [dim.DimensionIndexPointer for dim in self.get('DimensionIndexSequence')] - stackid_tag = tag_for_keyword('StackID') + stackid_tag = pydicom.datadict.tag_for_keyword('StackID') # remove the stack id axis if present if stackid_tag in dim_seq: stackid_dim_idx = dim_seq.index(stackid_tag) @@ -558,7 +558,7 @@ def image_shape(self): dim_seq.pop(stackid_dim_idx) if has_derived: # derived volume is included - derived_tag = tag_for_keyword("DiffusionBValue") + derived_tag = pydicom.datadict.tag_for_keyword("DiffusionBValue") if derived_tag not in dim_seq: raise WrapperError("Missing information, cannot remove indices " "with confidence.") diff --git a/nibabel/nicom/tests/__init__.py b/nibabel/nicom/tests/__init__.py index 127ad5a6e0..75f5dbc5ac 100644 --- a/nibabel/nicom/tests/__init__.py +++ b/nibabel/nicom/tests/__init__.py @@ -1,4 +1,6 @@ -from ...pydicom_compat import have_dicom import unittest +from nibabel.optpkg import optional_package -dicom_test = unittest.skipUnless(have_dicom, "Could not import dicom or pydicom") +pydicom, have_dicom, _ = optional_package("pydicom") + +dicom_test = unittest.skipUnless(have_dicom, "Could not import pydicom") diff --git a/nibabel/nicom/tests/test_csareader.py b/nibabel/nicom/tests/test_csareader.py index 1692aad622..912e98fe18 100644 --- a/nibabel/nicom/tests/test_csareader.py +++ b/nibabel/nicom/tests/test_csareader.py @@ -7,12 +7,11 @@ import numpy as np -from ...pydicom_compat import pydicom from .. import csareader as csa from .. import dwiparams as dwp import pytest -from . import dicom_test +from . import pydicom, dicom_test from .test_dicomwrappers import IO_DATA_PATH, DATA CSA2_B0 = open(pjoin(IO_DATA_PATH, 'csa2_b0.bin'), 'rb').read() diff --git a/nibabel/nicom/tests/test_dicomreaders.py b/nibabel/nicom/tests/test_dicomreaders.py index 7c28917a81..0ce7f8de2e 100644 --- a/nibabel/nicom/tests/test_dicomreaders.py +++ b/nibabel/nicom/tests/test_dicomreaders.py @@ -6,18 +6,16 @@ import numpy as np +from nibabel.optpkg import optional_package from .. import dicomreaders as didr -from ...pydicom_compat import pydicom - -import pytest -from . import dicom_test - from .test_dicomwrappers import EXPECTED_AFFINE, EXPECTED_PARAMS, IO_DATA_PATH, DATA +import pytest from numpy.testing import assert_array_equal, assert_array_almost_equal +pydicom, _, setup_module = optional_package("pydicom") + -@dicom_test def test_read_dwi(): img = didr.mosaic_to_nii(DATA) arr = img.get_fdata() @@ -25,7 +23,6 @@ def test_read_dwi(): assert_array_almost_equal(img.affine, EXPECTED_AFFINE) -@dicom_test def test_read_dwis(): data, aff, bs, gs = didr.read_mosaic_dwi_dir(IO_DATA_PATH, 'siemens_dwi_*.dcm.gz') @@ -37,7 +34,6 @@ def test_read_dwis(): didr.read_mosaic_dwi_dir('improbable') -@dicom_test def test_passing_kwds(): # Check that we correctly pass keywords to dicom dwi_glob = 'siemens_dwi_*.dcm.gz' @@ -61,7 +57,7 @@ def test_passing_kwds(): with pytest.raises(didr.DicomReadError): func(IO_DATA_PATH, csa_glob, dicom_kwargs=dict(force=True)) -@dicom_test + def test_slices_to_series(): dicom_files = (pjoin(IO_DATA_PATH, "%d.dcm" % i) for i in range(2)) wrappers = [didr.wrapper_from_file(f) for f in dicom_files] diff --git a/nibabel/nicom/tests/test_dicomwrappers.py b/nibabel/nicom/tests/test_dicomwrappers.py index 9fa9800a4e..440408a4c1 100755 --- a/nibabel/nicom/tests/test_dicomwrappers.py +++ b/nibabel/nicom/tests/test_dicomwrappers.py @@ -9,15 +9,13 @@ import numpy as np -from nibabel.pydicom_compat import have_dicom, pydicom, read_file, tag_for_keyword - +from . import pydicom, have_dicom, dicom_test from .. import dicomwrappers as didw from .. import dicomreaders as didr from ...volumeutils import endian_codes import pytest from unittest import TestCase -from . import dicom_test from numpy.testing import assert_array_equal, assert_array_almost_equal from ...tests.nibabel_data import get_nibabel_data, needs_nibabel_data @@ -26,8 +24,8 @@ DATA_FILE = pjoin(IO_DATA_PATH, 'siemens_dwi_1000.dcm.gz') DATA_FILE_PHILIPS = pjoin(IO_DATA_PATH, 'philips_mprage.dcm.gz') if have_dicom: - DATA = read_file(gzip.open(DATA_FILE)) - DATA_PHILIPS = read_file(gzip.open(DATA_FILE_PHILIPS)) + DATA = pydicom.read_file(gzip.open(DATA_FILE)) + DATA_PHILIPS = pydicom.read_file(gzip.open(DATA_FILE_PHILIPS)) else: DATA = None DATA_PHILIPS = None @@ -434,8 +432,8 @@ def __init__(self, div, sid): dim_idx_seq = [DimIdxSeqElem()] * num_of_frames # add an entry for StackID into the DimensionIndexSequence if sid_dim is not None: - sid_tag = tag_for_keyword('StackID') - fcs_tag = tag_for_keyword('FrameContentSequence') + sid_tag = pydicom.datadict.tag_for_keyword('StackID') + fcs_tag = pydicom.datadict.tag_for_keyword('FrameContentSequence') dim_idx_seq[sid_dim] = DimIdxSeqElem(sid_tag, fcs_tag) # create the PerFrameFunctionalGroupsSequence frames = [PerFrmFuncGrpSeqElem(div, sid) diff --git a/nibabel/nicom/tests/test_utils.py b/nibabel/nicom/tests/test_utils.py index 142daa3d16..ddfe68075c 100644 --- a/nibabel/nicom/tests/test_utils.py +++ b/nibabel/nicom/tests/test_utils.py @@ -2,14 +2,13 @@ """ import re +from nibabel.optpkg import optional_package +from .test_dicomwrappers import DATA, DATA_PHILIPS from ..utils import find_private_section -from . import dicom_test -from ...pydicom_compat import pydicom -from .test_dicomwrappers import DATA, DATA_PHILIPS +pydicom, _, setup_module = optional_package("pydicom") -@dicom_test def test_find_private_section_real(): # Find section containing named private creator information # On real data first diff --git a/nibabel/nifti1.py b/nibabel/nifti1.py index 799377f282..b9df63a450 100644 --- a/nibabel/nifti1.py +++ b/nibabel/nifti1.py @@ -17,6 +17,7 @@ import numpy.linalg as npl from numpy.compat.py3k import asstr +from .optpkg import optional_package from .filebasedimages import SerializableImage from .volumeutils import Recoder, make_dt_codes, endian_codes from .spatialimages import HeaderDataError, ImageFileError @@ -25,7 +26,8 @@ from . import analyze # module import from .spm99analyze import SpmAnalyzeHeader from .casting import have_binary128 -from .pydicom_compat import have_dicom, pydicom as pdcm + +pdcm, have_dicom, _ = optional_package("pydicom") # nifti1 flat header definition for Analyze-like first 348 bytes # first number in comments indicates offset in file header in bytes diff --git a/nibabel/tests/test_dft.py b/nibabel/tests/test_dft.py index c7c80b0dd9..b00c136312 100644 --- a/nibabel/tests/test_dft.py +++ b/nibabel/tests/test_dft.py @@ -16,8 +16,7 @@ # Shield optional package imports from ..optpkg import optional_package -from nibabel.pydicom_compat import have_dicom - +have_dicom = optional_package('pydicom')[1] PImage, have_pil, _ = optional_package('PIL.Image') data_dir = pjoin(dirname(__file__), 'data') diff --git a/nibabel/tests/test_nifti1.py b/nibabel/tests/test_nifti1.py index 562c09c15b..0a3a8901cb 100644 --- a/nibabel/tests/test_nifti1.py +++ b/nibabel/tests/test_nifti1.py @@ -24,6 +24,7 @@ slice_order_codes) from nibabel.spatialimages import HeaderDataError from nibabel.tmpdirs import InTemporaryDirectory +from nibabel.optpkg import optional_package from ..freesurfer import load as mghload from ..orientations import aff2axcodes @@ -52,8 +53,8 @@ header_file = os.path.join(data_path, 'nifti1.hdr') image_file = os.path.join(data_path, 'example4d.nii.gz') -from ..pydicom_compat import pydicom, have_dicom -dicom_test = unittest.skipUnless(have_dicom, "Could not import dicom or pydicom") +pydicom, have_dicom, _ = optional_package("pydicom") +dicom_test = unittest.skipUnless(have_dicom, "Could not import pydicom") # Example transformation matrix