Repository: pydicom/pydicom

Issue:
can open my dicom, error in re.match('^ISO[^_]IR', encoding)
```
(test) root@DESKTOP-COPUCVT:/mnt/e/test# python3 mydicom.py
Traceback (most recent call last):
  File "/root/.local/share/virtualenvs/test-LINKoilU/lib/python3.6/site-packages/pydicom/charset.py", line 625, in convert_encodings
    py_encodings.append(python_encoding[encoding])
KeyError: 73

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "mydicom.py", line 12, in <module>
    pydicom.dcmread("DX.X.1.2.276.0.7230010.3.1.4.313262848.25.1563878256.444385.dcm")
  File "/root/.local/share/virtualenvs/test-LINKoilU/lib/python3.6/site-packages/pydicom/filereader.py", line 850, in dcmread
    force=force, specific_tags=specific_tags)
  File "/root/.local/share/virtualenvs/test-LINKoilU/lib/python3.6/site-packages/pydicom/filereader.py", line 728, in read_partial
    specific_tags=specific_tags)
  File "/root/.local/share/virtualenvs/test-LINKoilU/lib/python3.6/site-packages/pydicom/filereader.py", line 382, in read_dataset
    encoding = convert_encodings(char_set)
  File "/root/.local/share/virtualenvs/test-LINKoilU/lib/python3.6/site-packages/pydicom/charset.py", line 628, in convert_encodings
    _python_encoding_for_corrected_encoding(encoding))
  File "/root/.local/share/virtualenvs/test-LINKoilU/lib/python3.6/site-packages/pydicom/charset.py", line 647, in _python_encoding_for_corrected_encoding
    if re.match('^ISO[^_]IR', encoding) is not None:
  File "/root/.local/share/virtualenvs/test-LINKoilU/lib/python3.6/re.py", line 172, in match
    return _compile(pattern, flags).match(string)
TypeError: expected string or bytes-like object
```

#### Description
 I dont know why pydicom cant open my pictures, but other python library can read the picture and read some meta data correctly. I suspect " if re.match('^ISO[^_]IR', encoding) is not None:"  the encoding here is not string for my dicom picture.   I am new to pydicom, 
Has anyone encountered a similar problem? how to solve it?  need help,thanks!

here is some dicom tags:
![image](https://user-images.githubusercontent.com/32253100/61868213-8016f500-af0b-11e9-8736-8703230229cf.png)
![image](https://user-images.githubusercontent.com/32253100/61868247-91600180-af0b-11e9-8767-a4045e901b8f.png)
![image](https://user-images.githubusercontent.com/32253100/61868284-a50b6800-af0b-11e9-88fd-10180e0acf56.png)



#### Steps/Code to Reproduce
```py

import pydicom
import os
import numpy
child_path = "DX.X.1.2.276.0.7230010.3.1.4.313262848.25.1563878256.444385.dcm"
pydicom.dcmread("DX.X.1.2.276.0.7230010.3.1.4.313262848.25.1563878256.444385.dcm"）

```

#### Expected Results
Example: read the file without error

#### Actual Results
cant read the file

#### Versions
v1.3.0

python v3.6

<!-- Thanks for contributing! -->


Discussion:
darcymason: Some relevant info:  http://dicom.nema.org/medical/dicom/current/output/html/part05.html#sect_6.2.2

Paraphrasing the info there:  it's intended for applications that process an implicit VR file and do not know the tag (therefore can't determine the VR).  ... OR its a VR with the shorter 16-bit length field and the length is greater than that.

Also of interest:

> If at some point an application knows the actual VR for an Attribute of VR UN (e.g., has its own applicable data dictionary), it can assume that the Value Field of the Attribute is encoded in Little Endian byte ordering with implicit VR encoding, irrespective of the current Transfer Syntax.

So I take that to mean we can definitely set a UN VR to the known correct one for text values (if length fits in 16 bits), and also for binary, if we pay attention to byte swapping... that's assuming applying the standard is the right thing to do, and some app hasn't just set a UN in error, as it appears must be the case in #899.

scaramallion: Fair enough

Patch:
diff --git a/doc/whatsnew/v1.4.0.rst b/doc/whatsnew/v1.4.0.rst
index 85d361338..ca25f8ed8 100644
--- a/doc/whatsnew/v1.4.0.rst
+++ b/doc/whatsnew/v1.4.0.rst
@@ -10,3 +10,4 @@ Fixes
 * Prevent exception if assigning `None` to UI element (:issue:`894`)
 * Fixed print output for numeric multi-value elements (:issue:`892`)
 * Fixed testing PN values for truthiness (:issue:`891`)
+* Fixed handling of known tags with VR UN (:issue:`899`)
diff --git a/pydicom/config.py b/pydicom/config.py
index ae9c8bdf1..d3f589110 100644
--- a/pydicom/config.py
+++ b/pydicom/config.py
@@ -62,10 +62,7 @@ and datetime.time respectively. Default: False
 
 # Logging system and debug function to change logging level
 logger = logging.getLogger('pydicom')
-handler = logging.StreamHandler()
-formatter = logging.Formatter("%(message)s")
-handler.setFormatter(formatter)
-logger.addHandler(handler)
+logger.addHandler(logging.NullHandler())
 
 
 import pydicom.pixel_data_handlers.numpy_handler as np_handler  # noqa
@@ -110,16 +107,29 @@ syntax, then this fact is announced in a NotImplementedError exception.
 """
 
 
-def debug(debug_on=True):
-    """Turn debugging of DICOM file reading and writing on or off.
+def debug(debug_on=True, default_handler=True):
+    """Turn on/off debugging of DICOM file reading and writing.
+
     When debugging is on, file location and details about the
     elements read at that location are logged to the 'pydicom'
     logger using python's logging module.
 
-    :param debug_on: True (default) to turn on debugging,
-    False to turn off.
+    Parameters
+    ----------
+    debug_on : bool, optional
+        If True (default) then turn on debugging, False to turn off.
+    default_handler : bool, optional
+        If True (default) then use ``logging.StreamHandler()`` as the handler
+        for log messages.
     """
     global logger, debugging
+
+    if default_handler:
+        handler = logging.StreamHandler()
+        formatter = logging.Formatter("%(message)s")
+        handler.setFormatter(formatter)
+        logger.addHandler(handler)
+
     if debug_on:
         logger.setLevel(logging.DEBUG)
         debugging = True
@@ -129,4 +139,4 @@ def debug(debug_on=True):
 
 
 # force level=WARNING, in case logging default is set differently (issue 103)
-debug(False)
+debug(False, False)
diff --git a/pydicom/dataelem.py b/pydicom/dataelem.py
index 11e971ecc..0661192fd 100644
--- a/pydicom/dataelem.py
+++ b/pydicom/dataelem.py
@@ -165,6 +165,17 @@ class DataElement(object):
         if not isinstance(tag, BaseTag):
             tag = Tag(tag)
         self.tag = tag
+
+        # a known tag shall only have the VR 'UN' if it has a length that
+        # exceeds the size that can be encoded in 16 bit - all other cases
+        # can be seen as an encoding error and can be corrected
+        if VR == 'UN' and (is_undefined_length or value is None or
+                           len(value) < 0xffff):
+            try:
+                VR = dictionary_VR(tag)
+            except KeyError:
+                pass
+
         self.VR = VR  # Note!: you must set VR before setting value
         if already_converted:
             self._value = value
diff --git a/setup.py b/setup.py
index c6f151ff7..6955f9ac6 100755
--- a/setup.py
+++ b/setup.py
@@ -40,6 +40,7 @@ CLASSIFIERS = [
     "Programming Language :: Python :: 3.4",
     "Programming Language :: Python :: 3.5",
     "Programming Language :: Python :: 3.6",
+    "Programming Language :: Python :: 3.7",
     "Operating System :: OS Independent",
     "Topic :: Scientific/Engineering :: Medical Science Apps.",
     "Topic :: Scientific/Engineering :: Physics",


Test patch:
diff --git a/pydicom/tests/test_config.py b/pydicom/tests/test_config.py
new file mode 100644
index 000000000..66824c709
--- /dev/null
+++ b/pydicom/tests/test_config.py
@@ -0,0 +1,107 @@
+# Copyright 2008-2019 pydicom authors. See LICENSE file for details.
+"""Unit tests for the pydicom.config module."""
+
+import logging
+import sys
+
+import pytest
+
+from pydicom import dcmread
+from pydicom.config import debug
+from pydicom.data import get_testdata_files
+
+
+DS_PATH = get_testdata_files("CT_small.dcm")[0]
+PYTEST = [int(x) for x in pytest.__version__.split('.')]
+
+
+@pytest.mark.skipif(PYTEST[:2] < [3, 4], reason='no caplog')
+class TestDebug(object):
+    """Tests for config.debug()."""
+    def setup(self):
+        self.logger = logging.getLogger('pydicom')
+
+    def teardown(self):
+        # Reset to just NullHandler
+        self.logger.handlers = [self.logger.handlers[0]]
+
+    def test_default(self, caplog):
+        """Test that the default logging handler is a NullHandler."""
+        assert 1 == len(self.logger.handlers)
+        assert isinstance(self.logger.handlers[0], logging.NullHandler)
+
+        with caplog.at_level(logging.DEBUG, logger='pydicom'):
+            ds = dcmread(DS_PATH)
+
+            assert "Call to dcmread()" not in caplog.text
+            assert "Reading File Meta Information preamble..." in caplog.text
+            assert "Reading File Meta Information prefix..." in caplog.text
+            assert "00000080: 'DICM' prefix found" in caplog.text
+
+    def test_debug_on_handler_null(self, caplog):
+        """Test debug(True, False)."""
+        debug(True, False)
+        assert 1 == len(self.logger.handlers)
+        assert isinstance(self.logger.handlers[0], logging.NullHandler)
+
+        with caplog.at_level(logging.DEBUG, logger='pydicom'):
+            ds = dcmread(DS_PATH)
+
+            assert "Call to dcmread()" in caplog.text
+            assert "Reading File Meta Information preamble..." in caplog.text
+            assert "Reading File Meta Information prefix..." in caplog.text
+            assert "00000080: 'DICM' prefix found" in caplog.text
+            msg = (
+                "00009848: fc ff fc ff 4f 42 00 00 7e 00 00 00    "
+                "(fffc, fffc) OB Length: 126"
+            )
+            assert msg in caplog.text
+
+    def test_debug_off_handler_null(self, caplog):
+        """Test debug(False, False)."""
+        debug(False, False)
+        assert 1 == len(self.logger.handlers)
+        assert isinstance(self.logger.handlers[0], logging.NullHandler)
+
+        with caplog.at_level(logging.DEBUG, logger='pydicom'):
+            ds = dcmread(DS_PATH)
+
+            assert "Call to dcmread()" not in caplog.text
+            assert "Reading File Meta Information preamble..." in caplog.text
+            assert "Reading File Meta Information prefix..." in caplog.text
+            assert "00000080: 'DICM' prefix found" in caplog.text
+
+    def test_debug_on_handler_stream(self, caplog):
+        """Test debug(True, True)."""
+        debug(True, True)
+        assert 2 == len(self.logger.handlers)
+        assert isinstance(self.logger.handlers[0], logging.NullHandler)
+        assert isinstance(self.logger.handlers[1], logging.StreamHandler)
+
+        with caplog.at_level(logging.DEBUG, logger='pydicom'):
+            ds = dcmread(DS_PATH)
+
+            assert "Call to dcmread()" in caplog.text
+            assert "Reading File Meta Information preamble..." in caplog.text
+            assert "Reading File Meta Information prefix..." in caplog.text
+            assert "00000080: 'DICM' prefix found" in caplog.text
+            msg = (
+                "00009848: fc ff fc ff 4f 42 00 00 7e 00 00 00    "
+                "(fffc, fffc) OB Length: 126"
+            )
+            assert msg in caplog.text
+
+    def test_debug_off_handler_stream(self, caplog):
+        """Test debug(False, True)."""
+        debug(False, True)
+        assert 2 == len(self.logger.handlers)
+        assert isinstance(self.logger.handlers[0], logging.NullHandler)
+        assert isinstance(self.logger.handlers[1], logging.StreamHandler)
+
+        with caplog.at_level(logging.DEBUG, logger='pydicom'):
+            ds = dcmread(DS_PATH)
+
+            assert "Call to dcmread()" not in caplog.text
+            assert "Reading File Meta Information preamble..." in caplog.text
+            assert "Reading File Meta Information prefix..." in caplog.text
+            assert "00000080: 'DICM' prefix found" in caplog.text
diff --git a/pydicom/tests/test_dataelem.py b/pydicom/tests/test_dataelem.py
index 8f885926e..8bd68fe11 100644
--- a/pydicom/tests/test_dataelem.py
+++ b/pydicom/tests/test_dataelem.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
 # Copyright 2008-2018 pydicom authors. See LICENSE file for details.
 """Unit tests for the pydicom.dataelem module."""
 
@@ -351,6 +352,51 @@ class TestDataElement(object):
         assert '[Overlay ID]' == private_data_elem.name
         assert 'UN' == private_data_elem.VR
 
+    def test_known_tags_with_UN_VR(self):
+        """Known tags with VR UN are correctly decoded."""
+        ds = Dataset()
+        ds[0x00080005] = DataElement(0x00080005, 'UN', b'ISO_IR 126')
+        ds[0x00100010] = DataElement(0x00100010, 'UN',
+                                     u'Διονυσιος'.encode('iso_ir_126'))
+        ds.decode()
+        assert 'CS' == ds[0x00080005].VR
+        assert 'PN' == ds[0x00100010].VR
+        assert u'Διονυσιος' == ds[0x00100010].value
+
+        ds = Dataset()
+        ds[0x00080005] = DataElement(0x00080005, 'UN',
+                                     b'ISO 2022 IR 100\\ISO 2022 IR 126')
+        ds[0x00100010] = DataElement(0x00100010, 'UN',
+                                     b'Dionysios=\x1b\x2d\x46'
+                                     + u'Διονυσιος'.encode('iso_ir_126'))
+        ds.decode()
+        assert 'CS' == ds[0x00080005].VR
+        assert 'PN' == ds[0x00100010].VR
+        assert u'Dionysios=Διονυσιος' == ds[0x00100010].value
+
+    def test_unknown_tags_with_UN_VR(self):
+        """Unknown tags with VR UN are not decoded."""
+        ds = Dataset()
+        ds[0x00080005] = DataElement(0x00080005, 'CS', b'ISO_IR 126')
+        ds[0x00111010] = DataElement(0x00111010, 'UN',
+                                     u'Διονυσιος'.encode('iso_ir_126'))
+        ds.decode()
+        assert 'UN' == ds[0x00111010].VR
+        assert u'Διονυσιος'.encode('iso_ir_126') == ds[0x00111010].value
+
+    def test_tag_with_long_value_UN_VR(self):
+        """Tag with length > 64kb with VR UN is not changed."""
+        ds = Dataset()
+        ds[0x00080005] = DataElement(0x00080005, 'CS', b'ISO_IR 126')
+
+        single_value = b'123456.789012345'
+        large_value = b'\\'.join([single_value] * 4500)
+        ds[0x30040058] = DataElement(0x30040058, 'UN',
+                                     large_value,
+                                     is_undefined_length=False)
+        ds.decode()
+        assert 'UN' == ds[0x30040058].VR
+
     def test_empty_text_values(self):
         """Test that assigning an empty value behaves as expected."""
         def check_empty_text_element(value):