Skip to content

Commit 2065503

Browse files
committed
RF: move dicom / pydicom imports into own module
Move logic for conditionally importing dicom or pydicom into own module, and use this module where we are using dicom routines.
1 parent e9b6256 commit 2065503

File tree

10 files changed

+74
-68
lines changed

10 files changed

+74
-68
lines changed

nibabel/dft.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,7 @@
2626

2727
from .nifti1 import Nifti1Header
2828

29-
# Shield optional dicom import
30-
from .optpkg import optional_package
31-
dicom, have_dicom, _ = optional_package('dicom')
29+
from .pydicom_compat import pydicom, read_file
3230

3331
logger = logging.getLogger('nibabel.dft')
3432

@@ -236,7 +234,7 @@ def __getattribute__(self, name):
236234
return val
237235

238236
def dicom(self):
239-
return dicom.read_file(self.files[0])
237+
return read_file(self.files[0])
240238

241239

242240
class _db_nochange:
@@ -383,8 +381,8 @@ def _update_dir(c, dir, files, studies, series, storage_instances):
383381

384382
def _update_file(c, path, fname, studies, series, storage_instances):
385383
try:
386-
do = dicom.read_file('%s/%s' % (path, fname))
387-
except dicom.filereader.InvalidDicomError:
384+
do = read_file('%s/%s' % (path, fname))
385+
except pydicom.filereader.InvalidDicomError:
388386
logger.debug(' not a DICOM file')
389387
return None
390388
try:

nibabel/nicom/dicomwrappers.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -50,10 +50,7 @@ def wrapper_from_file(file_like, *args, **kwargs):
5050
dcm_w : ``dicomwrappers.Wrapper`` or subclass
5151
DICOM wrapper corresponding to DICOM data type
5252
"""
53-
try:
54-
from dicom import read_file
55-
except ImportError:
56-
from pydicom.dicomio import read_file
53+
from ..pydicom_compat import read_file
5754

5855
with ImageOpener(file_like) as fobj:
5956
dcm_data = read_file(fobj, *args, **kwargs)

nibabel/nicom/tests/test_csareader.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,8 @@
1414

1515
from numpy.testing.decorators import skipif
1616

17-
from .test_dicomwrappers import (have_dicom, dicom_test,
18-
IO_DATA_PATH, DATA, DATA_FILE)
19-
if have_dicom:
20-
from .test_dicomwrappers import pydicom
17+
from nibabel.pydicom_compat import dicom_test, pydicom
18+
from .test_dicomwrappers import (IO_DATA_PATH, DATA)
2119

2220
CSA2_B0 = open(pjoin(IO_DATA_PATH, 'csa2_b0.bin'), 'rb').read()
2321
CSA2_B1000 = open(pjoin(IO_DATA_PATH, 'csa2_b1000.bin'), 'rb').read()

nibabel/nicom/tests/test_dicomreaders.py

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,9 @@
66

77
from .. import dicomreaders as didr
88

9-
from .test_dicomwrappers import (dicom_test,
10-
EXPECTED_AFFINE,
9+
from nibabel.pydicom_compat import dicom_test, pydicom
10+
11+
from .test_dicomwrappers import (EXPECTED_AFFINE,
1112
EXPECTED_PARAMS,
1213
IO_DATA_PATH,
1314
DATA)
@@ -41,10 +42,6 @@ def test_passing_kwds():
4142
# Check that we correctly pass keywords to dicom
4243
dwi_glob = 'siemens_dwi_*.dcm.gz'
4344
csa_glob = 'csa*.bin'
44-
try:
45-
from dicom.filereader import InvalidDicomError
46-
except ImportError:
47-
from pydicom.filereader import InvalidDicomError
4845
for func in (didr.read_mosaic_dwi_dir, didr.read_mosaic_dir):
4946
data, aff, bs, gs = func(IO_DATA_PATH, dwi_glob)
5047
# This should not raise an error
@@ -60,7 +57,7 @@ def test_passing_kwds():
6057
dwi_glob,
6158
dicom_kwargs=dict(not_a_parameter=True))
6259
# These are invalid dicoms, so will raise an error unless force=True
63-
assert_raises(InvalidDicomError,
60+
assert_raises(pydicom.filereader.InvalidDicomError,
6461
func,
6562
IO_DATA_PATH,
6663
csa_glob)

nibabel/nicom/tests/test_dicomwrappers.py

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,19 +9,7 @@
99

1010
import numpy as np
1111

12-
have_dicom = True
13-
try:
14-
import dicom as pydicom
15-
read_file = pydicom.read_file
16-
except ImportError:
17-
try:
18-
import pydicom
19-
except ImportError:
20-
have_dicom = False
21-
else:
22-
from pydicom.dicomio import read_file
23-
dicom_test = np.testing.dec.skipif(not have_dicom,
24-
'could not import pydicom')
12+
from nibabel.pydicom_compat import have_dicom, pydicom, read_file, dicom_test
2513

2614
from .. import dicomwrappers as didw
2715
from .. import dicomreaders as didr

nibabel/nicom/tests/test_utils.py

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212

1313
from ..utils import find_private_section
1414

15-
from .test_dicomwrappers import (have_dicom, dicom_test,
16-
IO_DATA_PATH, DATA, DATA_PHILIPS)
15+
from nibabel.pydicom_compat import dicom_test, pydicom
16+
from .test_dicomwrappers import (DATA, DATA_PHILIPS)
1717

1818

1919
@dicom_test
@@ -27,11 +27,7 @@ def test_find_private_section_real():
2727
assert_equal(find_private_section(DATA_PHILIPS, 0x29, 'SIEMENS CSA HEADER'),
2828
None)
2929
# Make fake datasets
30-
try:
31-
from dicom.dataset import Dataset
32-
except ImportError:
33-
from pydicom.dataset import Dataset
34-
ds = Dataset({})
30+
ds = pydicom.dataset.Dataset({})
3531
ds.add_new((0x11, 0x10), 'LO', b'some section')
3632
assert_equal(find_private_section(ds, 0x11, 'some section'), 0x1000)
3733
ds.add_new((0x11, 0x11), 'LO', b'anther section')

nibabel/nifti1.py

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
'''
1313
from __future__ import division, print_function
1414
import warnings
15+
from io import BytesIO
1516

1617
import numpy as np
1718
import numpy.linalg as npl
@@ -24,6 +25,7 @@
2425
from . import analyze # module import
2526
from .spm99analyze import SpmAnalyzeHeader
2627
from .casting import have_binary128
28+
from .pydicom_compat import have_dicom, pydicom as pdcm
2729

2830
# nifti1 flat header definition for Analyze-like first 348 bytes
2931
# first number in comments indicates offset in file header in bytes
@@ -392,7 +394,7 @@ def __init__(self, code, content, parent_hdr=None):
392394
self._is_little_endian = parent_hdr.endianness == '<'
393395
else:
394396
self._is_little_endian = True
395-
if content.__class__ == Dataset:
397+
if content.__class__ == pdcm.dataset.Dataset:
396398
self._is_implicit_VR = False
397399
self._raw_content = self._mangle(content)
398400
self._content = content
@@ -404,13 +406,13 @@ def __init__(self, code, content, parent_hdr=None):
404406
self._content = ds
405407
else: # Otherwise, initialize a new dicom dataset
406408
self._is_implicit_VR = False
407-
self._content = Dataset()
409+
self._content = pdcm.dataset.Dataset()
408410

409411
def _unmangle_and_verify(self, content, is_implicit_VR, is_little_endian):
410412
""""Decode and verify dicom dataset"""
411413
ds = self._unmangle(content, is_implicit_VR, is_little_endian)
412414
for elem in ds:
413-
if elem.VR not in dicom_converters.keys():
415+
if elem.VR not in pdcm.values.converters.keys():
414416
raise StandardError # Ensure that all VRs are valid
415417
return ds
416418

@@ -423,45 +425,37 @@ def _guess_implicit_VR(self):
423425
valid VRs
424426
"""
425427
potential_vr = self._raw_content[4:6].decode()
426-
if potential_vr in dicom_converters.keys():
428+
if potential_vr in pdcm.values.converters.keys():
427429
implicit_VR = False
428430
else:
429431
implicit_VR = True
430432
return implicit_VR
431433

432434
def _unmangle(self, value, is_implicit_VR=False, is_little_endian=True):
433435
bio = BytesIO(value)
434-
ds = read_dataset(bio, is_implicit_VR, is_little_endian)
436+
ds = pdcm.filereader.read_dataset(bio,
437+
is_implicit_VR,
438+
is_little_endian)
435439
return ds
436440

437441
def _mangle(self, dataset):
438442
bio = BytesIO()
439-
dio = DicomFileLike(bio)
443+
dio = pdcm.filebase.DicomFileLike(bio)
440444
dio.is_implicit_VR = self._is_implicit_VR
441445
dio.is_little_endian = self._is_little_endian
442-
ds_len = write_dataset(dio, dataset)
446+
ds_len = pdcm.filewriter.write_dataset(dio, dataset)
443447
dio.seek(0)
444448
return dio.read(ds_len)
445449

446450

447-
try:
448-
from dicom.dataset import Dataset
449-
from dicom.filebase import DicomFileLike
450-
from dicom.filereader import read_dataset
451-
from dicom.filewriter import write_dataset
452-
from dicom.values import converters as dicom_converters
453-
from io import BytesIO
454-
except ImportError:
455-
"""Fall back to standard reader if pydicom unavailable."""
456-
Nifti1DicomExtension = Nifti1Extension
457451

458452
# NIfTI header extension type codes (ECODE)
459453
# see nifti1_io.h for a complete list of all known extensions and
460454
# references to their description or contacts of the respective
461455
# initiators
462456
extension_codes = Recoder((
463457
(0, "ignore", Nifti1Extension),
464-
(2, "dicom", Nifti1DicomExtension),
458+
(2, "dicom", Nifti1DicomExtension if have_dicom else Nifti1Extension),
465459
(4, "afni", Nifti1Extension),
466460
(6, "comment", Nifti1Extension),
467461
(8, "xcede", Nifti1Extension),

nibabel/pydicom_compat.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
""" Adapter module for working with pydicom < 1.0 and >= 1.0
2+
3+
In what follows, "dicom is available" means we can import either a) ``dicom``
4+
(pydicom < 1.0) or or b) ``pydicom`` (pydicom >= 1.0).
5+
6+
Regardless of whether dicom is available this module should be importable
7+
without error, and always defines:
8+
9+
* have_dicom : True if we can import pydicom or dicom;
10+
* pydicom : pydicom module or dicom module or None of not importable;
11+
* read_file : ``read_file`` function if pydicom or dicom module is importable
12+
else None;
13+
* dicom_test : test decorator that skips test if dicom not available.
14+
"""
15+
16+
import numpy as np
17+
18+
have_dicom = True
19+
read_file = None
20+
pydicom = None
21+
22+
try:
23+
import dicom as pydicom
24+
except ImportError:
25+
try:
26+
import pydicom
27+
except ImportError:
28+
have_dicom = False
29+
else: # pydicom module available
30+
from pydicom.dicomio import read_file
31+
# Values not imported by default
32+
import pydicom.values
33+
else: # dicom module available
34+
read_file = pydicom.read_file
35+
36+
37+
# test decorator that skips test if dicom not available.
38+
dicom_test = np.testing.dec.skipif(not have_dicom,
39+
'could not import dicom or pydicom')

nibabel/tests/test_dft.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,10 @@
1717

1818
# Shield optional package imports
1919
from ..optpkg import optional_package
20+
2021
# setup_module will raise SkipTest if no dicom to import
21-
dicom, have_dicom, _ = optional_package('dicom')
22+
from nibabel.pydicom_compat import have_dicom
23+
2224
PImage, have_pil, _ = optional_package('PIL.Image')
2325
pil_test = np.testing.dec.skipif(not have_pil, 'could not import PIL.Image')
2426

nibabel/tests/test_nifti1.py

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from __future__ import division, print_function, absolute_import
1111
import os
1212
import warnings
13+
import struct
1314

1415
import numpy as np
1516

@@ -43,12 +44,8 @@
4344
header_file = os.path.join(data_path, 'nifti1.hdr')
4445
image_file = os.path.join(data_path, 'example4d.nii.gz')
4546

46-
from nibabel.optpkg import optional_package
47-
dicom, have_dicom, _ = optional_package('dicom')
48-
import struct
47+
from nibabel.pydicom_compat import pydicom, dicom_test
4948

50-
dicom_test = np.testing.dec.skipif(not have_dicom,
51-
'could not import pydicom')
5249

5350
# Example transformation matrix
5451
R = [[0, -1, 0], [1, 0, 0], [0, 0, 1]] # rotation matrix
@@ -1133,14 +1130,14 @@ def test_nifti_dicom_extension():
11331130

11341131
# create an empty dataset if no content provided (to write a new header)
11351132
dcmext = Nifti1DicomExtension(2, b'')
1136-
assert_equal(dcmext.get_content().__class__, dicom.dataset.Dataset)
1133+
assert_equal(dcmext.get_content().__class__, pydicom.dataset.Dataset)
11371134
assert_equal(len(dcmext.get_content().values()), 0)
11381135

11391136
# use a dataset if provided
1140-
ds = dicom.dataset.Dataset()
1137+
ds = pydicom.dataset.Dataset()
11411138
ds.add_new((0x10, 0x20), 'LO', 'NiPy')
11421139
dcmext = Nifti1DicomExtension(2, ds)
1143-
assert_equal(dcmext.get_content().__class__, dicom.dataset.Dataset)
1140+
assert_equal(dcmext.get_content().__class__, pydicom.dataset.Dataset)
11441141
assert_equal(len(dcmext.get_content().values()), 1)
11451142
assert_equal(dcmext.get_content().PatientID, 'NiPy')
11461143

0 commit comments

Comments
 (0)