From f42c8922879242ac84bb0d6115b7d1a2371e428e Mon Sep 17 00:00:00 2001 From: jevans Date: Tue, 18 Mar 2014 18:48:09 -0400 Subject: [PATCH] Another case for a modified defaultdict. #199 --- glymur/codestream.py | 21 ++++--------------- glymur/core.py | 40 +++++++++++++++++++++++++----------- glymur/jp2box.py | 3 +++ glymur/test/test_jp2k.py | 7 +++++++ glymur/test/test_printing.py | 10 +++++++++ 5 files changed, 52 insertions(+), 29 deletions(-) diff --git a/glymur/codestream.py b/glymur/codestream.py index 9751e14..2e9184a 100644 --- a/glymur/codestream.py +++ b/glymur/codestream.py @@ -17,7 +17,6 @@ codestreams. # the base Segment class. # pylint: disable=R0903 -import collections import math import struct import sys @@ -28,30 +27,18 @@ import numpy as np from .core import LRCP, RLCP, RPCL, PCRL, CPRL from .core import WAVELET_XFORM_9X7_IRREVERSIBLE from .core import WAVELET_XFORM_5X3_REVERSIBLE +from .core import _Keydefaultdict from .lib import openjp2 as opj2 -class _keydefaultdict(collections.defaultdict): - """Unlisted keys help form their own error message. - - Normally defaultdict uses a factory function with no input arguments, but - that's not quite the behavior we want. - """ - def __missing__(self, key): - if self.default_factory is None: - raise KeyError(key) - else: - ret = self[key] = self.default_factory(key) - return ret - _factory = lambda x: '{0} (invalid)'.format(x) -_PROGRESSION_ORDER_DISPLAY = _keydefaultdict(_factory, +_PROGRESSION_ORDER_DISPLAY = _Keydefaultdict(_factory, { LRCP: 'LRCP', RLCP: 'RLCP', RPCL: 'RPCL', PCRL: 'PCRL', CPRL: 'CPRL'}) -_WAVELET_TRANSFORM_DISPLAY = _keydefaultdict(_factory, +_WAVELET_TRANSFORM_DISPLAY = _Keydefaultdict(_factory, { WAVELET_XFORM_9X7_IRREVERSIBLE: '9-7 irreversible', WAVELET_XFORM_5X3_REVERSIBLE: '5-3 reversible'}) @@ -64,7 +51,7 @@ _PROFILE_4 = 4 _KNOWN_PROFILES = [_NO_PROFILE, _PROFILE_0, _PROFILE_1, _PROFILE_3, _PROFILE_4] # How to display the codestream profile. -_CAPABILITIES_DISPLAY = _keydefaultdict(_factory, +_CAPABILITIES_DISPLAY = _Keydefaultdict(_factory, { _NO_PROFILE: 'no profile', _PROFILE_0: '0', _PROFILE_1: '1', diff --git a/glymur/core.py b/glymur/core.py index e593bd7..07949f9 100644 --- a/glymur/core.py +++ b/glymur/core.py @@ -1,8 +1,22 @@ """Core definitions to be shared amongst the modules. """ +import collections import copy import lxml.etree as ET +class _Keydefaultdict(collections.defaultdict): + """Unlisted keys help form their own error message. + + Normally defaultdict uses a factory function with no input arguments, but + that's not quite the behavior we want. + """ + def __missing__(self, key): + if self.default_factory is None: + raise KeyError(key) + else: + ret = self[key] = self.default_factory(key) + return ret + # Progression order LRCP = 0 RLCP = 1 @@ -57,24 +71,26 @@ YCC = 18 E_SRGB = 20 ROMM_RGB = 21 -_COLORSPACE_MAP_DISPLAY = { - CMYK: 'CMYK', - SRGB: 'sRGB', - GREYSCALE: 'greyscale', - YCC: 'YCC', - E_SRGB: 'e-sRGB', - ROMM_RGB: 'ROMM-RGB'} +_factory = lambda x: '{0} (unrecognized)'.format(x) +_COLORSPACE_MAP_DISPLAY = _Keydefaultdict(_factory, + { CMYK: 'CMYK', + SRGB: 'sRGB', + GREYSCALE: 'greyscale', + YCC: 'YCC', + E_SRGB: 'e-sRGB', + ROMM_RGB: 'ROMM-RGB'} ) # enumerated color channel types COLOR = 0 OPACITY = 1 PRE_MULTIPLIED_OPACITY = 2 _UNSPECIFIED = 65535 -_COLOR_TYPE_MAP_DISPLAY = { - COLOR: 'color', - OPACITY: 'opacity', - PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', - _UNSPECIFIED: 'unspecified'} +_factory = lambda x: '{0} (invalid)'.format(x) +_COLOR_TYPE_MAP_DISPLAY = _Keydefaultdict(_factory, + { COLOR: 'color', + OPACITY: 'opacity', + PRE_MULTIPLIED_OPACITY: 'pre-multiplied opacity', + _UNSPECIFIED: 'unspecified'}) # color channel definitions. RED = 1 diff --git a/glymur/jp2box.py b/glymur/jp2box.py index 9796f58..fa84b83 100644 --- a/glymur/jp2box.py +++ b/glymur/jp2box.py @@ -419,6 +419,9 @@ class ColourSpecificationBox(Jp2kBox): # enumerated colour space read_buffer = fptr.read(4) colorspace, = struct.unpack('>I', read_buffer) + if colorspace not in _COLORSPACE_MAP_DISPLAY.keys(): + msg = "Unrecognized colorspace: {0}".format(colorspace) + warnings.warn(msg) icc_profile = None else: diff --git a/glymur/test/test_jp2k.py b/glymur/test/test_jp2k.py index e4ce6a1..6dd9ba4 100644 --- a/glymur/test/test_jp2k.py +++ b/glymur/test/test_jp2k.py @@ -758,6 +758,13 @@ class TestJp2k_2_1(unittest.TestCase): class TestJp2kOpjDataRoot(unittest.TestCase): """These tests should be run by just about all configuration.""" + @unittest.skipIf(sys.hexversion < 0x03000000, "Test requires Python 3.3+") + def test_invalid_colorspace(self): + """Should warn in case of invalid colorspace.""" + filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2') + with self.assertWarns(UserWarning): + jp2 = Jp2k(filename) + def test_no_cxform_pclr_jp2(self): """Indices for pclr jpxfile if no color transform""" filename = opj_data_file('input/conformance/file9.jp2') diff --git a/glymur/test/test_printing.py b/glymur/test/test_printing.py index 51af6e0..65b181e 100644 --- a/glymur/test/test_printing.py +++ b/glymur/test/test_printing.py @@ -653,6 +653,16 @@ class TestPrintingOpjDataRoot(unittest.TestCase): def tearDown(self): pass + def test_invalid_colorspace(self): + """An invalid colorspace shouldn't cause an error.""" + filename = opj_data_file('input/nonregression/edf_c2_1103421.jp2') + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + jp2 = Jp2k(filename) + with patch('sys.stdout', new=StringIO()) as fake_out: + print(jp2) + + def test_bad_rsiz(self): """Should still be able to print if rsiz is bad, issue196""" filename = opj_data_file('input/nonregression/edf_c2_1002767.jp2')