From 2bb57b3e8a69a7e4587ab4f2d67842a13188bd8b Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 18 Jul 2023 10:06:28 -0400 Subject: [PATCH 1/8] MAINT: Deprecations --- nibabel/__init__.py | 2 +- nibabel/casting.py | 7 +++++++ nibabel/nicom/utils.py | 2 +- nibabel/nifti1.py | 3 +-- nibabel/streamlines/trk.py | 2 +- nibabel/tests/test_openers.py | 4 ++-- 6 files changed, 13 insertions(+), 7 deletions(-) diff --git a/nibabel/__init__.py b/nibabel/__init__.py index c08890ac37..09be1d2792 100644 --- a/nibabel/__init__.py +++ b/nibabel/__init__.py @@ -39,7 +39,7 @@ # module imports from . import analyze as ana -from . import ecat, imagestats, mriutils +from . import ecat, imagestats, mriutils, orientations from . import nifti1 as ni1 from . import spm2analyze as spm2 from . import spm99analyze as spm99 diff --git a/nibabel/casting.py b/nibabel/casting.py index 6232c615b5..35d833940f 100644 --- a/nibabel/casting.py +++ b/nibabel/casting.py @@ -796,3 +796,10 @@ def ulp(val=np.float64(1.0)): fl2 = info['minexp'] # 'nmant' value does not include implicit first bit return 2 ** (fl2 - info['nmant']) + + +# Ported from np.compat +def asstr(s): + if isinstance(s, bytes): + return s.decode('latin1') + return str(s) diff --git a/nibabel/nicom/utils.py b/nibabel/nicom/utils.py index f62bc72c5a..ad5e794151 100644 --- a/nibabel/nicom/utils.py +++ b/nibabel/nicom/utils.py @@ -1,7 +1,7 @@ """Utilities for working with DICOM datasets """ -from numpy.compat.py3k import asstr +from nibabel.casting import asstr def find_private_section(dcm_data, group_no, creator): diff --git a/nibabel/nifti1.py b/nibabel/nifti1.py index 07fb177736..1908b9321a 100644 --- a/nibabel/nifti1.py +++ b/nibabel/nifti1.py @@ -17,12 +17,11 @@ import numpy as np import numpy.linalg as npl -from numpy.compat.py3k import asstr from . import analyze # module import from .arrayproxy import get_obj_dtype from .batteryrunners import Report -from .casting import have_binary128 +from .casting import have_binary128, asstr from .deprecated import alert_future_error from .filebasedimages import ImageFileError, SerializableImage from .optpkg import optional_package diff --git a/nibabel/streamlines/trk.py b/nibabel/streamlines/trk.py index 4f570a2803..d40cb0ed43 100644 --- a/nibabel/streamlines/trk.py +++ b/nibabel/streamlines/trk.py @@ -7,9 +7,9 @@ import warnings import numpy as np -from numpy.compat.py3k import asstr import nibabel as nib +from nibabel.casting import asstr from nibabel.openers import Opener from nibabel.orientations import aff2axcodes, axcodes2ornt from nibabel.volumeutils import endian_codes, native_code, swapped_code diff --git a/nibabel/tests/test_openers.py b/nibabel/tests/test_openers.py index 893c5f4f88..a048660d24 100644 --- a/nibabel/tests/test_openers.py +++ b/nibabel/tests/test_openers.py @@ -17,9 +17,9 @@ from unittest import mock import pytest -from numpy.compat.py3k import asbytes, asstr from packaging.version import Version +from ..casting import asstr from ..deprecator import ExpiredDeprecationError from ..openers import HAVE_INDEXED_GZIP, BZ2File, DeterministicGzipFile, ImageOpener, Opener from ..optpkg import optional_package @@ -342,7 +342,7 @@ def test_iter(): for input, does_t in files_to_test: with Opener(input, 'wb') as fobj: for line in lines: - fobj.write(asbytes(line + os.linesep)) + fobj.write(bytes(line + os.linesep, 'ascii')) with Opener(input, 'rb') as fobj: for back_line, line in zip(fobj, lines): assert asstr(back_line).rstrip() == line From 28a96399e9d04690dee9af1c8fc5c2cd668b190a Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 18 Jul 2023 13:09:42 -0400 Subject: [PATCH 2/8] FIX: Decode --- nibabel/casting.py | 7 ------- nibabel/nicom/utils.py | 16 ++++++++-------- nibabel/nifti1.py | 7 ++++--- nibabel/streamlines/trk.py | 20 +++++++++++++++----- nibabel/tests/test_openers.py | 3 +-- 5 files changed, 28 insertions(+), 25 deletions(-) diff --git a/nibabel/casting.py b/nibabel/casting.py index 35d833940f..6232c615b5 100644 --- a/nibabel/casting.py +++ b/nibabel/casting.py @@ -796,10 +796,3 @@ def ulp(val=np.float64(1.0)): fl2 = info['minexp'] # 'nmant' value does not include implicit first bit return 2 ** (fl2 - info['nmant']) - - -# Ported from np.compat -def asstr(s): - if isinstance(s, bytes): - return s.decode('latin1') - return str(s) diff --git a/nibabel/nicom/utils.py b/nibabel/nicom/utils.py index ad5e794151..0c1182f306 100644 --- a/nibabel/nicom/utils.py +++ b/nibabel/nicom/utils.py @@ -1,8 +1,6 @@ """Utilities for working with DICOM datasets """ -from nibabel.casting import asstr - def find_private_section(dcm_data, group_no, creator): """Return start element in group `group_no` given creator name `creator` @@ -19,10 +17,10 @@ def find_private_section(dcm_data, group_no, creator): ``tag``, ``VR``, ``value`` group_no : int Group number in which to search - creator : str or bytes or regex - Name of section - e.g. 'SIEMENS CSA HEADER' - or regex to search for + creator : bytes or regex + Name of section - e.g. b'SIEMENS CSA HEADER' - or regex to search for section name. Regex used via ``creator.search(element_value)`` where - ``element_value`` is the value of the data element. + ``element_value`` is the decoded value of the data element. Returns ------- @@ -31,8 +29,9 @@ def find_private_section(dcm_data, group_no, creator): """ if hasattr(creator, 'search'): match_func = creator.search - else: # assume string / bytes - match_func = asstr(creator).__eq__ + else: # assume bytes + creator = creator.decode('latin-1') + match_func = creator.__eq__ # Group elements assumed ordered by tag (groupno, elno) for element in dcm_data.group_dataset(group_no): elno = element.tag.elem @@ -40,6 +39,7 @@ def find_private_section(dcm_data, group_no, creator): break if element.VR not in ('LO', 'OB'): continue - if match_func(asstr(element.value)): + val = element.value.decode('latin-1') + if match_func(val): return elno * 0x100 return None diff --git a/nibabel/nifti1.py b/nibabel/nifti1.py index 1908b9321a..ae43a4f1c6 100644 --- a/nibabel/nifti1.py +++ b/nibabel/nifti1.py @@ -21,7 +21,7 @@ from . import analyze # module import from .arrayproxy import get_obj_dtype from .batteryrunners import Report -from .casting import have_binary128, asstr +from .casting import have_binary128 from .deprecated import alert_future_error from .filebasedimages import ImageFileError, SerializableImage from .optpkg import optional_package @@ -1404,7 +1404,7 @@ def get_intent(self, code_repr='label'): raise TypeError('repr can be "label" or "code"') n_params = len(recoder.parameters[code]) if known_intent else 0 params = (float(hdr['intent_p%d' % (i + 1)]) for i in range(n_params)) - name = asstr(hdr['intent_name'].item()) + name = hdr['intent_name'].item().decode('latin-1') return label, tuple(params), name def set_intent(self, code, params=(), name='', allow_unknown=False): @@ -1740,7 +1740,8 @@ def _chk_magic(hdr, fix=False): magic = hdr['magic'].item() if magic in (hdr.pair_magic, hdr.single_magic): return hdr, rep - rep.problem_msg = f'magic string "{asstr(magic)}" is not valid' + magic = magic.decode('latin-1') + rep.problem_msg = f'magic string "{magic}" is not valid' rep.problem_level = 45 if fix: rep.fix_msg = 'leaving as is, but future errors are likely' diff --git a/nibabel/streamlines/trk.py b/nibabel/streamlines/trk.py index d40cb0ed43..2a4cc61453 100644 --- a/nibabel/streamlines/trk.py +++ b/nibabel/streamlines/trk.py @@ -9,7 +9,6 @@ import numpy as np import nibabel as nib -from nibabel.casting import asstr from nibabel.openers import Opener from nibabel.orientations import aff2axcodes, axcodes2ornt from nibabel.volumeutils import endian_codes, native_code, swapped_code @@ -180,7 +179,7 @@ def decode_value_from_name(encoded_name): value : int Value decoded from the name. """ - encoded_name = asstr(encoded_name) + encoded_name = encoded_name.decode('latin1') if len(encoded_name) == 0: return encoded_name, 0 @@ -740,14 +739,25 @@ def __str__(self): vars[attr] = vars[hdr_field] nb_scalars = self.header[Field.NB_SCALARS_PER_POINT] - scalar_names = [asstr(s) for s in vars['scalar_name'][:nb_scalars] if len(s) > 0] + scalar_names = [ + s.decode('latin-1') + for s in vars['scalar_name'][:nb_scalars] + if len(s) > 0 + ] vars['scalar_names'] = '\n '.join(scalar_names) nb_properties = self.header[Field.NB_PROPERTIES_PER_STREAMLINE] - property_names = [asstr(s) for s in vars['property_name'][:nb_properties] if len(s) > 0] + property_names = [ + s.decode('latin-1') + for s in vars['property_name'][:nb_properties] + if len(s) > 0 + ] vars['property_names'] = '\n '.join(property_names) # Make all byte strings into strings # Fixes recursion error on Python 3.3 - vars = {k: asstr(v) if hasattr(v, 'decode') else v for k, v in vars.items()} + vars = { + k: v.decode('latin-1') if hasattr(v, 'decode') else v + for k, v in vars.items() + } return """\ MAGIC NUMBER: {MAGIC_NUMBER} v.{version} diff --git a/nibabel/tests/test_openers.py b/nibabel/tests/test_openers.py index a048660d24..f6efdeef22 100644 --- a/nibabel/tests/test_openers.py +++ b/nibabel/tests/test_openers.py @@ -19,7 +19,6 @@ import pytest from packaging.version import Version -from ..casting import asstr from ..deprecator import ExpiredDeprecationError from ..openers import HAVE_INDEXED_GZIP, BZ2File, DeterministicGzipFile, ImageOpener, Opener from ..optpkg import optional_package @@ -345,7 +344,7 @@ def test_iter(): fobj.write(bytes(line + os.linesep, 'ascii')) with Opener(input, 'rb') as fobj: for back_line, line in zip(fobj, lines): - assert asstr(back_line).rstrip() == line + assert back_line.decode('latin-1').rstrip() == line if not does_t: continue with Opener(input, 'rt') as fobj: From 410b8101addbef1a98f21c3259d835f0aa7669f9 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 18 Jul 2023 13:35:31 -0400 Subject: [PATCH 3/8] FIX: str --- nibabel/nicom/utils.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/nibabel/nicom/utils.py b/nibabel/nicom/utils.py index 0c1182f306..21b6507655 100644 --- a/nibabel/nicom/utils.py +++ b/nibabel/nicom/utils.py @@ -17,7 +17,7 @@ def find_private_section(dcm_data, group_no, creator): ``tag``, ``VR``, ``value`` group_no : int Group number in which to search - creator : bytes or regex + creator : str or regex Name of section - e.g. b'SIEMENS CSA HEADER' - or regex to search for section name. Regex used via ``creator.search(element_value)`` where ``element_value`` is the decoded value of the data element. @@ -29,8 +29,7 @@ def find_private_section(dcm_data, group_no, creator): """ if hasattr(creator, 'search'): match_func = creator.search - else: # assume bytes - creator = creator.decode('latin-1') + else: # assume str match_func = creator.__eq__ # Group elements assumed ordered by tag (groupno, elno) for element in dcm_data.group_dataset(group_no): @@ -39,7 +38,7 @@ def find_private_section(dcm_data, group_no, creator): break if element.VR not in ('LO', 'OB'): continue - val = element.value.decode('latin-1') + val = element.value if match_func(val): return elno * 0x100 return None From c8c3cf92a06fb1db11cf9dccd47a33aec7606546 Mon Sep 17 00:00:00 2001 From: Eric Larson Date: Tue, 18 Jul 2023 13:49:33 -0400 Subject: [PATCH 4/8] FIX: Revert --- nibabel/nicom/utils.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/nibabel/nicom/utils.py b/nibabel/nicom/utils.py index 21b6507655..617ff2a28a 100644 --- a/nibabel/nicom/utils.py +++ b/nibabel/nicom/utils.py @@ -17,10 +17,10 @@ def find_private_section(dcm_data, group_no, creator): ``tag``, ``VR``, ``value`` group_no : int Group number in which to search - creator : str or regex - Name of section - e.g. b'SIEMENS CSA HEADER' - or regex to search for + creator : str or bytes or regex + Name of section - e.g. 'SIEMENS CSA HEADER' - or regex to search for section name. Regex used via ``creator.search(element_value)`` where - ``element_value`` is the decoded value of the data element. + ``element_value`` is the value of the data element. Returns ------- @@ -29,7 +29,9 @@ def find_private_section(dcm_data, group_no, creator): """ if hasattr(creator, 'search'): match_func = creator.search - else: # assume str + else: + if isinstance(creator, bytes): + creator = creator.decode('latin-1') match_func = creator.__eq__ # Group elements assumed ordered by tag (groupno, elno) for element in dcm_data.group_dataset(group_no): @@ -39,6 +41,8 @@ def find_private_section(dcm_data, group_no, creator): if element.VR not in ('LO', 'OB'): continue val = element.value + if isinstance(val, bytes): + val = val.decode('latin-1') if match_func(val): return elno * 0x100 return None From 8102aa7146ee647fd52544a54525765c135276f7 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Fri, 4 Aug 2023 10:21:58 -0400 Subject: [PATCH 5/8] RF: Re-consolidate nifti error message --- nibabel/nifti1.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/nibabel/nifti1.py b/nibabel/nifti1.py index ae43a4f1c6..c1b0124ebb 100644 --- a/nibabel/nifti1.py +++ b/nibabel/nifti1.py @@ -1740,8 +1740,7 @@ def _chk_magic(hdr, fix=False): magic = hdr['magic'].item() if magic in (hdr.pair_magic, hdr.single_magic): return hdr, rep - magic = magic.decode('latin-1') - rep.problem_msg = f'magic string "{magic}" is not valid' + rep.problem_msg = f'magic string {magic.decode("latin1")!r} is not valid' rep.problem_level = 45 if fix: rep.fix_msg = 'leaving as is, but future errors are likely' From c7fdde50c38029c4cb9366c64c10c3f608d87b30 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Fri, 4 Aug 2023 10:22:29 -0400 Subject: [PATCH 6/8] STY: blue --- nibabel/streamlines/trk.py | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/nibabel/streamlines/trk.py b/nibabel/streamlines/trk.py index 2a4cc61453..04ac56a51d 100644 --- a/nibabel/streamlines/trk.py +++ b/nibabel/streamlines/trk.py @@ -740,24 +740,17 @@ def __str__(self): nb_scalars = self.header[Field.NB_SCALARS_PER_POINT] scalar_names = [ - s.decode('latin-1') - for s in vars['scalar_name'][:nb_scalars] - if len(s) > 0 + s.decode('latin-1') for s in vars['scalar_name'][:nb_scalars] if len(s) > 0 ] vars['scalar_names'] = '\n '.join(scalar_names) nb_properties = self.header[Field.NB_PROPERTIES_PER_STREAMLINE] property_names = [ - s.decode('latin-1') - for s in vars['property_name'][:nb_properties] - if len(s) > 0 + s.decode('latin-1') for s in vars['property_name'][:nb_properties] if len(s) > 0 ] vars['property_names'] = '\n '.join(property_names) # Make all byte strings into strings # Fixes recursion error on Python 3.3 - vars = { - k: v.decode('latin-1') if hasattr(v, 'decode') else v - for k, v in vars.items() - } + vars = {k: v.decode('latin-1') if hasattr(v, 'decode') else v for k, v in vars.items()} return """\ MAGIC NUMBER: {MAGIC_NUMBER} v.{version} From cd2ba2f08c3650928bc9f482af5c1c523fb87062 Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Fri, 4 Aug 2023 10:23:32 -0400 Subject: [PATCH 7/8] TEST: Use standard encode/decode --- nibabel/tests/test_openers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/nibabel/tests/test_openers.py b/nibabel/tests/test_openers.py index f6efdeef22..0d150a145c 100644 --- a/nibabel/tests/test_openers.py +++ b/nibabel/tests/test_openers.py @@ -341,10 +341,10 @@ def test_iter(): for input, does_t in files_to_test: with Opener(input, 'wb') as fobj: for line in lines: - fobj.write(bytes(line + os.linesep, 'ascii')) + fobj.write(str.encode(line + os.linesep)) with Opener(input, 'rb') as fobj: for back_line, line in zip(fobj, lines): - assert back_line.decode('latin-1').rstrip() == line + assert back_line.decode().rstrip() == line if not does_t: continue with Opener(input, 'rt') as fobj: From 5bb4d4cd67bf1ff0895f814d06445d6ba4c449ff Mon Sep 17 00:00:00 2001 From: Chris Markiewicz Date: Fri, 4 Aug 2023 10:33:07 -0400 Subject: [PATCH 8/8] TEST: Switch to single quotes for expected magic errors --- nibabel/tests/test_nifti1.py | 2 +- nibabel/tests/test_scripts.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/nibabel/tests/test_nifti1.py b/nibabel/tests/test_nifti1.py index 7b7f44fe0b..1031c6c1aa 100644 --- a/nibabel/tests/test_nifti1.py +++ b/nibabel/tests/test_nifti1.py @@ -251,7 +251,7 @@ def test_magic_offset_checks(self): fhdr, message, raiser = self.log_chk(hdr, 45) assert fhdr['magic'] == b'ooh' assert ( - message == 'magic string "ooh" is not valid; ' + message == "magic string 'ooh' is not valid; " 'leaving as is, but future errors are likely' ) # For pairs, any offset is OK, but should be divisible by 16 diff --git a/nibabel/tests/test_scripts.py b/nibabel/tests/test_scripts.py index e875065c8d..cc4bb468ad 100644 --- a/nibabel/tests/test_scripts.py +++ b/nibabel/tests/test_scripts.py @@ -228,7 +228,7 @@ def test_nib_nifti_dx(): expected = f"""Picky header check output for "{dirty_hdr}" pixdim[0] (qfac) should be 1 (default) or -1 -magic string "" is not valid +magic string '' is not valid sform_code 11776 not valid""" # Split strings to remove line endings assert stdout == expected