diff --git a/.github/workflows/pythonpackage.yml b/.github/workflows/pythonpackage.yml index ff24ead..37c4534 100644 --- a/.github/workflows/pythonpackage.yml +++ b/.github/workflows/pythonpackage.yml @@ -13,14 +13,11 @@ jobs: fail-fast: false matrix: os: [ubuntu-latest, macOS-latest, windows-latest] - python-version: [3.6, 3.7, 3.8, 3.9, "3.10"] - exclude: - - os: macOS-latest - python-version: 3.6 + python-version: [3.7, 3.8, 3.9, "3.10"] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v1 + uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - name: Install dependencies @@ -29,7 +26,7 @@ jobs: pip install -r requirements-dev.txt - name: Run tests with coverage run: | - if [[ '${{ matrix.os }}' == 'ubuntu-latest' && '${{ matrix.python-version }}' == '3.7' ]] + if [[ '${{ matrix.os }}' == 'ubuntu-latest' && '${{ matrix.python-version }}' == '3.9' ]] then python -m pytest --cov=dicom_validator --cov-config=.coveragerc dicom_validator else @@ -37,6 +34,6 @@ jobs: fi - name: Upload coverage to Codecov uses: codecov/codecov-action@v1 - if: ${{ success() && matrix.python-version == 3.7 && matrix.os == 'ubuntu-latest' }} + if: ${{ success() && matrix.python-version == 3.9 && matrix.os == 'ubuntu-latest' }} with: name: codecov-dicom-validator diff --git a/CHANGES.md b/CHANGES.md index 9824fb6..ede2fc5 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -6,12 +6,17 @@ The released versions correspond to PyPi releases. ### Fixes * condition parser: multiple or expressions handled correctly * Condition parser: handle a few more complicated conditions +* Condition parser: handle conditions without values, + see [#15](../.. /issues/15) + +### Changes +* Removed support for Python 3.6 ## [Version 0.3.4](https://pypi.python.org/pypi/dicom-validator/0.3.3) (2021-11-22) Fixes a regression introduced with the last release. ### Fixes -- fixed regression that broke the validator command line tool +- fixed regression that broke the validator command line tool, see [#9](../../issues/9)) ## [Version 0.3.3](https://pypi.python.org/pypi/dicom-validator/0.3.3) (2021-11-20) diff --git a/dicom_validator/spec_reader/condition.py b/dicom_validator/spec_reader/condition.py index 985dc4c..a4a60d6 100644 --- a/dicom_validator/spec_reader/condition.py +++ b/dicom_validator/spec_reader/condition.py @@ -104,24 +104,30 @@ def to_string(self, dict_info: Dict) -> str: result = self.tag if self.index: result += '[{}]'.format(self.index) + if self.operator is None: + return result if self.operator == '+': result += ' exists' elif self.operator == '++': result += ' exists and has a value' + elif self.operator == '-': + result += ' is not present' elif self.operator == '=>': tag_value = int(self.values[0]) result += ' points to ' + tag_name_from_id(tag_value, dict_info) - elif self.operator == '-': - result += ' is not present' + elif not self.values: + # if no values are found here, we have some unhandled condition + # and ignore it for the time being + return result elif self.operator == '=': - result += ' is equal to ' values = ['"' + value + '"' for value in self.values] + result += ' is equal to ' if len(values) > 1: result += ', '.join(values[:-1]) + ' or ' result += values[-1] elif self.operator == '!=': - result += ' is not equal to ' values = ['"' + value + '"' for value in self.values] + result += ' is not equal to ' if len(values) > 1: result += ', '.join(values[:-1]) + ' and ' result += values[-1] diff --git a/dicom_validator/spec_reader/condition_parser.py b/dicom_validator/spec_reader/condition_parser.py index 88f82eb..a443ebc 100644 --- a/dicom_validator/spec_reader/condition_parser.py +++ b/dicom_validator/spec_reader/condition_parser.py @@ -82,8 +82,10 @@ def parse(self, condition_str: str) -> Condition: """Parse the given condition string and return a Condition object with the required attributes. """ - condition_prefixes = ('required if ', 'shall be present if ', - 'required for images where ') + condition_prefixes = ( + 'required if ', 'shall be present if ', + 'required for images where ', 'required only if' + ) for prefix in condition_prefixes: index = condition_str.lower().find(prefix) if index >= 0: diff --git a/dicom_validator/spec_reader/tests/test_condition_parser.py b/dicom_validator/spec_reader/tests/test_condition_parser.py index 99f8785..752061f 100644 --- a/dicom_validator/spec_reader/tests/test_condition_parser.py +++ b/dicom_validator/spec_reader/tests/test_condition_parser.py @@ -33,21 +33,34 @@ def test_ignore_invalid_condition(self): def test_ignore_uncheckable_tag_condition(self): result = self.parser.parse( 'Required if Numeric Value (0040,A30A) has insufficient ' - 'precision to represent the value as a string.') + 'precision to represent the value as a string.' + ) self.assertEqual('U', result.type) self.assertIsNone(result.tag) def test_ignore_condition_without_tag(self): result = self.parser.parse( 'Required if present and consistent ' - 'in the contributing SOP Instances. ') + 'in the contributing SOP Instances. ' + ) self.assertEqual('U', result.type) + def test_handle_condition_without_value(self): + # regression test for #15 + result = self.parser.parse( + 'required if Selector Attribute (0072,0026) is nested in ' + 'one or more Sequences or is absent.' + ) + self.assertEqual('MN', result.type) + self.assertEqual('(0072,0026)', result.tag) + self.assertEqual('=', result.operator) + class SimpleConditionParserTest(ConditionParserTest): def test_not_present(self): result = self.parser.parse( - 'Required if VOI LUT Sequence (0028,3010) is not present.') + 'Required if VOI LUT Sequence (0028,3010) is not present.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0028,3010)', result.tag) self.assertEqual('-', result.operator) @@ -56,7 +69,8 @@ def test_not_present(self): def test_operator_in_tag(self): result = self.parser.parse( 'Required if Fractional Channel Display Scale ' - '(003A,0247) is not present') + '(003A,0247) is not present' + ) self.assertEqual('MN', result.type) self.assertEqual('(003A,0247)', result.tag) self.assertEqual('-', result.operator) @@ -65,7 +79,8 @@ def test_operator_in_tag(self): def test_is_present(self): result = self.parser.parse( 'Required if Bounding Box Top Left Hand Corner ' - '(0070,0010) is present.') + '(0070,0010) is present.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0070,0010)', result.tag) self.assertEqual('+', result.operator) @@ -73,7 +88,8 @@ def test_is_present(self): def test_is_present_with_value(self): result = self.parser.parse( - 'Required if Responsible Person is present and has a value.') + 'Required if Responsible Person is present and has a value.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0010,2297)', result.tag) self.assertEqual('++', result.operator) @@ -81,7 +97,8 @@ def test_is_present_with_value(self): def test_is_present_tag_name_with_digit(self): result = self.parser.parse( - 'Required if 3D Mating Point (0068,64C0) is present.') + 'Required if 3D Mating Point (0068,64C0) is present.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0068,64C0)', result.tag) self.assertEqual('+', result.operator) @@ -90,7 +107,8 @@ def test_is_present_tag_name_with_digit(self): def test_not_sent(self): result = self.parser.parse( 'Required if Anatomic Region Modifier Sequence ' - '(0008,2220) is not sent. ') + '(0008,2220) is not sent. ' + ) self.assertEqual('MN', result.type) self.assertEqual('(0008,2220)', result.tag) self.assertEqual('-', result.operator) @@ -99,18 +117,30 @@ def test_not_sent(self): def test_shall_be_condition_with_absent_tag(self): result = self.parser.parse( 'Some Stuff. Shall be present if Clinical Trial Subject Reading ID' - ' (0012,0042) is absent. May be present otherwise.') + ' (0012,0042) is absent. May be present otherwise.' + ) self.assertEqual('MU', result.type) self.assertEqual('(0012,0042)', result.tag) self.assertEqual(0, result.index) self.assertEqual('-', result.operator) self.assertEqual([], result.values) + def test_required_only_if(self): + result = self.parser.parse( + 'Required only if Referenced Dose Reference Number (300C,0051) ' + 'is not present. It shall not be present otherwise.' + ) + self.assertEqual('MN', result.type) + self.assertEqual('(300C,0051)', result.tag) + self.assertEqual('-', result.operator) + self.assertEqual([], result.values) + class ValueConditionParserTest(ConditionParserTest): def test_equality_tag(self): result = self.parser.parse( - 'C - Required if Modality (0008,0060) = IVUS') + 'C - Required if Modality (0008,0060) = IVUS' + ) self.assertEqual('MN', result.type) self.assertEqual('(0008,0060)', result.tag) self.assertEqual(0, result.index) @@ -128,7 +158,8 @@ def test_equality_tag_without_tag_id(self): def test_multiple_values_and_index(self): result = self.parser.parse( 'C - Required if Image Type (0008,0008) Value 3 ' - 'is GATED, GATED TOMO, or RECON GATED TOMO') + 'is GATED, GATED TOMO, or RECON GATED TOMO' + ) self.assertEqual('MN', result.type) self.assertEqual('(0008,0008)', result.tag) self.assertEqual(2, result.index) @@ -149,7 +180,8 @@ def test_multiple_values_with_or(self): def test_comma_before_value(self): result = self.parser.parse( - 'Required if Series Type (0054,1000), Value 2 is REPROJECTION.') + 'Required if Series Type (0054,1000), Value 2 is REPROJECTION.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0054,1000)', result.tag) self.assertEqual(1, result.index) @@ -159,7 +191,8 @@ def test_comma_before_value(self): def test_may_be_present_otherwise(self): result = self.parser.parse( 'C - Required if Image Type (0008,0008) Value 1 equals ORIGINAL.' - ' May be present otherwise.') + ' May be present otherwise.' + ) self.assertEqual('MU', result.type) self.assertEqual('(0008,0008)', result.tag) self.assertEqual(0, result.index) @@ -168,7 +201,8 @@ def test_may_be_present_otherwise(self): def test_greater_operator(self): result = self.parser.parse( - 'C - Required if Number of Frames is greater than 1') + 'C - Required if Number of Frames is greater than 1' + ) self.assertEqual('MN', result.type) self.assertEqual('(0028,0008)', result.tag) self.assertEqual(0, result.index) @@ -178,7 +212,8 @@ def test_greater_operator(self): def test_value_greater_than_operator(self): result = self.parser.parse( 'Required if Samples per Pixel ' - '(0028,0002) has a value greater than 1') + '(0028,0002) has a value greater than 1' + ) self.assertEqual('MN', result.type) self.assertEqual('(0028,0002)', result.tag) self.assertEqual(0, result.index) @@ -188,7 +223,8 @@ def test_value_greater_than_operator(self): def test_tag_ids_as_values(self): result = self.parser.parse( 'C - Required if Frame Increment Pointer (0028,0009) ' - 'is Frame Time (0018,1063) or Frame Time Vector (0018,1065)') + 'is Frame Time (0018,1063) or Frame Time Vector (0018,1065)' + ) self.assertEqual('MN', result.type) self.assertEqual('(0028,0009)', result.tag) self.assertEqual(0, result.index) @@ -200,7 +236,8 @@ def test_tag_ids_as_values(self): def test_has_a_value_of(self): result = self.parser.parse( 'Required if Pixel Presentation ' - '(0008,9205) has a value of TRUE_COLOR.') + '(0008,9205) has a value of TRUE_COLOR.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0008,9205)', result.tag) self.assertEqual('=', result.operator) @@ -209,7 +246,8 @@ def test_has_a_value_of(self): def test_at_the_image_level_equals(self): result = self.parser.parse( '"Required if Pixel Presentation (0008,9205) at the image level ' - 'equals COLOR or MIXED.') + 'equals COLOR or MIXED.' + ) self.assertEqual('MN', result.type) self.assertEqual('=', result.operator) self.assertEqual(['COLOR', 'MIXED'], result.values) @@ -217,7 +255,8 @@ def test_at_the_image_level_equals(self): def test_is_with_colon(self): result = self.parser.parse( 'Required if Image Type ' - '(0008,0008) Value 3 is: WHOLE BODY or STATIC.') + '(0008,0008) Value 3 is: WHOLE BODY or STATIC.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0008,0008)', result.tag) self.assertEqual('=', result.operator) @@ -226,7 +265,8 @@ def test_is_with_colon(self): def test_remove_apostrophes(self): result = self.parser.parse( - 'Required if Lossy Image Compression (0028,2110) is "01".') + 'Required if Lossy Image Compression (0028,2110) is "01".' + ) self.assertEqual('=', result.operator) self.assertEqual(['01'], result.values) @@ -234,7 +274,8 @@ def test_remove_apostrophes_from_uids(self): result = self.parser.parse( 'Required if SOP Class UID (0008,0016) ' 'equals "1.2.840.10008.5.1.4.1.1.12.1.1" ' - 'or "1.2.840.10008.5.1.4.1.1.12.2.1". May be present otherwise.') + 'or "1.2.840.10008.5.1.4.1.1.12.2.1". May be present otherwise.' + ) self.assertEqual('=', result.operator) self.assertEqual(['1.2.840.10008.5.1.4.1.1.12.1.1', '1.2.840.10008.5.1.4.1.1.12.2.1'], result.values) @@ -242,7 +283,8 @@ def test_remove_apostrophes_from_uids(self): def test_value_of(self): result = self.parser.parse( 'Required if the value of Context Group Extension Flag ' - '(0008,010B) is "Y".') + '(0008,010B) is "Y".' + ) self.assertEqual('MN', result.type) self.assertEqual('=', result.operator) self.assertEqual(['Y'], result.values) @@ -250,7 +292,8 @@ def test_value_of(self): def test_value_more_than(self): result = self.parser.parse( 'Required if Data Point Rows ' - '(0028,9001) has a value of more than 1.') + '(0028,9001) has a value of more than 1.' + ) self.assertEqual('MN', result.type) self.assertEqual('(0028,9001)', result.tag) self.assertEqual('>', result.operator) @@ -259,7 +302,8 @@ def test_value_more_than(self): def test_is_not_with_uid(self): result = self.parser.parse( 'Required if SOP Class UID is not ' - '"1.2.840.10008.5.1.4.1.1.4.4" (Legacy Converted).') + '"1.2.840.10008.5.1.4.1.1.4.4" (Legacy Converted).' + ) self.assertEqual('MN', result.type) self.assertEqual('(0008,0016)', result.tag) self.assertEqual('!=', result.operator) @@ -268,14 +312,16 @@ def test_is_not_with_uid(self): def test_present_with_value(self): result = self.parser.parse( 'Required if Selector Attribute VR ' - '(0072,0050) is present and the value is AS.') + '(0072,0050) is present and the value is AS.' + ) self.assertEqual('MN', result.type) self.assertEqual('=', result.operator) self.assertEqual(['AS'], result.values) def test_other_than(self): result = self.parser.parse( - 'Required if Decay Correction (0054,1102) is other than NONE.') + 'Required if Decay Correction (0054,1102) is other than NONE.' + ) self.assertEqual('MN', result.type) self.assertEqual('!=', result.operator) self.assertEqual(['NONE'], result.values) @@ -283,7 +329,8 @@ def test_other_than(self): def test_not_equal_to(self): result = self.parser.parse( 'Required if Planes in Acquisition ' - '(0018,9410) is not equal to UNDEFINED.') + '(0018,9410) is not equal to UNDEFINED.' + ) self.assertEqual('MN', result.type) self.assertEqual('!=', result.operator) self.assertEqual(['UNDEFINED'], result.values) @@ -291,7 +338,8 @@ def test_not_equal_to(self): def test_present_with_value_of(self): result = self.parser.parse( 'Required if Partial View ' - '(0028,1350) is present with a value of YES.') + '(0028,1350) is present with a value of YES.' + ) self.assertEqual('MN', result.type) self.assertEqual('=', result.operator) self.assertEqual(['YES'], result.values) @@ -299,7 +347,8 @@ def test_present_with_value_of(self): def test_points_to_tag(self): result = self.parser.parse( 'Required if Frame Increment Pointer (0028,0009) points to ' - 'Frame Label Vector (0018,2002).') + 'Frame Label Vector (0018,2002).' + ) self.assertEqual('MN', result.type) self.assertEqual('=>', result.operator) self.assertEqual(['1581058'], result.values) @@ -309,7 +358,8 @@ class NotMandatoryConditionParserTest(ConditionParserTest): def test_default(self): result = self.parser.parse( 'Required if Image Type ' - '(0008,0008) Value 1 is ORIGINAL. May be present otherwise.') + '(0008,0008) Value 1 is ORIGINAL. May be present otherwise.' + ) self.assertEqual('MU', result.type) self.assertEqual('=', result.operator) self.assertEqual(['ORIGINAL'], result.values) @@ -318,14 +368,16 @@ def test_comma_instead_of_dot(self): result = self.parser.parse( 'Required if Absolute Channel Display Scale ' '(003A,0248) is not present, ' - 'may be present otherwise.') + 'may be present otherwise.' + ) self.assertEqual('MU', result.type) self.assertEqual('-', result.operator) def test_missing_dot(self): result = self.parser.parse( 'Required if Image Type ' - '(0008,0008) Value 1 is ORIGINAL May be present otherwise.') + '(0008,0008) Value 1 is ORIGINAL May be present otherwise.' + ) self.assertEqual('MU', result.type) self.assertEqual('=', result.operator) self.assertEqual(['ORIGINAL'], result.values) @@ -335,7 +387,8 @@ class CompositeConditionParserTest(ConditionParserTest): def test_and_condition(self): result = self.parser.parse( 'Required if Series Type (0054,1000), Value 1 is GATED and ' - 'Beat Rejection Flag (0018,1080) is Y.') + 'Beat Rejection Flag (0018,1080) is Y.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) result1 = result.and_conditions[0] @@ -350,7 +403,8 @@ def test_and_condition(self): def test_ignore_unverifyable_and_condition(self): result = self.parser.parse( 'Required if Delivery Type (300A,00CE) is CONTINUATION and ' - 'one or more channels of any Application Setup are omitted.') + 'one or more channels of any Application Setup are omitted.' + ) self.assertEqual('MN', result.type) self.assertEqual(0, len(result.and_conditions)) self.assertEqual('=', result.operator) @@ -359,7 +413,8 @@ def test_ignore_unverifyable_and_condition(self): def test_and_without_value(self): result = self.parser.parse( 'Required if Recorded Channel Sequence (3008,0130) is sent and ' - 'Brachy Treatment Type (300A,0202) is not MANUAL or PDR.') + 'Brachy Treatment Type (300A,0202) is not MANUAL or PDR.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) result1 = result.and_conditions[0] @@ -374,7 +429,8 @@ def test_and_with_multiple_values(self): result = self.parser.parse( 'Required if Image Type (0008,0008) Value 1 is ORIGINAL or MIXED ' 'and Respiratory Motion Compensation Technique ' - '(0018,9170) equals other than NONE.') + '(0018,9170) equals other than NONE.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) self.assertEqual('=', result.and_conditions[0].operator) @@ -387,7 +443,8 @@ def test_either_or_tag_presence(self): result = self.parser.parse( "Required if either Patient's Birth Date in Alternative Calendar " "(0010,0033) or Patient's Alternative Death Date in Calendar " - "(0010,0034) is present.") + "(0010,0034) is present." + ) self.assertEqual('MN', result.type) result1 = result.or_conditions[0] self.assertEqual('(0010,0033)', result1.tag) @@ -401,7 +458,8 @@ def test_multiple_tag_absence(self): 'Required if DICOM Media Retrieval Sequence (0040,E022), ' 'WADO Retrieval Sequence (0040,E023), WADO-RS Retrieval Sequence ' '(0040,E025) and XDS Retrieval Sequence ' - '(0040,E024) are not present. May be present otherwise.') + '(0040,E024) are not present. May be present otherwise.' + ) self.assertEqual('MU', result.type) self.assertEqual(4, len(result.and_conditions)) for result_part in result.and_conditions: @@ -412,7 +470,8 @@ def test_multiple_tag_absence_with_comma(self): 'Required if DICOM Retrieval Sequence (0040,E021), ' 'WADO Retrieval Sequence (0040,E023), ' 'and WADO-RS Retrieval Sequence (0040,E025) ' - 'and XDS Retrieval Sequence (0040,E024) are not present.') + 'and XDS Retrieval Sequence (0040,E024) are not present.' + ) self.assertEqual('MN', result.type) self.assertEqual(4, len(result.and_conditions)) for result_part in result.and_conditions: @@ -421,7 +480,8 @@ def test_multiple_tag_absence_with_comma(self): def test_multiple_tag_presence(self): result = self.parser.parse( 'Required if Selector Attribute (0072,0026) and ' - 'Filter-by Operator (0072,0406) are present.') + 'Filter-by Operator (0072,0406) are present.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) for result_part in result.and_conditions: @@ -430,7 +490,8 @@ def test_multiple_tag_presence(self): def test_mixed_and_or_tag_presence(self): result = self.parser.parse( 'Required if Selector Attribute (0072,0026) or Filter-by Category ' - '(0072,0402), and Filter-by Operator (0072,0406) are present.') + '(0072,0402), and Filter-by Operator (0072,0406) are present.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) self.assertEqual(2, len(result.and_conditions[0].or_conditions)) @@ -442,7 +503,8 @@ def test_multi_tag_in_second_condition(self): result = self.parser.parse( 'Required if Temporal Range Type (0040,A130) is present, ' 'and if Referenced Time Offsets (0040,A138) and ' - 'Referenced DateTime (0040,A13A) are not present.') + 'Referenced DateTime (0040,A13A) are not present.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) self.assertEqual(2, len(result.and_conditions[1].and_conditions)) @@ -453,7 +515,8 @@ def test_multi_tag_in_second_condition(self): def test_is_present_with_multiple_tags(self): result = self.parser.parse( 'Required if Bounding Box Top Left Hand Corner (0070,0010) ' - 'or Bounding Box Bottom Right Hand Corner (0070,0011) is present.') + 'or Bounding Box Bottom Right Hand Corner (0070,0011) is present.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.or_conditions)) for result_part in result.or_conditions: @@ -464,7 +527,8 @@ def test_multiple_tags_with_value(self): 'Required if the value of Image Box Layout Type ' '(0072,0304) is TILED, and the value of ' 'Image Box Tile Horizontal Dimension (0072,0306) or ' - 'Image Box Tile Vertical Dimension (0072,0308) is greater than 1.') + 'Image Box Tile Vertical Dimension (0072,0308) is greater than 1.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) self.assertEqual(2, len(result.and_conditions[1].or_conditions)) @@ -477,7 +541,8 @@ def test_ispresent_with_value(self): result = self.parser.parse( 'Required if Patient Identity Removed (0012,0062) is present and ' 'has a value of YES and De-identification Method Code Sequence ' - '(0012,0064) is not present.') + '(0012,0064) is not present.' + ) self.assertEqual('MN', result.type) self.assertEqual(2, len(result.and_conditions)) self.assertEqual('=', result.and_conditions[0].operator) @@ -496,14 +561,16 @@ def test_or_condition_with_space(self): result = self.parser.parse( '"Required if Photometric Interpretation ' '(0028,0004) has a value of PALETTE COLOR ' - 'or Pixel Presentation (0008,9205) equals COLOR or MIXED.') + 'or Pixel Presentation (0008,9205) equals COLOR or MIXED.' + ) self.check_or_condition(result) def test_or_condition_with_comma(self): result = self.parser.parse( '"Required if Photometric Interpretation ' '(0028,0004) has a value of PALETTE COLOR, ' - 'or Pixel Presentation (0008,9205) equals COLOR or MIXED.') + 'or Pixel Presentation (0008,9205) equals COLOR or MIXED.' + ) self.check_or_condition(result) @@ -513,7 +580,8 @@ def disabled_test_ispresent_with_value(self): 'Required if Graphic Data (0070,0022) is "closed", ' 'that is Graphic Type (0070,0023) is CIRCLE or ELLIPSE, ' 'or Graphic Type (0070,0023) is POLYLINE or INTERPOLATED ' - 'and the first data point is the same as the last data point.') + 'and the first data point is the same as the last data point.' + ) self.assertEqual('MN', result.type) self.assertEqual(3, len(result.or_conditions)) self.assertEqual('=', result.or_conditions[0].operator) diff --git a/setup.py b/setup.py index 1db1835..f3a12fc 100644 --- a/setup.py +++ b/setup.py @@ -36,7 +36,6 @@ "Development Status :: 3 - Alpha", "Environment :: Console", "Programming Language :: Python", - "Programming Language :: Python :: 3.6", "Programming Language :: Python :: 3.7", "Programming Language :: Python :: 3.8", "Programming Language :: Python :: 3.9",