prompt: Make PersonName3 iterable
  ```python
  from pydicom import Dataset
  
  ds = Dataset()
  ds.PatientName = 'SomeName'
  
  'S' in ds.PatientName
  ```
  ```
  Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
  TypeError: argument of type 'PersonName3' is not iterable
  ```
  
  I'm not really sure if this is intentional or if PN elements should support `str` methods. And yes I know I can `str(ds.PatientName)` but it's a bit silly, especially when I keep having to write exceptions to my element iterators just for PN elements.
  
instance: {"FAIL_TO_PASS": "[\"pydicom/tests/test_valuerep.py::TestPersonName::test_next\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_iterator\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_contains\"]", "PASS_TO_PASS": "[\"pydicom/tests/test_valuerep.py::TestTM::test_pickling\", \"pydicom/tests/test_valuerep.py::TestDT::test_pickling\", \"pydicom/tests/test_valuerep.py::TestDA::test_pickling\", \"pydicom/tests/test_valuerep.py::TestDS::test_empty_value\", \"pydicom/tests/test_valuerep.py::TestDS::test_float_values\", \"pydicom/tests/test_valuerep.py::TestDSfloat::test_pickling\", \"pydicom/tests/test_valuerep.py::TestDSfloat::test_str\", \"pydicom/tests/test_valuerep.py::TestDSfloat::test_repr\", \"pydicom/tests/test_valuerep.py::TestDSdecimal::test_pickling\", \"pydicom/tests/test_valuerep.py::TestDSdecimal::test_float_value\", \"pydicom/tests/test_valuerep.py::TestIS::test_empty_value\", \"pydicom/tests/test_valuerep.py::TestIS::test_valid_value\", \"pydicom/tests/test_valuerep.py::TestIS::test_invalid_value\", \"pydicom/tests/test_valuerep.py::TestIS::test_pickling\", \"pydicom/tests/test_valuerep.py::TestIS::test_longint\", \"pydicom/tests/test_valuerep.py::TestIS::test_overflow\", \"pydicom/tests/test_valuerep.py::TestIS::test_str\", \"pydicom/tests/test_valuerep.py::TestIS::test_repr\", \"pydicom/tests/test_valuerep.py::TestBadValueRead::test_read_bad_value_in_VR_default\", \"pydicom/tests/test_valuerep.py::TestBadValueRead::test_read_bad_value_in_VR_enforce_valid_value\", \"pydicom/tests/test_valuerep.py::TestDecimalString::test_DS_decimal_set\", \"pydicom/tests/test_valuerep.py::TestDecimalString::test_valid_decimal_strings\", \"pydicom/tests/test_valuerep.py::TestDecimalString::test_invalid_decimal_strings\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_last_first\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_copy\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_three_component\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_formatting\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_unicode_kr\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_unicode_jp_from_bytes\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_unicode_jp_from_bytes_comp_delimiter\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_unicode_jp_from_bytes_caret_delimiter\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_unicode_jp_from_unicode\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_not_equal\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_encoding_carried\", \"pydicom/tests/test_valuerep.py::TestPersonName::test_hash\", \"pydicom/tests/test_valuerep.py::TestDateTime::test_date\", \"pydicom/tests/test_valuerep.py::TestDateTime::test_date_time\", \"pydicom/tests/test_valuerep.py::TestDateTime::test_time\"]", "base_commit": "b9fb05c177b685bf683f7f57b2d57374eb7d882d", "created_at": "2020-06-26T11:47:17Z", "environment_setup_commit": "9d69811e539774f296c2f289839147e741251716", "hints_text": "I think it is reasonable to support at least some `str` methods (definitely `__contains__` for the example above), but there are many that don't make a lot of sense in this context though - e.g. `join`, `ljust`, `maketrans`, `splitlines` just to name a few, but I suppose each would either never be actually used or would have no effect.\r\n\r\nI have a vague memory that one or more of the `PersonName` classes was at one time subclassed from `str`, or at least that it was discussed... does anyone remember?  Maybe it would be easier now with only Python 3 supported.\n`PersonName` was derived from `str` or `unicode` in Python 2, but that caused a number of problems, which is why you switched to `PersonName3` in Python 3, I think. I agree though that it makes sense to implement `str` methods, either by implementing some of them, or generically by adding `__getattr__` that converts it to `str` and applies the attribute to that string. ", "instance_id": "pydicom__pydicom-1139", "patch": "diff --git a/pydicom/valuerep.py b/pydicom/valuerep.py\n--- a/pydicom/valuerep.py\n+++ b/pydicom/valuerep.py\n@@ -1,6 +1,5 @@\n # Copyright 2008-2018 pydicom authors. See LICENSE file for details.\n \"\"\"Special classes for DICOM value representations (VR)\"\"\"\n-from copy import deepcopy\n from decimal import Decimal\n import re\n \n@@ -750,6 +749,25 @@ def __ne__(self, other):\n     def __str__(self):\n         return '='.join(self.components).__str__()\n \n+    def __next__(self):\n+        # Get next character or stop iteration\n+        if self._i < self._rep_len:\n+            c = self._str_rep[self._i]\n+            self._i += 1\n+            return c\n+        else:\n+            raise StopIteration\n+\n+    def __iter__(self):\n+        # Get string rep. and length, initialize index counter\n+        self._str_rep = self.__str__()\n+        self._rep_len = len(self._str_rep)\n+        self._i = 0\n+        return self\n+\n+    def __contains__(self, x):\n+        return x in self.__str__()\n+\n     def __repr__(self):\n         return '='.join(self.components).__repr__()\n \n", "problem_statement": "Make PersonName3 iterable\n```python\r\nfrom pydicom import Dataset\r\n\r\nds = Dataset()\r\nds.PatientName = 'SomeName'\r\n\r\n'S' in ds.PatientName\r\n```\r\n```\r\nTraceback (most recent call last):\r\n  File \"<stdin>\", line 1, in <module>\r\nTypeError: argument of type 'PersonName3' is not iterable\r\n```\r\n\r\nI'm not really sure if this is intentional or if PN elements should support `str` methods. And yes I know I can `str(ds.PatientName)` but it's a bit silly, especially when I keep having to write exceptions to my element iterators just for PN elements.\n", "repo": "pydicom/pydicom", "test_patch": "diff --git a/pydicom/tests/test_valuerep.py b/pydicom/tests/test_valuerep.py\n--- a/pydicom/tests/test_valuerep.py\n+++ b/pydicom/tests/test_valuerep.py\n@@ -427,6 +427,62 @@ def test_hash(self):\n         )\n         assert hash(pn1) == hash(pn2)\n \n+    def test_next(self):\n+        \"\"\"Test that the next function works on it's own\"\"\"\n+        # Test getting the first character\n+        pn1 = PersonName(\"John^Doe^^Dr\", encodings=default_encoding)\n+        pn1_itr = iter(pn1)\n+        assert next(pn1_itr) == \"J\"\n+\n+        # Test getting multiple characters\n+        pn2 = PersonName(\n+            \"Yamada^Tarou=\u5c71\u7530^\u592a\u90ce=\u3084\u307e\u3060^\u305f\u308d\u3046\", [default_encoding, \"iso2022_jp\"]\n+        )\n+        pn2_itr = iter(pn2)\n+        assert next(pn2_itr) == \"Y\"\n+        assert next(pn2_itr) == \"a\"\n+\n+        # Test getting all characters\n+        pn3 = PersonName(\"SomeName\")\n+        pn3_itr = iter(pn3)\n+        assert next(pn3_itr) == \"S\"\n+        assert next(pn3_itr) == \"o\"\n+        assert next(pn3_itr) == \"m\"\n+        assert next(pn3_itr) == \"e\"\n+        assert next(pn3_itr) == \"N\"\n+        assert next(pn3_itr) == \"a\"\n+        assert next(pn3_itr) == \"m\"\n+        assert next(pn3_itr) == \"e\"\n+\n+        # Attempting to get next characeter should stop the iteration\n+        # I.e. next can only start once\n+        with pytest.raises(StopIteration):\n+            next(pn3_itr)\n+\n+        # Test that next() doesn't work without instantiating an iterator\n+        pn4 = PersonName(\"SomeName\")\n+        with pytest.raises(AttributeError):\n+            next(pn4)\n+\n+    def test_iterator(self):\n+        \"\"\"Test that iterators can be corretly constructed\"\"\"\n+        name_str = \"John^Doe^^Dr\"\n+        pn1 = PersonName(name_str)\n+        \n+        for i, c in enumerate(pn1):\n+            assert name_str[i] == c\n+\n+        # Ensure that multiple iterators can be created on the same variable\n+        for i, c in enumerate(pn1):\n+            assert name_str[i] == c\n+\n+    def test_contains(self):\n+        \"\"\"Test that characters can be check if they are within the name\"\"\"\n+        pn1 = PersonName(\"John^Doe\")\n+        assert (\"J\" in pn1) == True\n+        assert (\"o\" in pn1) == True\n+        assert (\"x\" in pn1) == False\n+\n \n class TestDateTime:\n     \"\"\"Unit tests for DA, DT, TM conversion to datetime objects\"\"\"\n", "version": "2.0"}
parsed_commit_content: {"file_diffs":[{"old_file_content":"# Copyright 2008-2018 pydicom authors. See LICENSE file for details.\n\"\"\"Special classes for DICOM value representations (VR)\"\"\"\nfrom copy import deepcopy\nfrom decimal import Decimal\nimport re\n\nfrom datetime import (date, datetime, time, timedelta, timezone)\n\n# don't import datetime_conversion directly\nfrom pydicom import config\nfrom pydicom.multival import MultiValue\n\n# can't import from charset or get circular import\ndefault_encoding = \"iso8859\"\n\n# For reading/writing data elements,\n# these ones have longer explicit VR format\n# Taken from PS3.5 Section 7.1.2\nextra_length_VRs = ('OB', 'OD', 'OF', 'OL', 'OW', 'SQ', 'UC', 'UN', 'UR', 'UT')\n\n# VRs that can be affected by character repertoire\n# in (0008,0005) Specific Character Set\n# See PS-3.5 (2011), section 6.1.2 Graphic Characters\n# and PN, but it is handled separately.\ntext_VRs = ('SH', 'LO', 'ST', 'LT', 'UC', 'UT')\n\n# Delimiters for text strings and person name that reset the encoding.\n# See PS3.5, Section 6.1.2.5.3\n# Note: We use character codes for Python 3\n# because those are the types yielded if iterating over a byte string.\n\n# Characters/Character codes for text VR delimiters: LF, CR, TAB, FF\nTEXT_VR_DELIMS = {0x0d, 0x0a, 0x09, 0x0c}\n\n# Character/Character code for PN delimiter: name part separator '^'\n# (the component separator '=' is handled separately)\nPN_DELIMS = {0xe5}\n\n\nclass DA(date):\n    \"\"\"Store value for an element with VR **DA** as :class:`datetime.date`.\n\n    Note that the :class:`datetime.date` base class is immutable.\n    \"\"\"\n    __slots__ = ['original_string']\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __reduce__(self):\n        return super(DA, self).__reduce__() + (self.__getstate__(),)\n\n    def __reduce_ex__(self, protocol):\n        return super(DA, self).__reduce__() + (self.__getstate__(),)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of DA object.\n\n        Raise an exception if the string cannot be parsed or the argument\n        is otherwise incompatible.\n\n        Parameters\n        ----------\n        val : str\n            A string conformant to the DA definition in the DICOM Standard,\n            Part 5, :dcm:`Table 6.2-1<part05/sect_6.2.html#table_6.2-1>`.\n        \"\"\"\n        if isinstance(val, str):\n            if len(val) == 8:\n                year = int(val[0:4])\n                month = int(val[4:6])\n                day = int(val[6:8])\n                val = super(DA, cls).__new__(cls, year, month, day)\n            elif len(val) == 10 and val[4] == '.' and val[7] == '.':\n                # ACR-NEMA Standard 300, predecessor to DICOM\n                # for compatibility with a few old pydicom example files\n                year = int(val[0:4])\n                month = int(val[5:7])\n                day = int(val[8:10])\n                val = super(DA, cls).__new__(cls, year, month, day)\n            elif val == '':\n                val = None  # empty date\n            else:\n                try:\n                    val = super(DA, cls).__new__(cls, val)\n                except TypeError:\n                    raise ValueError(\"Cannot convert to datetime: '%s'\" %\n                                     (val))\n        elif isinstance(val, date):\n            val = super(DA, cls).__new__(cls, val.year, val.month, val.day)\n        else:\n            val = super(DA, cls).__new__(cls, val)\n        return val\n\n    def __init__(self, val):\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, DA) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n        else:\n            return super(DA, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\nclass DT(datetime):\n    \"\"\"Store value for an element with VR **DT** as :class:`datetime.datetime`.\n\n    Note that the :class:`datetime.datetime` base class is immutable.\n    \"\"\"\n    __slots__ = ['original_string']\n    _regex_dt = re.compile(r\"((\\d{4,14})(\\.(\\d{1,6}))?)([+-]\\d{4})?\")\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __reduce__(self):\n        return super(DT, self).__reduce__() + (self.__getstate__(),)\n\n    def __reduce_ex__(self, protocol):\n        return super(DT, self).__reduce__() + (self.__getstate__(),)\n\n    @staticmethod\n    def _utc_offset(offset, name):\n        return timezone(timedelta(seconds=offset), name)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of DT object.\n\n        Raise an exception if the string cannot be parsed or the argument\n        is otherwise incompatible.\n\n        Parameters\n        ----------\n        val : str\n            A string conformant to the DT definition in the DICOM Standard,\n            Part 5, :dcm:`Table 6.2-1<part05/sect_6.2.html#table_6.2-1>`.\n        \"\"\"\n        if isinstance(val, str):\n            match = DT._regex_dt.match(val)\n            if match and len(val) <= 26:\n                dt_match = match.group(2)\n                year = int(dt_match[0:4])\n                if len(dt_match) < 6:\n                    month = 1\n                else:\n                    month = int(dt_match[4:6])\n                if len(dt_match) < 8:\n                    day = 1\n                else:\n                    day = int(dt_match[6:8])\n                if len(dt_match) < 10:\n                    hour = 0\n                else:\n                    hour = int(dt_match[8:10])\n                if len(dt_match) < 12:\n                    minute = 0\n                else:\n                    minute = int(dt_match[10:12])\n                if len(dt_match) < 14:\n                    second = 0\n                    microsecond = 0\n                else:\n                    second = int(dt_match[12:14])\n                    ms_match = match.group(4)\n                    if ms_match:\n                        microsecond = int(ms_match.rstrip().ljust(6, '0'))\n                    else:\n                        microsecond = 0\n                tz_match = match.group(5)\n                if tz_match:\n                    offset1 = int(tz_match[1:3]) * 60\n                    offset2 = int(tz_match[3:5])\n                    offset = (offset1 + offset2) * 60\n                    if tz_match[0] == '-':\n                        offset = -offset\n                    tzinfo = cls._utc_offset(offset, tz_match)\n                else:\n                    tzinfo = None\n                val = super(DT,\n                            cls).__new__(cls, year, month, day, hour, minute,\n                                         second, microsecond, tzinfo)\n            else:\n                try:\n                    val = super(DT, cls).__new__(cls, val)\n                except TypeError:\n                    raise ValueError(\"Cannot convert to datetime: '%s'\" %\n                                     (val))\n        elif isinstance(val, datetime):\n            val = super(DT, cls).__new__(cls, val.year, val.month, val.day,\n                                         val.hour, val.minute, val.second,\n                                         val.microsecond, val.tzinfo)\n        else:\n            val = super(DT, cls).__new__(cls, val)\n        return val\n\n    def __init__(self, val):\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, DT) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n        else:\n            return super(DT, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\nclass TM(time):\n    \"\"\"Store value for an element with VR **TM** as :class:`datetime.time`.\n\n    Note that the :class:`datetime.time` base class is immutable.\n    \"\"\"\n    __slots__ = ['original_string']\n    _regex_tm = re.compile(r\"(\\d{2,6})(\\.(\\d{1,6}))?\")\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __reduce__(self):\n        return super(TM, self).__reduce__() + (self.__getstate__(),)\n\n    def __reduce_ex__(self, protocol):\n        return super(TM, self).__reduce__() + (self.__getstate__(),)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of TM object from a string.\n\n        Raise an exception if the string cannot be parsed or the argument\n        is otherwise incompatible.\n\n        Parameters\n        ----------\n        val : str\n            A string conformant to the TM definition in the DICOM Standard,\n            Part 5, :dcm:`Table 6.2-1<part05/sect_6.2.html#table_6.2-1>`.\n        \"\"\"\n        if isinstance(val, str):\n            match = TM._regex_tm.match(val)\n            if match and len(val) <= 16:\n                tm_match = match.group(1)\n                hour = int(tm_match[0:2])\n                if len(tm_match) < 4:\n                    minute = 0\n                else:\n                    minute = int(tm_match[2:4])\n                if len(tm_match) < 6:\n                    second = 0\n                    microsecond = 0\n                else:\n                    second = int(tm_match[4:6])\n                    ms_match = match.group(3)\n                    if ms_match:\n                        microsecond = int(ms_match.rstrip().ljust(6, '0'))\n                    else:\n                        microsecond = 0\n                val = super(TM, cls).__new__(cls, hour, minute, second,\n                                             microsecond)\n            elif val == '':\n                val = None  # empty time\n            else:\n                try:\n                    val = super(TM, cls).__new__(cls, val)\n                except TypeError:\n                    raise ValueError(\"Cannot convert to datetime: '%s\" % (val))\n        elif isinstance(val, time):\n            val = super(TM, cls).__new__(cls, val.hour, val.minute, val.second,\n                                         val.microsecond)\n        else:\n            val = super(TM, cls).__new__(cls, val)\n        return val\n\n    def __init__(self, val):\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, TM) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n        else:\n            return super(TM, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\nclass DSfloat(float):\n    \"\"\"Store value for an element with VR **DS** as :class:`float`.\n\n    If constructed from an empty string, return the empty string,\n    not an instance of this class.\n\n    \"\"\"\n    __slots__ = ['original_string']\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __init__(self, val):\n        \"\"\"Store the original string if one given, for exact write-out of same\n        value later.\n        \"\"\"\n        # ... also if user changes a data element value, then will get\n        # a different object, because float is immutable.\n\n        has_attribute = hasattr(val, 'original_string')\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, (DSfloat, DSdecimal)) and has_attribute:\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n\n        # Issue #937 (Python 3.8 compatibility)\n        return repr(self)[1:-1]\n\n    def __repr__(self):\n        return '\"{}\"'.format(super(DSfloat, self).__repr__())\n\n\nclass DSdecimal(Decimal):\n    \"\"\"Store value for an element with VR **DS** as :class:`decimal.Decimal`.\n\n    Notes\n    -----\n    If constructed from an empty string, returns the empty string, not an\n    instance of this class.\n    \"\"\"\n    __slots__ = ['original_string']\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of DS object, or return a blank string if one is\n        passed in, e.g. from a type 2 DICOM blank value.\n\n        Parameters\n        ----------\n        val : str or numeric\n            A string or a number type which can be converted to a decimal.\n        \"\"\"\n        # Store this value here so that if the input string is actually a valid\n        # string but decimal.Decimal transforms it to an invalid string it will\n        # still be initialized properly\n        enforce_length = config.enforce_valid_values\n        # DICOM allows spaces around the string,\n        # but python doesn't, so clean it\n        if isinstance(val, str):\n            val = val.strip()\n            # If the input string is actually invalid that we relax the valid\n            # value constraint for this particular instance\n            if len(val) <= 16:\n                enforce_length = False\n        if val == '':\n            return val\n        if isinstance(val, float) and not config.allow_DS_float:\n            msg = (\"DS cannot be instantiated with a float value, \"\n                   \"unless config.allow_DS_float is set to True. \"\n                   \"It is recommended to convert to a string instead, \"\n                   \"with the desired number of digits, or use \"\n                   \"Decimal.quantize and pass a Decimal instance.\")\n            raise TypeError(msg)\n        if not isinstance(val, Decimal):\n            val = super(DSdecimal, cls).__new__(cls, val)\n        if len(str(val)) > 16 and enforce_length:\n            msg = (\"DS value representation must be <= 16 \"\n                   \"characters by DICOM standard. Initialize with \"\n                   \"a smaller string, or set config.enforce_valid_values \"\n                   \"to False to override, or use Decimal.quantize() and \"\n                   \"initialize with a Decimal instance.\")\n            raise OverflowError(msg)\n        return val\n\n    def __init__(self, val):\n        \"\"\"Store the original string if one given, for exact write-out of same\n        value later. E.g. if set ``'1.23e2'``, :class:`~decimal.Decimal` would\n        write ``'123'``, but :class:`DS` will use the original.\n        \"\"\"\n        # ... also if user changes a data element value, then will get\n        # a different Decimal, as Decimal is immutable.\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, (DSfloat, DSdecimal)) and hasattr(val, 'original_string'):  # noqa\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string') and len(self.original_string) <= 16:  # noqa\n            return self.original_string\n        else:\n            return super(DSdecimal, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\n# CHOOSE TYPE OF DS\nif config.use_DS_decimal:\n    DSclass = DSdecimal\nelse:\n    DSclass = DSfloat\n\n\ndef DS(val):\n    \"\"\"Factory function for creating DS class instances.\n\n    Checks for blank string; if so, returns that, else calls :class:`DSfloat`\n    or :class:`DSdecimal` to create the class instance. This avoids overriding\n    ``DSfloat.__new__()`` (which carries a time penalty for large arrays of\n    DS).\n\n    Similarly the string clean and check can be avoided and :class:`DSfloat`\n    called directly if a string has already been processed.\n    \"\"\"\n    if isinstance(val, str):\n        val = val.strip()\n    if val == '' or val is None:\n        return val\n    return DSclass(val)\n\n\nclass IS(int):\n    \"\"\"Store value for an element with VR **IS** as :class:`int`.\n\n    Stores original integer string for exact rewriting of the string\n    originally read or stored.\n    \"\"\"\n\n    def __new__(cls, val):\n        \"\"\"Create instance if new integer string\"\"\"\n        if val is None:\n            return val\n        if isinstance(val, str) and val.strip() == '':\n            return ''\n\n        newval = super(IS, cls).__new__(cls, val)\n\n        # check if a float or Decimal passed in, then could have lost info,\n        # and will raise error. E.g. IS(Decimal('1')) is ok, but not IS(1.23)\n        if isinstance(val, (float, Decimal)) and newval != val:\n            raise TypeError(\"Could not convert value to integer without loss\")\n        # Checks in case underlying int is >32 bits, DICOM does not allow this\n        check_newval = (newval < -2 ** 31 or newval >= 2 ** 31)\n        if check_newval and config.enforce_valid_values:\n            dcm_limit = \"-2**31 to (2**31 - 1) for IS\"\n            message = \"Value exceeds DICOM limits of %s\" % (dcm_limit)\n            raise OverflowError(message)\n        return newval\n\n    def __init__(self, val):\n        # If a string passed, then store it\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, IS) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n\n        # Issue #937 (Python 3.8 compatibility)\n        return repr(self)[1:-1]\n\n    def __repr__(self):\n        return '\"{}\"'.format(super(IS, self).__repr__())\n\n\ndef MultiString(val, valtype=str):\n    \"\"\"Split a bytestring by delimiters if there are any\n\n    Parameters\n    ----------\n    val : bytes or str\n        DICOM byte string to split up.\n    valtype\n        Default :class:`str`, but can be e.g. :class:`~pydicom.uid.UID` to\n        overwrite to a specific type.\n\n    Returns\n    -------\n    valtype or list of valtype\n        The split value as `valtype` or a :class:`list` of `valtype`.\n    \"\"\"\n    # Remove trailing blank used to pad to even length\n    # 2005.05.25: also check for trailing 0, error made\n    # in PET files we are converting\n\n    while val and (val.endswith(' ') or val.endswith('\\x00')):\n        val = val[:-1]\n    splitup = val.split(\"\\\\\")\n\n    if len(splitup) == 1:\n        val = splitup[0]\n        return valtype(val) if val else val\n    else:\n        return MultiValue(valtype, splitup)\n\n\ndef _verify_encodings(encodings):\n    \"\"\"Checks the encoding to ensure proper format\"\"\"\n    if encodings is not None:\n        if not isinstance(encodings, (list, tuple)):\n            return encodings,\n        return tuple(encodings)\n    return encodings\n\n\ndef _decode_personname(components, encodings):\n    \"\"\"Return a list of decoded person name components.\n\n    Parameters\n    ----------\n    components : list of byte string\n        The list of the up to three encoded person name components\n    encodings : list of str\n        The Python encodings uses to decode `components`.\n\n    Returns\n    -------\n    text type\n        The unicode string representing the person name.\n        If the decoding of some component parts is not possible using the\n        given encodings, they are decoded with the first encoding using\n        replacement characters for bytes that cannot be decoded.\n    \"\"\"\n    from pydicom.charset import decode_string\n\n    if isinstance(components[0], str):\n        comps = components\n    else:\n        comps = [decode_string(comp, encodings, PN_DELIMS)\n                 for comp in components]\n    # Remove empty elements from the end to avoid trailing '='\n    while len(comps) and not comps[-1]:\n        comps.pop()\n    return tuple(comps)\n\n\ndef _encode_personname(components, encodings):\n    \"\"\"Encode a list of text string person name components.\n\n    Parameters\n    ----------\n    components : list of text type\n        The list of the up to three unicode person name components\n    encodings : list of str\n        The Python encodings uses to encode `components`.\n\n    Returns\n    -------\n    byte string\n        The byte string that can be written as a PN DICOM tag value.\n        If the encoding of some component parts is not possible using the\n        given encodings, they are encoded with the first encoding using\n        replacement bytes for characters that cannot be encoded.\n    \"\"\"\n    from pydicom.charset import encode_string\n\n    encoded_comps = []\n    for comp in components:\n        groups = [encode_string(group, encodings)\n                  for group in comp.split('^')]\n        encoded_comps.append(b'^'.join(groups))\n\n    # Remove empty elements from the end\n    while len(encoded_comps) and not encoded_comps[-1]:\n        encoded_comps.pop()\n    return b'='.join(encoded_comps)\n\n\nclass PersonName:\n    def __new__(cls, *args, **kwargs):\n        # Handle None value by returning None instead of a PersonName object\n        if len(args) and args[0] is None:\n            return None\n        return super(PersonName, cls).__new__(cls)\n\n    def __init__(self, val, encodings=None, original_string=None):\n        if isinstance(val, PersonName):\n            encodings = val.encodings\n            self.original_string = val.original_string\n            self._components = tuple(str(val).split('='))\n        elif isinstance(val, bytes):\n            # this is the raw byte string - decode it on demand\n            self.original_string = val\n            self._components = None\n        else:\n            # handle None `val` as empty string\n            val = val or ''\n\n            # this is the decoded string - save the original string if\n            # available for easier writing back\n            self.original_string = original_string\n            components = val.split('=')\n            # Remove empty elements from the end to avoid trailing '='\n            while len(components) and not components[-1]:\n                components.pop()\n            self._components = tuple(components)\n\n            # if the encoding is not given, leave it as undefined (None)\n        self.encodings = _verify_encodings(encodings)\n        self._dict = {}\n\n    def _create_dict(self):\n        \"\"\"Creates a dictionary of person name group and component names.\n\n        Used exclusively for `formatted` for backwards compatibility.\n        \"\"\"\n        if not self._dict:\n            for name in ('family_name', 'given_name', 'middle_name',\n                         'name_prefix', 'name_suffix',\n                         'ideographic', 'phonetic'):\n                self._dict[name] = getattr(self, name, '')\n\n    @property\n    def components(self):\n        \"\"\"Returns up to three decoded person name components.\n\n        .. versionadded:: 1.2\n\n        The returned components represent the alphabetic, ideographic and\n        phonetic representations as a list of unicode strings.\n        \"\"\"\n        if self._components is None:\n            groups = self.original_string.split(b'=')\n            encodings = self.encodings or [default_encoding]\n            self._components = _decode_personname(groups, encodings)\n\n        return self._components\n\n    def _name_part(self, i):\n        try:\n            return self.components[0].split('^')[i]\n        except IndexError:\n            return ''\n\n    @property\n    def family_name(self):\n        \"\"\"Return the first (family name) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(0)\n\n    @property\n    def given_name(self):\n        \"\"\"Return the second (given name) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(1)\n\n    @property\n    def middle_name(self):\n        \"\"\"Return the third (middle name) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(2)\n\n    @property\n    def name_prefix(self):\n        \"\"\"Return the fourth (name prefix) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(3)\n\n    @property\n    def name_suffix(self):\n        \"\"\"Return the fifth (name suffix) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(4)\n\n    @property\n    def ideographic(self):\n        \"\"\"Return the second (ideographic) person name component as a\n        unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        try:\n            return self.components[1]\n        except IndexError:\n            return ''\n\n    @property\n    def phonetic(self):\n        \"\"\"Return the third (phonetic) person name component as a\n        unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        try:\n            return self.components[2]\n        except IndexError:\n            return ''\n\n    def __eq__(self, other):\n        return str(self) == other\n\n    def __ne__(self, other):\n        return not self == other\n\n    def __str__(self):\n        return '='.join(self.components).__str__()\n\n    def __repr__(self):\n        return '='.join(self.components).__repr__()\n\n    def __hash__(self):\n        return hash(self.components)\n\n    def decode(self, encodings=None):\n        \"\"\"Return the patient name decoded by the given `encodings`.\n\n        Parameters\n        ----------\n        encodings : list of str\n            The list of encodings used for decoding the byte string. If not\n            given, the initial encodings set in the object are used.\n\n        Returns\n        -------\n        valuerep.PersonName\n            A person name object that will return the decoded string with\n            the given encodings on demand. If the encodings are not given,\n            the current object is returned.\n        \"\"\"\n        # in the common case (encoding did not change) we decode on demand\n        if encodings is None or encodings == self.encodings:\n            return self\n        # the encoding was unknown or incorrect - create a new\n        # PersonName object with the changed encoding\n        encodings = _verify_encodings(encodings)\n        if self.original_string is None:\n            # if the original encoding was not set, we set it now\n            self.original_string = _encode_personname(\n                self.components, self.encodings or [default_encoding])\n        return PersonName(self.original_string, encodings)\n\n    def encode(self, encodings=None):\n        \"\"\"Return the patient name decoded by the given `encodings`.\n\n        Parameters\n        ----------\n        encodings : list of str\n            The list of encodings used for encoding the unicode string. If\n            not given, the initial encodings set in the object are used.\n\n        Returns\n        -------\n        bytes\n            The person name encoded with the given encodings as a byte string.\n            If no encoding is given, the original byte string is returned, if\n            available, otherwise each group of the patient name is encoded\n            with the first matching of the given encodings.\n        \"\"\"\n        encodings = _verify_encodings(encodings) or self.encodings\n\n        # if the encoding is not the original encoding, we have to return\n        # a re-encoded string (without updating the original string)\n        if encodings != self.encodings and self.encodings is not None:\n            return _encode_personname(self.components, encodings)\n        if self.original_string is None:\n            # if the original encoding was not set, we set it now\n            self.original_string = _encode_personname(\n                self.components, encodings or [default_encoding])\n        return self.original_string\n\n    def family_comma_given(self):\n        return self.formatted('%(family_name)s, %(given_name)s')\n\n    def formatted(self, format_str):\n        self._create_dict()\n        return format_str % self._dict\n\n    def __bool__(self):\n        if self.original_string is None:\n            return (bool(self._components) and\n                    (len(self._components) > 1 or bool(self._components[0])))\n        return bool(self.original_string)\n\n\n# Alias old class names for backwards compat in user code\nPersonNameUnicode = PersonName = PersonName\n","new_file_content":"# Copyright 2008-2018 pydicom authors. See LICENSE file for details.\n\"\"\"Special classes for DICOM value representations (VR)\"\"\"\nfrom decimal import Decimal\nimport re\n\nfrom datetime import (date, datetime, time, timedelta, timezone)\n\n# don't import datetime_conversion directly\nfrom pydicom import config\nfrom pydicom.multival import MultiValue\n\n# can't import from charset or get circular import\ndefault_encoding = \"iso8859\"\n\n# For reading/writing data elements,\n# these ones have longer explicit VR format\n# Taken from PS3.5 Section 7.1.2\nextra_length_VRs = ('OB', 'OD', 'OF', 'OL', 'OW', 'SQ', 'UC', 'UN', 'UR', 'UT')\n\n# VRs that can be affected by character repertoire\n# in (0008,0005) Specific Character Set\n# See PS-3.5 (2011), section 6.1.2 Graphic Characters\n# and PN, but it is handled separately.\ntext_VRs = ('SH', 'LO', 'ST', 'LT', 'UC', 'UT')\n\n# Delimiters for text strings and person name that reset the encoding.\n# See PS3.5, Section 6.1.2.5.3\n# Note: We use character codes for Python 3\n# because those are the types yielded if iterating over a byte string.\n\n# Characters/Character codes for text VR delimiters: LF, CR, TAB, FF\nTEXT_VR_DELIMS = {0x0d, 0x0a, 0x09, 0x0c}\n\n# Character/Character code for PN delimiter: name part separator '^'\n# (the component separator '=' is handled separately)\nPN_DELIMS = {0xe5}\n\n\nclass DA(date):\n    \"\"\"Store value for an element with VR **DA** as :class:`datetime.date`.\n\n    Note that the :class:`datetime.date` base class is immutable.\n    \"\"\"\n    __slots__ = ['original_string']\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __reduce__(self):\n        return super(DA, self).__reduce__() + (self.__getstate__(),)\n\n    def __reduce_ex__(self, protocol):\n        return super(DA, self).__reduce__() + (self.__getstate__(),)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of DA object.\n\n        Raise an exception if the string cannot be parsed or the argument\n        is otherwise incompatible.\n\n        Parameters\n        ----------\n        val : str\n            A string conformant to the DA definition in the DICOM Standard,\n            Part 5, :dcm:`Table 6.2-1<part05/sect_6.2.html#table_6.2-1>`.\n        \"\"\"\n        if isinstance(val, str):\n            if len(val) == 8:\n                year = int(val[0:4])\n                month = int(val[4:6])\n                day = int(val[6:8])\n                val = super(DA, cls).__new__(cls, year, month, day)\n            elif len(val) == 10 and val[4] == '.' and val[7] == '.':\n                # ACR-NEMA Standard 300, predecessor to DICOM\n                # for compatibility with a few old pydicom example files\n                year = int(val[0:4])\n                month = int(val[5:7])\n                day = int(val[8:10])\n                val = super(DA, cls).__new__(cls, year, month, day)\n            elif val == '':\n                val = None  # empty date\n            else:\n                try:\n                    val = super(DA, cls).__new__(cls, val)\n                except TypeError:\n                    raise ValueError(\"Cannot convert to datetime: '%s'\" %\n                                     (val))\n        elif isinstance(val, date):\n            val = super(DA, cls).__new__(cls, val.year, val.month, val.day)\n        else:\n            val = super(DA, cls).__new__(cls, val)\n        return val\n\n    def __init__(self, val):\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, DA) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n        else:\n            return super(DA, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\nclass DT(datetime):\n    \"\"\"Store value for an element with VR **DT** as :class:`datetime.datetime`.\n\n    Note that the :class:`datetime.datetime` base class is immutable.\n    \"\"\"\n    __slots__ = ['original_string']\n    _regex_dt = re.compile(r\"((\\d{4,14})(\\.(\\d{1,6}))?)([+-]\\d{4})?\")\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __reduce__(self):\n        return super(DT, self).__reduce__() + (self.__getstate__(),)\n\n    def __reduce_ex__(self, protocol):\n        return super(DT, self).__reduce__() + (self.__getstate__(),)\n\n    @staticmethod\n    def _utc_offset(offset, name):\n        return timezone(timedelta(seconds=offset), name)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of DT object.\n\n        Raise an exception if the string cannot be parsed or the argument\n        is otherwise incompatible.\n\n        Parameters\n        ----------\n        val : str\n            A string conformant to the DT definition in the DICOM Standard,\n            Part 5, :dcm:`Table 6.2-1<part05/sect_6.2.html#table_6.2-1>`.\n        \"\"\"\n        if isinstance(val, str):\n            match = DT._regex_dt.match(val)\n            if match and len(val) <= 26:\n                dt_match = match.group(2)\n                year = int(dt_match[0:4])\n                if len(dt_match) < 6:\n                    month = 1\n                else:\n                    month = int(dt_match[4:6])\n                if len(dt_match) < 8:\n                    day = 1\n                else:\n                    day = int(dt_match[6:8])\n                if len(dt_match) < 10:\n                    hour = 0\n                else:\n                    hour = int(dt_match[8:10])\n                if len(dt_match) < 12:\n                    minute = 0\n                else:\n                    minute = int(dt_match[10:12])\n                if len(dt_match) < 14:\n                    second = 0\n                    microsecond = 0\n                else:\n                    second = int(dt_match[12:14])\n                    ms_match = match.group(4)\n                    if ms_match:\n                        microsecond = int(ms_match.rstrip().ljust(6, '0'))\n                    else:\n                        microsecond = 0\n                tz_match = match.group(5)\n                if tz_match:\n                    offset1 = int(tz_match[1:3]) * 60\n                    offset2 = int(tz_match[3:5])\n                    offset = (offset1 + offset2) * 60\n                    if tz_match[0] == '-':\n                        offset = -offset\n                    tzinfo = cls._utc_offset(offset, tz_match)\n                else:\n                    tzinfo = None\n                val = super(DT,\n                            cls).__new__(cls, year, month, day, hour, minute,\n                                         second, microsecond, tzinfo)\n            else:\n                try:\n                    val = super(DT, cls).__new__(cls, val)\n                except TypeError:\n                    raise ValueError(\"Cannot convert to datetime: '%s'\" %\n                                     (val))\n        elif isinstance(val, datetime):\n            val = super(DT, cls).__new__(cls, val.year, val.month, val.day,\n                                         val.hour, val.minute, val.second,\n                                         val.microsecond, val.tzinfo)\n        else:\n            val = super(DT, cls).__new__(cls, val)\n        return val\n\n    def __init__(self, val):\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, DT) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n        else:\n            return super(DT, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\nclass TM(time):\n    \"\"\"Store value for an element with VR **TM** as :class:`datetime.time`.\n\n    Note that the :class:`datetime.time` base class is immutable.\n    \"\"\"\n    __slots__ = ['original_string']\n    _regex_tm = re.compile(r\"(\\d{2,6})(\\.(\\d{1,6}))?\")\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __reduce__(self):\n        return super(TM, self).__reduce__() + (self.__getstate__(),)\n\n    def __reduce_ex__(self, protocol):\n        return super(TM, self).__reduce__() + (self.__getstate__(),)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of TM object from a string.\n\n        Raise an exception if the string cannot be parsed or the argument\n        is otherwise incompatible.\n\n        Parameters\n        ----------\n        val : str\n            A string conformant to the TM definition in the DICOM Standard,\n            Part 5, :dcm:`Table 6.2-1<part05/sect_6.2.html#table_6.2-1>`.\n        \"\"\"\n        if isinstance(val, str):\n            match = TM._regex_tm.match(val)\n            if match and len(val) <= 16:\n                tm_match = match.group(1)\n                hour = int(tm_match[0:2])\n                if len(tm_match) < 4:\n                    minute = 0\n                else:\n                    minute = int(tm_match[2:4])\n                if len(tm_match) < 6:\n                    second = 0\n                    microsecond = 0\n                else:\n                    second = int(tm_match[4:6])\n                    ms_match = match.group(3)\n                    if ms_match:\n                        microsecond = int(ms_match.rstrip().ljust(6, '0'))\n                    else:\n                        microsecond = 0\n                val = super(TM, cls).__new__(cls, hour, minute, second,\n                                             microsecond)\n            elif val == '':\n                val = None  # empty time\n            else:\n                try:\n                    val = super(TM, cls).__new__(cls, val)\n                except TypeError:\n                    raise ValueError(\"Cannot convert to datetime: '%s\" % (val))\n        elif isinstance(val, time):\n            val = super(TM, cls).__new__(cls, val.hour, val.minute, val.second,\n                                         val.microsecond)\n        else:\n            val = super(TM, cls).__new__(cls, val)\n        return val\n\n    def __init__(self, val):\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, TM) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n        else:\n            return super(TM, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\nclass DSfloat(float):\n    \"\"\"Store value for an element with VR **DS** as :class:`float`.\n\n    If constructed from an empty string, return the empty string,\n    not an instance of this class.\n\n    \"\"\"\n    __slots__ = ['original_string']\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __init__(self, val):\n        \"\"\"Store the original string if one given, for exact write-out of same\n        value later.\n        \"\"\"\n        # ... also if user changes a data element value, then will get\n        # a different object, because float is immutable.\n\n        has_attribute = hasattr(val, 'original_string')\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, (DSfloat, DSdecimal)) and has_attribute:\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n\n        # Issue #937 (Python 3.8 compatibility)\n        return repr(self)[1:-1]\n\n    def __repr__(self):\n        return '\"{}\"'.format(super(DSfloat, self).__repr__())\n\n\nclass DSdecimal(Decimal):\n    \"\"\"Store value for an element with VR **DS** as :class:`decimal.Decimal`.\n\n    Notes\n    -----\n    If constructed from an empty string, returns the empty string, not an\n    instance of this class.\n    \"\"\"\n    __slots__ = ['original_string']\n\n    def __getstate__(self):\n        return dict((slot, getattr(self, slot)) for slot in self.__slots__\n                    if hasattr(self, slot))\n\n    def __setstate__(self, state):\n        for slot, value in state.items():\n            setattr(self, slot, value)\n\n    def __new__(cls, val):\n        \"\"\"Create an instance of DS object, or return a blank string if one is\n        passed in, e.g. from a type 2 DICOM blank value.\n\n        Parameters\n        ----------\n        val : str or numeric\n            A string or a number type which can be converted to a decimal.\n        \"\"\"\n        # Store this value here so that if the input string is actually a valid\n        # string but decimal.Decimal transforms it to an invalid string it will\n        # still be initialized properly\n        enforce_length = config.enforce_valid_values\n        # DICOM allows spaces around the string,\n        # but python doesn't, so clean it\n        if isinstance(val, str):\n            val = val.strip()\n            # If the input string is actually invalid that we relax the valid\n            # value constraint for this particular instance\n            if len(val) <= 16:\n                enforce_length = False\n        if val == '':\n            return val\n        if isinstance(val, float) and not config.allow_DS_float:\n            msg = (\"DS cannot be instantiated with a float value, \"\n                   \"unless config.allow_DS_float is set to True. \"\n                   \"It is recommended to convert to a string instead, \"\n                   \"with the desired number of digits, or use \"\n                   \"Decimal.quantize and pass a Decimal instance.\")\n            raise TypeError(msg)\n        if not isinstance(val, Decimal):\n            val = super(DSdecimal, cls).__new__(cls, val)\n        if len(str(val)) > 16 and enforce_length:\n            msg = (\"DS value representation must be <= 16 \"\n                   \"characters by DICOM standard. Initialize with \"\n                   \"a smaller string, or set config.enforce_valid_values \"\n                   \"to False to override, or use Decimal.quantize() and \"\n                   \"initialize with a Decimal instance.\")\n            raise OverflowError(msg)\n        return val\n\n    def __init__(self, val):\n        \"\"\"Store the original string if one given, for exact write-out of same\n        value later. E.g. if set ``'1.23e2'``, :class:`~decimal.Decimal` would\n        write ``'123'``, but :class:`DS` will use the original.\n        \"\"\"\n        # ... also if user changes a data element value, then will get\n        # a different Decimal, as Decimal is immutable.\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, (DSfloat, DSdecimal)) and hasattr(val, 'original_string'):  # noqa\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string') and len(self.original_string) <= 16:  # noqa\n            return self.original_string\n        else:\n            return super(DSdecimal, self).__str__()\n\n    def __repr__(self):\n        return \"\\\"\" + str(self) + \"\\\"\"\n\n\n# CHOOSE TYPE OF DS\nif config.use_DS_decimal:\n    DSclass = DSdecimal\nelse:\n    DSclass = DSfloat\n\n\ndef DS(val):\n    \"\"\"Factory function for creating DS class instances.\n\n    Checks for blank string; if so, returns that, else calls :class:`DSfloat`\n    or :class:`DSdecimal` to create the class instance. This avoids overriding\n    ``DSfloat.__new__()`` (which carries a time penalty for large arrays of\n    DS).\n\n    Similarly the string clean and check can be avoided and :class:`DSfloat`\n    called directly if a string has already been processed.\n    \"\"\"\n    if isinstance(val, str):\n        val = val.strip()\n    if val == '' or val is None:\n        return val\n    return DSclass(val)\n\n\nclass IS(int):\n    \"\"\"Store value for an element with VR **IS** as :class:`int`.\n\n    Stores original integer string for exact rewriting of the string\n    originally read or stored.\n    \"\"\"\n\n    def __new__(cls, val):\n        \"\"\"Create instance if new integer string\"\"\"\n        if val is None:\n            return val\n        if isinstance(val, str) and val.strip() == '':\n            return ''\n\n        newval = super(IS, cls).__new__(cls, val)\n\n        # check if a float or Decimal passed in, then could have lost info,\n        # and will raise error. E.g. IS(Decimal('1')) is ok, but not IS(1.23)\n        if isinstance(val, (float, Decimal)) and newval != val:\n            raise TypeError(\"Could not convert value to integer without loss\")\n        # Checks in case underlying int is >32 bits, DICOM does not allow this\n        check_newval = (newval < -2 ** 31 or newval >= 2 ** 31)\n        if check_newval and config.enforce_valid_values:\n            dcm_limit = \"-2**31 to (2**31 - 1) for IS\"\n            message = \"Value exceeds DICOM limits of %s\" % (dcm_limit)\n            raise OverflowError(message)\n        return newval\n\n    def __init__(self, val):\n        # If a string passed, then store it\n        if isinstance(val, str):\n            self.original_string = val\n        elif isinstance(val, IS) and hasattr(val, 'original_string'):\n            self.original_string = val.original_string\n\n    def __str__(self):\n        if hasattr(self, 'original_string'):\n            return self.original_string\n\n        # Issue #937 (Python 3.8 compatibility)\n        return repr(self)[1:-1]\n\n    def __repr__(self):\n        return '\"{}\"'.format(super(IS, self).__repr__())\n\n\ndef MultiString(val, valtype=str):\n    \"\"\"Split a bytestring by delimiters if there are any\n\n    Parameters\n    ----------\n    val : bytes or str\n        DICOM byte string to split up.\n    valtype\n        Default :class:`str`, but can be e.g. :class:`~pydicom.uid.UID` to\n        overwrite to a specific type.\n\n    Returns\n    -------\n    valtype or list of valtype\n        The split value as `valtype` or a :class:`list` of `valtype`.\n    \"\"\"\n    # Remove trailing blank used to pad to even length\n    # 2005.05.25: also check for trailing 0, error made\n    # in PET files we are converting\n\n    while val and (val.endswith(' ') or val.endswith('\\x00')):\n        val = val[:-1]\n    splitup = val.split(\"\\\\\")\n\n    if len(splitup) == 1:\n        val = splitup[0]\n        return valtype(val) if val else val\n    else:\n        return MultiValue(valtype, splitup)\n\n\ndef _verify_encodings(encodings):\n    \"\"\"Checks the encoding to ensure proper format\"\"\"\n    if encodings is not None:\n        if not isinstance(encodings, (list, tuple)):\n            return encodings,\n        return tuple(encodings)\n    return encodings\n\n\ndef _decode_personname(components, encodings):\n    \"\"\"Return a list of decoded person name components.\n\n    Parameters\n    ----------\n    components : list of byte string\n        The list of the up to three encoded person name components\n    encodings : list of str\n        The Python encodings uses to decode `components`.\n\n    Returns\n    -------\n    text type\n        The unicode string representing the person name.\n        If the decoding of some component parts is not possible using the\n        given encodings, they are decoded with the first encoding using\n        replacement characters for bytes that cannot be decoded.\n    \"\"\"\n    from pydicom.charset import decode_string\n\n    if isinstance(components[0], str):\n        comps = components\n    else:\n        comps = [decode_string(comp, encodings, PN_DELIMS)\n                 for comp in components]\n    # Remove empty elements from the end to avoid trailing '='\n    while len(comps) and not comps[-1]:\n        comps.pop()\n    return tuple(comps)\n\n\ndef _encode_personname(components, encodings):\n    \"\"\"Encode a list of text string person name components.\n\n    Parameters\n    ----------\n    components : list of text type\n        The list of the up to three unicode person name components\n    encodings : list of str\n        The Python encodings uses to encode `components`.\n\n    Returns\n    -------\n    byte string\n        The byte string that can be written as a PN DICOM tag value.\n        If the encoding of some component parts is not possible using the\n        given encodings, they are encoded with the first encoding using\n        replacement bytes for characters that cannot be encoded.\n    \"\"\"\n    from pydicom.charset import encode_string\n\n    encoded_comps = []\n    for comp in components:\n        groups = [encode_string(group, encodings)\n                  for group in comp.split('^')]\n        encoded_comps.append(b'^'.join(groups))\n\n    # Remove empty elements from the end\n    while len(encoded_comps) and not encoded_comps[-1]:\n        encoded_comps.pop()\n    return b'='.join(encoded_comps)\n\n\nclass PersonName:\n    def __new__(cls, *args, **kwargs):\n        # Handle None value by returning None instead of a PersonName object\n        if len(args) and args[0] is None:\n            return None\n        return super(PersonName, cls).__new__(cls)\n\n    def __init__(self, val, encodings=None, original_string=None):\n        if isinstance(val, PersonName):\n            encodings = val.encodings\n            self.original_string = val.original_string\n            self._components = tuple(str(val).split('='))\n        elif isinstance(val, bytes):\n            # this is the raw byte string - decode it on demand\n            self.original_string = val\n            self._components = None\n        else:\n            # handle None `val` as empty string\n            val = val or ''\n\n            # this is the decoded string - save the original string if\n            # available for easier writing back\n            self.original_string = original_string\n            components = val.split('=')\n            # Remove empty elements from the end to avoid trailing '='\n            while len(components) and not components[-1]:\n                components.pop()\n            self._components = tuple(components)\n\n            # if the encoding is not given, leave it as undefined (None)\n        self.encodings = _verify_encodings(encodings)\n        self._dict = {}\n\n    def _create_dict(self):\n        \"\"\"Creates a dictionary of person name group and component names.\n\n        Used exclusively for `formatted` for backwards compatibility.\n        \"\"\"\n        if not self._dict:\n            for name in ('family_name', 'given_name', 'middle_name',\n                         'name_prefix', 'name_suffix',\n                         'ideographic', 'phonetic'):\n                self._dict[name] = getattr(self, name, '')\n\n    @property\n    def components(self):\n        \"\"\"Returns up to three decoded person name components.\n\n        .. versionadded:: 1.2\n\n        The returned components represent the alphabetic, ideographic and\n        phonetic representations as a list of unicode strings.\n        \"\"\"\n        if self._components is None:\n            groups = self.original_string.split(b'=')\n            encodings = self.encodings or [default_encoding]\n            self._components = _decode_personname(groups, encodings)\n\n        return self._components\n\n    def _name_part(self, i):\n        try:\n            return self.components[0].split('^')[i]\n        except IndexError:\n            return ''\n\n    @property\n    def family_name(self):\n        \"\"\"Return the first (family name) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(0)\n\n    @property\n    def given_name(self):\n        \"\"\"Return the second (given name) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(1)\n\n    @property\n    def middle_name(self):\n        \"\"\"Return the third (middle name) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(2)\n\n    @property\n    def name_prefix(self):\n        \"\"\"Return the fourth (name prefix) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(3)\n\n    @property\n    def name_suffix(self):\n        \"\"\"Return the fifth (name suffix) group of the alphabetic person name\n        representation as a unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        return self._name_part(4)\n\n    @property\n    def ideographic(self):\n        \"\"\"Return the second (ideographic) person name component as a\n        unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        try:\n            return self.components[1]\n        except IndexError:\n            return ''\n\n    @property\n    def phonetic(self):\n        \"\"\"Return the third (phonetic) person name component as a\n        unicode string\n\n        .. versionadded:: 1.2\n        \"\"\"\n        try:\n            return self.components[2]\n        except IndexError:\n            return ''\n\n    def __eq__(self, other):\n        return str(self) == other\n\n    def __ne__(self, other):\n        return not self == other\n\n    def __str__(self):\n        return '='.join(self.components).__str__()\n\n    def __next__(self):\n        # Get next character or stop iteration\n        if self._i < self._rep_len:\n            c = self._str_rep[self._i]\n            self._i += 1\n            return c\n        else:\n            raise StopIteration\n\n    def __iter__(self):\n        # Get string rep. and length, initialize index counter\n        self._str_rep = self.__str__()\n        self._rep_len = len(self._str_rep)\n        self._i = 0\n        return self\n\n    def __contains__(self, x):\n        return x in self.__str__()\n\n    def __repr__(self):\n        return '='.join(self.components).__repr__()\n\n    def __hash__(self):\n        return hash(self.components)\n\n    def decode(self, encodings=None):\n        \"\"\"Return the patient name decoded by the given `encodings`.\n\n        Parameters\n        ----------\n        encodings : list of str\n            The list of encodings used for decoding the byte string. If not\n            given, the initial encodings set in the object are used.\n\n        Returns\n        -------\n        valuerep.PersonName\n            A person name object that will return the decoded string with\n            the given encodings on demand. If the encodings are not given,\n            the current object is returned.\n        \"\"\"\n        # in the common case (encoding did not change) we decode on demand\n        if encodings is None or encodings == self.encodings:\n            return self\n        # the encoding was unknown or incorrect - create a new\n        # PersonName object with the changed encoding\n        encodings = _verify_encodings(encodings)\n        if self.original_string is None:\n            # if the original encoding was not set, we set it now\n            self.original_string = _encode_personname(\n                self.components, self.encodings or [default_encoding])\n        return PersonName(self.original_string, encodings)\n\n    def encode(self, encodings=None):\n        \"\"\"Return the patient name decoded by the given `encodings`.\n\n        Parameters\n        ----------\n        encodings : list of str\n            The list of encodings used for encoding the unicode string. If\n            not given, the initial encodings set in the object are used.\n\n        Returns\n        -------\n        bytes\n            The person name encoded with the given encodings as a byte string.\n            If no encoding is given, the original byte string is returned, if\n            available, otherwise each group of the patient name is encoded\n            with the first matching of the given encodings.\n        \"\"\"\n        encodings = _verify_encodings(encodings) or self.encodings\n\n        # if the encoding is not the original encoding, we have to return\n        # a re-encoded string (without updating the original string)\n        if encodings != self.encodings and self.encodings is not None:\n            return _encode_personname(self.components, encodings)\n        if self.original_string is None:\n            # if the original encoding was not set, we set it now\n            self.original_string = _encode_personname(\n                self.components, encodings or [default_encoding])\n        return self.original_string\n\n    def family_comma_given(self):\n        return self.formatted('%(family_name)s, %(given_name)s')\n\n    def formatted(self, format_str):\n        self._create_dict()\n        return format_str % self._dict\n\n    def __bool__(self):\n        if self.original_string is None:\n            return (bool(self._components) and\n                    (len(self._components) > 1 or bool(self._components[0])))\n        return bool(self.original_string)\n\n\n# Alias old class names for backwards compat in user code\nPersonNameUnicode = PersonName = PersonName\n","header":{"file":{"path":"pydicom/valuerep.py"},"misc_line":null},"index_line":null,"is_binary_file":false,"binary_line":null,"minus_file":{"path":"a/pydicom/valuerep.py"},"plus_file":{"path":"b/pydicom/valuerep.py"},"hunks":[{"descriptor":{"old_range":{"start":1,"length":6},"new_range":{"start":1,"length":5},"section":""},"line_group":{"all_lines":[{"content":"# Copyright 2008-2018 pydicom authors. See LICENSE file for details.","type":"context"},{"content":"\"\"\"Special classes for DICOM value representations (VR)\"\"\"","type":"context"},{"content":"from copy import deepcopy","type":"deleted"},{"content":"from decimal import Decimal","type":"context"},{"content":"import re","type":"context"},{"content":"","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]},{"descriptor":{"old_range":{"start":750,"length":6},"new_range":{"start":749,"length":25},"section":"def __ne__(self, other):"},"line_group":{"all_lines":[{"content":"    def __str__(self):","type":"context"},{"content":"        return '='.join(self.components).__str__()","type":"context"},{"content":"","type":"context"},{"content":"    def __next__(self):","type":"added"},{"content":"        # Get next character or stop iteration","type":"added"},{"content":"        if self._i < self._rep_len:","type":"added"},{"content":"            c = self._str_rep[self._i]","type":"added"},{"content":"            self._i += 1","type":"added"},{"content":"            return c","type":"added"},{"content":"        else:","type":"added"},{"content":"            raise StopIteration","type":"added"},{"content":"","type":"added"},{"content":"    def __iter__(self):","type":"added"},{"content":"        # Get string rep. and length, initialize index counter","type":"added"},{"content":"        self._str_rep = self.__str__()","type":"added"},{"content":"        self._rep_len = len(self._str_rep)","type":"added"},{"content":"        self._i = 0","type":"added"},{"content":"        return self","type":"added"},{"content":"","type":"added"},{"content":"    def __contains__(self, x):","type":"added"},{"content":"        return x in self.__str__()","type":"added"},{"content":"","type":"added"},{"content":"    def __repr__(self):","type":"context"},{"content":"        return '='.join(self.components).__repr__()","type":"context"},{"content":"","type":"context"},{"content":"","type":"context"}]},"modified_entities":[],"added_entities":[],"deleted_entities":[]}]}],"old_commit_hash":"b9fb05c177b685bf683f7f57b2d57374eb7d882d","new_commit_hash":"2e9301e8801e6e30c71ccf6833ec2562420bfbbd","commit_message":"next() no longer starts iterator","commit_date":"2020-06-29T10:45:31+02:00","metadata":{}}