[Git][debian-gis-team/glymur][upstream] New upstream version 0.13.3

Antonio Valentino (@antonio.valentino) gitlab at salsa.debian.org
Tue Jul 2 20:04:36 BST 2024



Antonio Valentino pushed to branch upstream at Debian GIS Project / glymur


Commits:
2c3f3c35 by Antonio Valentino at 2024-07-02T18:47:04+00:00
New upstream version 0.13.3
- - - - -


26 changed files:

- CHANGES.txt
- appveyor.yml
- ci/travis-310.yaml
- ci/travis-311-no-gdal.yaml
- ci/travis-311-no-opj.yaml
- ci/travis-311.yaml
- ci/travis-312.yaml
- ci/travis-39.yaml
- docs/source/conf.py
- docs/source/how_do_i.rst
- docs/source/whatsnew/0.13.rst
- glymur/codestream.py
- glymur/jp2box.py
- glymur/jp2k.py
- glymur/jp2kr.py
- glymur/version.py
- setup.cfg
- + tests/data/no_jp2c.jp2
- tests/test_codestream.py
- tests/test_jp2box.py
- tests/test_jp2k.py
- tests/test_jp2k_writes.py
- tests/test_jp2kr.py
- tests/test_libtiff.py
- tests/test_set_decoded_components.py
- tests/test_warnings.py


Changes:

=====================================
CHANGES.txt
=====================================
@@ -1,3 +1,10 @@
+June 30, 2024 - v0.13.3
+    Refactor parsing errors and warnings.
+    Update CI configuration for numpy 2.0.
+    Skip psnr doctest for numpy 2.0.
+    Fix test issue on s390x.
+    Refactor code pattern for finding first element.
+
 May 07, 2024 - v0.13.2
     Improve doctesting, fix broken libtiff doctest
     Increase code coverage


=====================================
appveyor.yml
=====================================
@@ -16,42 +16,36 @@ environment:
       PYTHON_VERSION: "3.9"
       PYTHON_ARCH: "64"
       CONDA_PY: "39"
-      CONDA_NPY: "23"
       USE_PATH_FOR_GDAL_PYTHON: "YES"
 
     - CONDA_ROOT: "C:\\Miniconda3_64"
       PYTHON_VERSION: "3.10"
       PYTHON_ARCH: "64"
       CONDA_PY: "310"
-      CONDA_NPY: "23"
       USE_PATH_FOR_GDAL_PYTHON: "YES"
 
     - CONDA_ROOT: "C:\\Miniconda3_64"
       PYTHON_VERSION: "3.11"
       PYTHON_ARCH: "64"
       CONDA_PY: "311-no-opj"
-      CONDA_NPY: "24"
       USE_PATH_FOR_GDAL_PYTHON: "YES"
 
     - CONDA_ROOT: "C:\\Miniconda3_64"
       PYTHON_VERSION: "3.11"
       PYTHON_ARCH: "64"
       CONDA_PY: "311-no-gdal"
-      CONDA_NPY: "24"
       USE_PATH_FOR_GDAL_PYTHON: "YES"
 
     - CONDA_ROOT: "C:\\Miniconda3_64"
       PYTHON_VERSION: "3.11"
       PYTHON_ARCH: "64"
       CONDA_PY: "311"
-      CONDA_NPY: "24"
       USE_PATH_FOR_GDAL_PYTHON: "YES"
 
     - CONDA_ROOT: "C:\\Miniconda3_64"
       PYTHON_VERSION: "3.12"
       PYTHON_ARCH: "64"
       CONDA_PY: "312"
-      CONDA_NPY: "26"
       USE_PATH_FOR_GDAL_PYTHON: "YES"
 
 # We always use a 64-bit machine, but can build x86 distributions


=====================================
ci/travis-310.yaml
=====================================
@@ -6,7 +6,7 @@ dependencies:
     - gdal
     - libtiff
     - lxml
-    - numpy
+    - numpy>=1.25,<1.26
     - openjpeg
     - pytest-xdist
     - scikit-image


=====================================
ci/travis-311-no-gdal.yaml
=====================================
@@ -5,7 +5,7 @@ dependencies:
     - python=3.11.*
     - libtiff
     - lxml
-    - numpy
+    - numpy>=1.26,<2.0
     - openjpeg
     - pytest-xdist
     - scikit-image


=====================================
ci/travis-311-no-opj.yaml
=====================================
@@ -5,7 +5,7 @@ dependencies:
     - python=3.11.*
     - gdal
     - lxml
-    - numpy
+    - numpy>=1.26,<2.0
     - pip
     - pytest-xdist
     - scikit-image


=====================================
ci/travis-311.yaml
=====================================
@@ -6,7 +6,7 @@ dependencies:
     - gdal
     - libtiff
     - lxml
-    - numpy
+    - numpy>=1.26,<2.0
     - openjpeg
     - pytest-xdist
     - scikit-image


=====================================
ci/travis-312.yaml
=====================================
@@ -6,7 +6,7 @@ dependencies:
     - gdal
     - libtiff
     - lxml
-    - numpy
+    - numpy>=2.0
     - openjpeg
     - pytest-xdist
     - scikit-image


=====================================
ci/travis-39.yaml
=====================================
@@ -5,7 +5,7 @@ dependencies:
     - python=3.9.*
     - gdal
     - lxml
-    - numpy
+    - numpy>=1.24,<1.25
     - openjpeg
     - scikit-image
     - libtiff


=====================================
docs/source/conf.py
=====================================
@@ -78,7 +78,7 @@ copyright = '2013-2024, John Evans'
 # The short X.Y version.
 version = '0.13'
 # The full version, including alpha/beta/rc tags.
-release = '0.13.2'
+release = '0.13.3'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.


=====================================
docs/source/how_do_i.rst
=====================================
@@ -248,7 +248,7 @@ We suppress a harmless warning from scikit-image below. ::
     >>> for layer in range(4):
     ...     jp2.layer = layer
     ...     psnr.append(skimage.metrics.peak_signal_noise_ratio(truth, jp2[:]))
-    >>> print(psnr)
+    >>> print(psnr)                # doctest: +SKIP
     [inf, 29.90221522329731, 39.71824592284344, 48.381047443043634]
 
 ... convert TIFF images to JPEG 2000?


=====================================
docs/source/whatsnew/0.13.rst
=====================================
@@ -2,6 +2,16 @@
 Changes in glymur 0.13
 ######################
 
+*****************
+Changes in 0.13.3
+*****************
+
+    * Refactor parsing errors and warnings.
+    * Update CI configuration for numpy 2.0.
+    * Skip psnr doctest for numpy 2.0.
+    * Fix test issue on s390x.
+    * Refactor code pattern for finding first element.
+
 *****************
 Changes in 0.13.2
 *****************


=====================================
glymur/codestream.py
=====================================
@@ -767,11 +767,10 @@ class Codestream(object):
             num_tiles_y = (xysiz[1] - xyosiz[1]) / (xytsiz[1] - xytosiz[1])
         except ZeroDivisionError:
             msg = (
-                f"Invalid tile specification:  "
-                f"size of {xytsiz[1]} x {xytsiz[0]}, "
-                f"offset of {xytosiz[1]} x {xytsiz[0]}."
+                f"Invalid tile specification in SIZ segment at byte offset "
+                f"{offset}:  tile size of {xytsiz[1]} x {xytsiz[0]}."
             )
-            warnings.warn(msg, UserWarning)
+            raise ZeroDivisionError(msg)
         else:
             numtiles = np.ceil(num_tiles_x) * np.ceil(num_tiles_y)
             if numtiles > 65535:


=====================================
glymur/jp2box.py
=====================================
@@ -71,7 +71,7 @@ _EXIF_UUID = UUID(bytes=b'JpgTiffExif->JP2')
 _XMP_UUID = UUID('be7acfcb-97a9-42e8-9c71-999491e3afac')
 
 
-class InvalidJp2kWarning(RuntimeError):
+class InvalidJp2kWarning(UserWarning):
     """Issue this warning in case the file is technically invalid but we can
     still read the image.
     """


=====================================
glymur/jp2k.py
=====================================
@@ -258,7 +258,7 @@ class Jp2k(Jp2kr):
         header box if we were so instructed.  This requires a wrapping
         operation.
         """
-        jp2h = [box for box in self.box if box.box_id == 'jp2h'][0]
+        jp2h = next(filter(lambda x: x.box_id == 'jp2h', self.box), None)
 
         extra_boxes = []
         if self._capture_resolution is not None:
@@ -1125,8 +1125,7 @@ class Jp2k(Jp2kr):
 
     def _validate_jp2_colr(self, boxes):
         """Validate JP2 requirements on colour specification boxes."""
-        lst = [box for box in boxes if box.box_id == 'jp2h']
-        jp2h = lst[0]
+        jp2h = next(filter(lambda x: x.box_id == 'jp2h', boxes), None)
         for colr in [box for box in jp2h.box if box.box_id == 'colr']:
             if colr.approximation != 0:
                 msg = (
@@ -1161,19 +1160,21 @@ class Jp2k(Jp2kr):
     def _validate_jp2c(self, boxes):
         """Validate the codestream box in relation to other boxes."""
         # jp2c must be preceeded by jp2h
-        jp2h_lst = [idx for (idx, box) in enumerate(boxes)
-                    if box.box_id == 'jp2h']
-        jp2h_idx = jp2h_lst[0]
-        jp2c_lst = [idx for (idx, box) in enumerate(boxes)
-                    if box.box_id == 'jp2c']
-        if len(jp2c_lst) == 0:
+        jp2h_idx, _ = next(
+            filter(lambda x: x[1].box_id == 'jp2h', enumerate(boxes)),
+            (None, None)
+        )
+        jp2c_idx, _ = next(
+            filter(lambda x: x[1].box_id == 'jp2c', enumerate(boxes)),
+            (None, None)
+        )
+        if jp2c_idx is None:
             msg = (
                 "A codestream box must be defined in the outermost list of "
                 "boxes."
             )
             raise InvalidJp2kError(msg)
 
-        jp2c_idx = jp2c_lst[0]
         if jp2h_idx >= jp2c_idx:
             msg = "The codestream box must be preceeded by a jp2 header box."
             raise InvalidJp2kError(msg)
@@ -1182,8 +1183,7 @@ class Jp2k(Jp2kr):
         """Validate the JP2 Header box."""
         self._check_jp2h_child_boxes(boxes, 'top-level')
 
-        jp2h_lst = [box for box in boxes if box.box_id == 'jp2h']
-        jp2h = jp2h_lst[0]
+        jp2h = next(filter(lambda x: x.box_id == 'jp2h', boxes), None)
 
         # 1st jp2 header box cannot be empty.
         if len(jp2h.box) == 0:
@@ -1199,20 +1199,18 @@ class Jp2k(Jp2kr):
             raise InvalidJp2kError(msg)
 
         # colr must be present in jp2 header box.
-        colr_lst = [
-            j for (j, box) in enumerate(jp2h.box) if box.box_id == 'colr'
-        ]
-        if len(colr_lst) == 0:
+        colr = next(filter(lambda x: x.box_id == 'colr', jp2h.box), None)
+        if colr is None:
             msg = "The jp2 header box must contain a color definition box."
             raise InvalidJp2kError(msg)
-        colr = jp2h.box[colr_lst[0]]
 
         self._validate_channel_definition(jp2h, colr)
 
     def _validate_channel_definition(self, jp2h, colr):
         """Validate the channel definition box."""
-        cdef_lst = [j for (j, box) in enumerate(jp2h.box)
-                    if box.box_id == 'cdef']
+        cdef_lst = [
+            idx for (idx, box) in enumerate(jp2h.box) if box.box_id == 'cdef'
+        ]
         if len(cdef_lst) > 1:
             msg = ("Only one channel definition box is allowed in the "
                    "JP2 header.")


=====================================
glymur/jp2kr.py
=====================================
@@ -9,11 +9,11 @@ License:  MIT
 # Standard library imports...
 from __future__ import annotations
 from contextlib import ExitStack
-from itertools import filterfalse
 import ctypes
 import pathlib
 import re
 import struct
+import sys
 import warnings
 
 # Third party library imports
@@ -22,7 +22,7 @@ import numpy as np
 # Local imports...
 from .codestream import Codestream
 from . import core, version, get_option
-from .jp2box import Jp2kBox, FileTypeBox, InvalidJp2kError
+from .jp2box import Jp2kBox, FileTypeBox, InvalidJp2kError, InvalidJp2kWarning
 from .lib import openjp2 as opj2
 
 
@@ -108,8 +108,8 @@ class Jp2kr(Jp2kBox):
             num_components = len(cstr.segment[1].xrsiz)
         else:
             # try to get the image size from the IHDR box
-            jp2h = [box for box in self.box if box.box_id == 'jp2h'][0]
-            ihdr = [box for box in jp2h.box if box.box_id == 'ihdr'][0]
+            jp2h = next(filter(lambda x: x.box_id == 'jp2h', self.box), None)
+            ihdr = next(filter(lambda x: x.box_id == 'ihdr', jp2h.box), None)
 
             height, width = ihdr.height, ihdr.width
             num_components = ihdr.num_components
@@ -189,10 +189,10 @@ class Jp2kr(Jp2kBox):
     @layer.setter
     def layer(self, layer):
         # Set to the indicated value so long as it is valid.
-        cod = [
-            segment for segment in self.codestream.segment
-            if segment.marker_id == 'COD'
-        ][0]
+        cod = next(
+            filter(lambda x: x.marker_id == 'COD', self.codestream.segment),
+            None
+        )
         if layer < 0 or layer >= cod.layers:
             msg = f"Invalid layer number, must be in range [0, {cod.layers})."
             raise ValueError(msg)
@@ -355,7 +355,13 @@ class Jp2kr(Jp2kBox):
             # Don't bother trying to validate JPX.
             return
 
-        jp2h = [box for box in self.box if box.box_id == 'jp2h'][0]
+        jp2h = next(filter(lambda x: x.box_id == 'jp2h', self.box), None)
+        if jp2h is None:
+            msg = (
+                "No JP2 header box was located in the outermost jacket of "
+                "boxes."
+            )
+            raise InvalidJp2kError(msg)
 
         # An IHDR box is required as the first child box of the JP2H box.
         if jp2h.box[0].box_id != 'ihdr':
@@ -373,7 +379,7 @@ class Jp2kr(Jp2kBox):
                     "enumerated colorspace or a restricted ICC profile if the "
                     "file type box brand is 'jp2 '."
                 )
-                warnings.warn(msg, UserWarning)
+                warnings.warn(msg, InvalidJp2kWarning)
 
         # We need to have one and only one JP2H box if we have a JP2 file.
         num_jp2h_boxes = len([box for box in self.box if box.box_id == 'jp2h'])
@@ -382,7 +388,7 @@ class Jp2kr(Jp2kBox):
                 f"This file has {num_jp2h_boxes} JP2H boxes in the outermost "
                 "layer of boxes.  There should only be one."
             )
-            warnings.warn(msg)
+            warnings.warn(msg, InvalidJp2kWarning)
 
         # We should have one and only one JP2C box if we have a JP2 file.
         num_jp2c_boxes = len([box for box in self.box if box.box_id == 'jp2c'])
@@ -404,10 +410,10 @@ class Jp2kr(Jp2kBox):
         ihdr = jp2h.box[0]
         ihdr_dims = ihdr.height, ihdr.width, ihdr.num_components
 
-        siz = [
-            segment for segment in self.codestream.segment
-            if segment.marker_id == 'SIZ'
-        ][0]
+        siz = next(
+            filter(lambda x: x.marker_id == 'SIZ', self.codestream.segment),
+            None
+        )
 
         siz_dims = (siz.ysiz, siz.xsiz, len(siz.bitdepth))
         if ihdr_dims != siz_dims:
@@ -460,9 +466,9 @@ class Jp2kr(Jp2kBox):
         if isinstance(pargs, tuple) and any(isinstance(x, int) for x in pargs):
             # Replace the first such integer argument, replace it with a slice.
             lst = list(pargs)
-            g = filterfalse(lambda x: not isinstance(x[1], int),
-                            enumerate(pargs))
-            idx = next(g)[0]
+            idx, _ = next(
+                filter(lambda x: isinstance(x[1], int), enumerate(lst)), None
+            )
             lst[idx] = slice(pargs[idx], pargs[idx] + 1)
             newindex = tuple(lst)
 
@@ -676,10 +682,13 @@ class Jp2kr(Jp2kBox):
         # Must check the specified rlevel against the maximum.
         if rlevel != 0:
             # Must check the specified rlevel against the maximum.
-            cod_seg = [
-                segment for segment in self.codestream.segment
-                if segment.marker_id == 'COD'
-            ][0]
+            cod_seg = next(
+                filter(
+                    lambda x: x.marker_id == 'COD',
+                    self.codestream.segment
+                ),
+                None
+            )
             max_rlevel = cod_seg.num_res
             if rlevel == -1:
                 # -1 is shorthand for the largest rlevel
@@ -889,25 +898,45 @@ class Jp2kr(Jp2kBox):
             Vertical, Horizontal Subsampling:  ((1, 1), (1, 1), (1, 1))
         """
         with self.path.open('rb') as fptr:
+
+            # if it's just a raw codestream file, it's easy
             if self._codec_format == opj2.CODEC_J2K:
-                codestream = Codestream(fptr, self.length,
-                                        header_only=header_only)
-            else:
-                box = [x for x in self.box if x.box_id == 'jp2c']
-                fptr.seek(box[0].offset)
+                return self._get_codestream(fptr, self.length, header_only)
+
+            # continue assuming JP2, must seek to the JP2C box and past its
+            # header
+            box = next(filter(lambda x: x.box_id == 'jp2c', self.box), None)
+
+            fptr.seek(box.offset)
+            read_buffer = fptr.read(8)
+            (box_length, _) = struct.unpack('>I4s', read_buffer)
+            if box_length == 0:
+                # The length of the box is presumed to last until the end
+                # of the file.  Compute the effective length of the box.
+                box_length = self.path.stat().st_size - fptr.tell() + 8
+            elif box_length == 1:
+                # Seek past the XL field.
                 read_buffer = fptr.read(8)
-                (box_length, _) = struct.unpack('>I4s', read_buffer)
-                if box_length == 0:
-                    # The length of the box is presumed to last until the end
-                    # of the file.  Compute the effective length of the box.
-                    box_length = self.path.stat().st_size - fptr.tell() + 8
-                elif box_length == 1:
-                    # Seek past the XL field.
-                    read_buffer = fptr.read(8)
-                    box_length, = struct.unpack('>Q', read_buffer)
-                codestream = Codestream(fptr, box_length - 8,
-                                        header_only=header_only)
+                box_length, = struct.unpack('>Q', read_buffer)
+
+            return self._get_codestream(fptr, box_length - 8, header_only)
+
+    def _get_codestream(self, fptr, length, header_only):
+        """
+        Parsing errors can make for confusing errors sometimes, so catch any
+        such error and add context to it.
+        """
 
+        try:
+            codestream = Codestream(fptr, length, header_only=header_only)
+        except Exception:
+            _, value, traceback = sys.exc_info()
+            msg = (
+                f'The file is invalid '
+                f'because the codestream could not be parsed:  "{value}"'
+            )
+            raise InvalidJp2kError(msg).with_traceback(traceback)
+        else:
             return codestream
 
     def _validate_nonzero_image_size(self, nrows, ncols, component_index):


=====================================
glymur/version.py
=====================================
@@ -20,7 +20,7 @@ from .lib import tiff
 
 # Do not change the format of this next line!  Doing so risks breaking
 # setup.py
-version = "0.13.2"
+version = "0.13.3"
 
 version_tuple = parse(version).release
 


=====================================
setup.cfg
=====================================
@@ -1,6 +1,6 @@
 [metadata]
 name = Glymur
-version = 0.13.2
+version = 0.13.3
 author = 'John Evans'
 author_email = "John Evans" <jevans667cc at proton.me>
 license = 'MIT'


=====================================
tests/data/no_jp2c.jp2
=====================================
Binary files /dev/null and b/tests/data/no_jp2c.jp2 differ


=====================================
tests/test_codestream.py
=====================================
@@ -6,18 +6,21 @@ Test suite for codestream oddities
 # Standard library imports ...
 import importlib.resources as ir
 from io import BytesIO
+import pathlib
 import struct
+import tempfile
 import unittest
 import warnings
 
 # Local imports ...
 import glymur
-from glymur import Jp2k
+from glymur import Jp2k, Jp2kr
+from glymur.jp2box import InvalidJp2kError
 from . import fixtures
 
 
 class TestSuite(fixtures.TestCommon):
-    """Test suite for ICC Profile code."""
+    """Test suite for codestreams."""
 
     def setUp(self):
         super().setUp()
@@ -28,6 +31,107 @@ class TestSuite(fixtures.TestCommon):
         self.issue142 = ir.files('tests.data').joinpath('issue142.j2k')
         self.edf_c2_1178956 = ir.files('tests.data').joinpath('edf_c2_1178956.jp2')  # noqa : E501
 
+    def test_unrecognized_marker(self):
+        """
+        SCENARIO:  There is an unrecognized marker just after an SOT marker but
+        before the EOC marker.  All markers must have a leading byte value of
+        0xff.
+
+        EXPECTED RESULT:  InvalidJp2kError
+        """
+        with open(self.temp_j2k_filename, mode='wb') as tfile:
+            with open(self.j2kfile, 'rb') as ifile:
+                # Everything up until the SOT marker.
+                read_buffer = ifile.read(98)
+                tfile.write(read_buffer)
+
+                # Write the bad marker 0xd900
+                read_buffer = struct.pack('>H', 0xd900)
+                tfile.write(read_buffer)
+
+                # Get the rest of the input file.
+                read_buffer = ifile.read()
+                tfile.write(read_buffer)
+                tfile.flush()
+
+            with self.assertRaises(InvalidJp2kError):
+                Jp2k(tfile.name).get_codestream(header_only=False)
+
+    def test_bad_tile_part_pointer(self):
+        """
+        SCENARIO:  A bad SOT marker segment is encountered (Psot value pointing
+        far beyond the end of the EOC marker) when requesting a fully parsed
+        codestream.
+
+        EXPECTED RESULT:  InvalidJp2kError
+        """
+        with open(self.temp_jp2_filename, 'wb') as ofile:
+            with open(self.jp2file, 'rb') as ifile:
+                # Copy up until Psot field.
+                ofile.write(ifile.read(204))
+
+                # Write a bad Psot value.
+                ofile.write(struct.pack('>I', 2000000))
+
+                # copy the rest of the file as-is.
+                ifile.seek(208)
+                ofile.write(ifile.read())
+                ofile.flush()
+
+        j = Jp2kr(self.temp_jp2_filename)
+        with self.assertRaises(InvalidJp2kError):
+            j.get_codestream(header_only=False)
+
+    def test_tile_height_is_zero(self):
+        """
+        Scenario:  A tile has height of zero.
+
+        Expected result:  ZeroDivisionError
+
+        Original test file was input/nonregression/2539.pdf.SIGFPE.706.1712.jp2
+        """
+        fp = BytesIO()
+
+        buffer = struct.pack('>H', 47)  # length
+
+        # kwargs = {'rsiz': 1,
+        #           'xysiz': (1000, 1000),
+        #           'xyosiz': (0, 0),
+        #           'xytsiz': (0, 1000),
+        #           'xytosiz': (0, 0),
+        #           'Csiz': 3,
+        #           'bitdepth': (8, 8, 8),
+        #           'signed':  (False, False, False),
+        #           'xyrsiz': ((1, 1, 1), (1, 1, 1)),
+        #           'length': 47,
+        #           'offset': 2}
+        buffer += struct.pack('>HIIIIIIIIH', 1, 1000, 1000, 0, 0, 0, 1000,
+                              0, 0, 3)
+        buffer += struct.pack('>BBBBBBBBB', 7, 1, 1, 7, 1, 1, 7, 1, 1)
+        fp.write(buffer)
+        fp.seek(0)
+
+        with self.assertRaises(ZeroDivisionError):
+            glymur.codestream.Codestream._parse_siz_segment(fp)
+
+    def test_invalid_codestream_past_header(self):
+        """
+        Scenario:  the codestream is ok thru the header, but invalid after
+        that.  The codestream header for the complete test file ends at byte
+
+        Expected result:  InvalidJp2kError
+        """
+        path = ir.files('tests.data').joinpath('p1_06.j2k')
+
+        with tempfile.TemporaryDirectory() as tdir:
+            with open(path, mode='rb') as ifile:
+                with open(pathlib.Path(tdir) / 'tmp.j2k', mode='wb') as ofile:
+                    ofile.write(ifile.read(555))
+
+                with self.assertRaises(InvalidJp2kError):
+                    j = Jp2k(pathlib.Path(tdir) / 'tmp.j2k')
+                    j.get_codestream(header_only=False)
+
     def test_tlm_segment(self):
         """
         Verify parsing of the TLM segment.
@@ -141,12 +245,14 @@ class TestSuite(fixtures.TestCommon):
 
     def test_626(self):
         """
-        Scenario:
+        Scenario:  After parsing the SOC and SIZ segments, an unknown segment
+        (probably invalid) is hit, and then the file ends, leaving us trying
+        to interpret EOF as another marker segment.
 
-        Expected result:  J2KParseError
+        Expected result:  InvalidJp2kError
         """
         path = ir.files('tests.data').joinpath('issue626.j2k')
-        with self.assertRaises(glymur.codestream.J2KParseError):
+        with self.assertRaises(InvalidJp2kError):
             Jp2k(path)
 
 


=====================================
tests/test_jp2box.py
=====================================
@@ -1,7 +1,6 @@
 """Test suite specifically targeting JP2 box layout.
 """
 # Standard library imports ...
-import doctest
 import importlib.resources as ir
 from io import BytesIO
 import os
@@ -33,20 +32,6 @@ from . import fixtures
 from .fixtures import OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG
 
 
-def docTearDown(doctest_obj):  # pragma: no cover
-    glymur.set_option('parse.full_codestream', False)
-
-
-def load_tests(loader, tests, ignore):  # pragma: no cover
-    """Run doc tests as well."""
-    if os.name == "nt":
-        # Can't do it on windows, temporary file issue.
-        return tests
-    tests.addTests(doctest.DocTestSuite('glymur.jp2box',
-                                        tearDown=docTearDown))
-    return tests
-
-
 @unittest.skipIf(OPENJPEG_NOT_AVAILABLE, OPENJPEG_NOT_AVAILABLE_MSG)
 class TestDataEntryURL(fixtures.TestCommon):
     """Test suite for DataEntryURL boxes."""
@@ -1055,11 +1040,40 @@ class TestJp2Boxes(fixtures.TestCommon):
         box._filename = str(self.jp2file)
         box.codestream
 
+    def test_no_jp2h_box(self):
+        """
+        SCENARIO:  The JP2/JP2H box is missing
+
+        EXPECTED RESULT:  InvalidJp2kError
+        """
+        # Write a new JP2 file that omits the IHDR box.
+        j = Jp2k(self.jp2file)
+        jp2h = [box for box in j.box if box.box_id == 'jp2h'][0]
+        with open(self.temp_jp2_filename, mode='wb') as tfile:
+            numbytes = jp2h.offset
+            with open(self.jp2file, 'rb') as ifile:
+                # Write all the way up to the ihdr box
+                tfile.write(ifile.read(numbytes))
+
+                # Seek past the ihdr box
+                ifile.seek(jp2h.length, os.SEEK_CUR)
+
+                # Write the rest of the JP2 file
+                tfile.write(ifile.read(numbytes))
+
+            tfile.flush()
+
+            with self.assertRaises(InvalidJp2kError):
+                with warnings.catch_warnings():
+                    # Lots of things wrong with this file.
+                    warnings.simplefilter('ignore')
+                    Jp2k(tfile.name)
+
     def test_no_ihdr_box(self):
         """
         SCENARIO:  The JP2/IHDR box cannot be parsed.
 
-        EXPECTED RESULT:  An RuntimeError is issued.
+        EXPECTED RESULT:  InvalidJp2kError
         """
         # Write a new JP2 file that omits the IHDR box.
         j = Jp2k(self.jp2file)
@@ -1089,19 +1103,16 @@ class TestJp2Boxes(fixtures.TestCommon):
         """
         SCENARIO:  The JP2 file has no JP2C box.
 
-        EXPECTED RESULT:  An InvalidJp2kError is issued.
+        EXPECTED RESULT:  An InvalidJp2kError is issued when the file is
+        parsed.
         """
-        # Write a new JP2 file that omits the JP2C box.
-        j = Jp2k(self.jp2file)
-        jp2c = [box for box in j.box if box.box_id == 'jp2c'][0]
-        with open(self.temp_jp2_filename, mode='wb') as tfile:
-            numbytes = jp2c.offset
-            with open(self.jp2file, 'rb') as ifile:
-                tfile.write(ifile.read(numbytes))
-            tfile.flush()
+        testfile = ir.files('tests.data').joinpath('no_jp2c.jp2')
 
+        with warnings.catch_warnings():
+            # Lots of things wrong with this file.
+            warnings.simplefilter('ignore')
             with self.assertRaises(InvalidJp2kError):
-                Jp2k(tfile.name)
+                Jp2k(testfile)
 
     def test_two_jp2c_boxes(self):
         """


=====================================
tests/test_jp2k.py
=====================================
@@ -262,31 +262,6 @@ class TestJp2k(fixtures.TestCommon):
 
         np.testing.assert_array_equal(rgb, bgr[:, :, [2, 1, 0]])
 
-    def test_bad_tile_part_pointer(self):
-        """
-        SCENARIO:  A bad SOT marker segment is encountered (Psot value pointing
-        far beyond the end of the EOC marker) when requesting a fully parsed
-        codestream.
-
-        EXPECTED RESULT:  struct.error
-        """
-        with open(self.temp_jp2_filename, 'wb') as ofile:
-            with open(self.jp2file, 'rb') as ifile:
-                # Copy up until Psot field.
-                ofile.write(ifile.read(204))
-
-                # Write a bad Psot value.
-                ofile.write(struct.pack('>I', 2000000))
-
-                # copy the rest of the file as-is.
-                ifile.seek(208)
-                ofile.write(ifile.read())
-                ofile.flush()
-
-        j = Jp2k(self.temp_jp2_filename)
-        with self.assertRaises(struct.error):
-            j.get_codestream(header_only=False)
-
     def test_read_differing_subsamples(self):
         """
         SCENARIO:  Attempt to read a file where the components have differing
@@ -393,20 +368,6 @@ class TestJp2k(fixtures.TestCommon):
         with self.assertRaises(InvalidJp2kError):
             Jp2k(path)
 
-    @unittest.skip("This test may not be appropriate")
-    def test_file_does_not_exist(self):
-        """
-        Scenario:  The Jp2k construtor is passed a file that does not exist
-        and the intent is reading.
-
-        Expected Result:  FileNotFoundError
-        """
-        # Verify that we error out appropriately if not given an existing file
-        # at all.
-        filename = 'this file does not actually exist on the file system.'
-        with self.assertRaises(FileNotFoundError):
-            Jp2k(filename)
-
     def test_codestream(self):
         """
         Verify the markers and segments of a JP2 file codestream.


=====================================
tests/test_jp2k_writes.py
=====================================
@@ -1842,7 +1842,7 @@ class TestSuite(fixtures.TestCommon):
 
     def test_1x1_tile(self):
         """
-        SCENARIO:  Write an image that is tiled 1x1.
+        SCENARIO:  Write by tiles an image that is tiled 1x1.
 
         EXPECTED RESULT:  RuntimeError, as this triggers an unresolved
         bug, issue586.
@@ -1858,8 +1858,7 @@ class TestSuite(fixtures.TestCommon):
             self.temp_j2k_filename, shape=shape, tilesize=tilesize,
         )
         with self.assertRaises(RuntimeError):
-            for tw in j.get_tilewriters():
-                tw[:] = j2k_data
+            j.get_tilewriters()
 
     def test_openjpeg_library_too_old_for_tile_writing(self):
         """


=====================================
tests/test_jp2kr.py
=====================================
@@ -205,31 +205,6 @@ class TestJp2kr(fixtures.TestCommon):
                 rgb_from_idx[r, c] = palette[idx[r, c]]
         np.testing.assert_array_equal(rgb, rgb_from_idx)
 
-    def test_bad_tile_part_pointer(self):
-        """
-        SCENARIO:  A bad SOT marker segment is encountered (Psot value pointing
-        far beyond the end of the EOC marker) when requesting a fully parsed
-        codestream.
-
-        EXPECTED RESULT:  struct.error
-        """
-        with open(self.temp_jp2_filename, 'wb') as ofile:
-            with open(self.jp2file, 'rb') as ifile:
-                # Copy up until Psot field.
-                ofile.write(ifile.read(204))
-
-                # Write a bad Psot value.
-                ofile.write(struct.pack('>I', 2000000))
-
-                # copy the rest of the file as-is.
-                ifile.seek(208)
-                ofile.write(ifile.read())
-                ofile.flush()
-
-        j = Jp2kr(self.temp_jp2_filename)
-        with self.assertRaises(struct.error):
-            j.get_codestream(header_only=False)
-
     def test_read_differing_subsamples(self):
         """
         SCENARIO:  Attempt to read a file where the components have differing


=====================================
tests/test_libtiff.py
=====================================
@@ -1,6 +1,7 @@
 # standard library imports
 import importlib.resources as ir
 import platform
+import sys
 import unittest
 from unittest.mock import patch
 import warnings
@@ -119,6 +120,9 @@ class TestSuite(fixtures.TestCommon):
         actual = libtiff.readRGBAImageOriented(fp)
         libtiff.close(fp)
 
+        # Adjust for big-endian if necessary
+        actual = np.flip(actual, 2) if sys.byteorder == 'big' else actual
+
         error = fixtures.skimage.metrics.mean_squared_error(
             actual[:, :, :3], expected
         )


=====================================
tests/test_set_decoded_components.py
=====================================
@@ -150,7 +150,6 @@ class TestSuite(unittest.TestCase):
             with warnings.catch_warnings():
                 warnings.simplefilter('error')
                 j2k.decoded_components = -1
-                j2k[:]
 
     def test_same_component_several_times(self):
         """


=====================================
tests/test_warnings.py
=====================================
@@ -78,33 +78,6 @@ class TestSuite(fixtures.TestCommon):
                 # c = Jp2k(tfile.name).get_codestream(header_only=False)
                 Jp2k(tfile.name)
 
-    def test_unrecognized_marker(self):
-        """
-        SCENARIO:  There is an unrecognized marker just after an SOT marker but
-        before the EOC marker.  All markers must have a leading byte value of
-        0xff.
-
-        EXPECTED RESULT:  The SOT marker is the last one retrieved from the
-        codestream.
-        """
-        with open(self.temp_j2k_filename, mode='wb') as tfile:
-            with open(self.j2kfile, 'rb') as ifile:
-                # Everything up until the SOT marker.
-                read_buffer = ifile.read(98)
-                tfile.write(read_buffer)
-
-                # Write the bad marker 0xd900
-                read_buffer = struct.pack('>H', 0xd900)
-                tfile.write(read_buffer)
-
-                # Get the rest of the input file.
-                read_buffer = ifile.read()
-                tfile.write(read_buffer)
-                tfile.flush()
-
-            with self.assertRaises(ValueError):
-                Jp2k(tfile.name).get_codestream(header_only=False)
-
     def test_unrecoverable_xml(self):
         """
         Bad byte sequence in XML that cannot be parsed.
@@ -123,36 +96,6 @@ class TestSuite(fixtures.TestCommon):
 
         self.assertIsNone(box.xml)
 
-    def test_tile_height_is_zero(self):
-        """
-        Zero tile height should not cause an exception.
-
-        Original test file was input/nonregression/2539.pdf.SIGFPE.706.1712.jp2
-        """
-        fp = BytesIO()
-
-        buffer = struct.pack('>H', 47)  # length
-
-        # kwargs = {'rsiz': 1,
-        #           'xysiz': (1000, 1000),
-        #           'xyosiz': (0, 0),
-        #           'xytsiz': (0, 1000),
-        #           'xytosiz': (0, 0),
-        #           'Csiz': 3,
-        #           'bitdepth': (8, 8, 8),
-        #           'signed':  (False, False, False),
-        #           'xyrsiz': ((1, 1, 1), (1, 1, 1)),
-        #           'length': 47,
-        #           'offset': 2}
-        buffer += struct.pack('>HIIIIIIIIH', 1, 1000, 1000, 0, 0, 0, 1000,
-                              0, 0, 3)
-        buffer += struct.pack('>BBBBBBBBB', 7, 1, 1, 7, 1, 1, 7, 1, 1)
-        fp.write(buffer)
-        fp.seek(0)
-
-        with self.assertWarns(UserWarning):
-            glymur.codestream.Codestream._parse_siz_segment(fp)
-
     def test_invalid_progression_order(self):
         """
         Should still be able to parse even if prog order is invalid.



View it on GitLab: https://salsa.debian.org/debian-gis-team/glymur/-/commit/2c3f3c350e519789ba77935f2b1dc407cc6a7c20

-- 
This project does not include diff previews in email notifications.
View it on GitLab: https://salsa.debian.org/debian-gis-team/glymur/-/commit/2c3f3c350e519789ba77935f2b1dc407cc6a7c20
You're receiving this email because of your account on salsa.debian.org.


-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://alioth-lists.debian.net/pipermail/pkg-grass-devel/attachments/20240702/b6e07818/attachment-0001.htm>


More information about the Pkg-grass-devel mailing list