Repository: pydicom/pydicom

Issue:
More helpful message if numpy not installed
<!-- Instructions For Filing a Bug: https://github.com/pydicom/pydicom/blob/master/CONTRIBUTING.md#filing-bugs -->

#### Description
<!-- Example: Attribute Error thrown when printing (0x0010, 0x0020) patient Id> 0-->

#### Steps/Code to Reproduce
Install pydicom in a venv with no numpy.
Try to read data\test_files\MR_small.dcm.
Error message states that no image handler could decode the transfer syntax, when all that is needed is numpy.  I think the error message could be more direct in this case.

#### Expected Results
A clear error message that numpy is needed.
#### Actual Results
Traceback (most recent call last):
  File "downsize.py", line 11, in <module>
    data = ds.pixel_array
  File "C:\env\liverc1\lib\site-packages\pydicom\dataset.py", line 800, in pixel
_array
    return self._get_pixel_array()
  File "C:\env\liverc1\lib\site-packages\pydicom\dataset.py", line 692, in _get_
pixel_array
    self.convert_pixel_data()
  File "C:\env\liverc1\lib\site-packages\pydicom\dataset.py", line 743, in conve
rt_pixel_data
    raise NotImplementedError(msg)
NotImplementedError: No available image handler could decode this transfer synta
x Explicit VR Little Endian

#### Versions

Python 3.6.4 (v3.6.4:d48eceb, Dec 19 2017, 06:54:40) [MSC v.1900 64 bit (AMD64)]
pydicom 1.0.1rc1

<!-- Thanks for contributing! -->


Discussion:
pep8speaks: Hello @scaramallion! Thanks for submitting the PR.

 - In the file [`pydicom/config.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/config.py), following                are the PEP8 issues :
> [Line 71:1](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/config.py#L71): [E402](https://duckduckgo.com/?q=pep8%20E402) module level import not at top of file
> [Line 72:1](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/config.py#L72): [E402](https://duckduckgo.com/?q=pep8%20E402) module level import not at top of file
> [Line 73:1](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/config.py#L73): [E402](https://duckduckgo.com/?q=pep8%20E402) module level import not at top of file
> [Line 74:1](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/config.py#L74): [E402](https://duckduckgo.com/?q=pep8%20E402) module level import not at top of file
> [Line 75:1](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/config.py#L75): [E402](https://duckduckgo.com/?q=pep8%20E402) module level import not at top of file
> [Line 114:1](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/config.py#L114): [E303](https://duckduckgo.com/?q=pep8%20E303) too many blank lines (3)


 - In the file [`pydicom/dataset.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/dataset.py), following                are the PEP8 issues :
> [Line 819:62](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/dataset.py#L819): [E127](https://duckduckgo.com/?q=pep8%20E127) continuation line over-indented for visual indent


 - In the file [`pydicom/pixel_data_handlers/gdcm_handler.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/gdcm_handler.py), following                are the PEP8 issues :
> [Line 25:12](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/gdcm_handler.py#L25): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'
> [Line 26:11](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/gdcm_handler.py#L26): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'


 - In the file [`pydicom/pixel_data_handlers/jpeg_ls_handler.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/jpeg_ls_handler.py), following                are the PEP8 issues :
> [Line 26:12](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/jpeg_ls_handler.py#L26): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'
> [Line 27:14](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/jpeg_ls_handler.py#L27): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'


 - In the file [`pydicom/pixel_data_handlers/numpy_handler.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/numpy_handler.py), following                are the PEP8 issues :
> [Line 55:12](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/numpy_handler.py#L55): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'


 - In the file [`pydicom/pixel_data_handlers/pillow_handler.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/pillow_handler.py), following                are the PEP8 issues :
> [Line 51:12](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/pillow_handler.py#L51): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'
> [Line 52:13](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/pillow_handler.py#L52): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'


 - In the file [`pydicom/pixel_data_handlers/rle_handler.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/rle_handler.py), following                are the PEP8 issues :
> [Line 52:12](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/pixel_data_handlers/rle_handler.py#L52): [E203](https://duckduckgo.com/?q=pep8%20E203) whitespace before ':'


 - There are no PEP8 issues in the file [`pydicom/tests/test_JPEG_LS_transfer_syntax.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/tests/test_JPEG_LS_transfer_syntax.py) !

 - There are no PEP8 issues in the file [`pydicom/tests/test_dataset.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/tests/test_dataset.py) !

 - There are no PEP8 issues in the file [`pydicom/tests/test_gdcm_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/tests/test_gdcm_pixel_data.py) !

 - There are no PEP8 issues in the file [`pydicom/tests/test_jpeg_ls_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/tests/test_jpeg_ls_pixel_data.py) !

 - There are no PEP8 issues in the file [`pydicom/tests/test_numpy_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/tests/test_numpy_pixel_data.py) !

 - There are no PEP8 issues in the file [`pydicom/tests/test_pillow_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/tests/test_pillow_pixel_data.py) !

 - There are no PEP8 issues in the file [`pydicom/tests/test_rle_pixel_data.py`](https://github.com/scaramallion/pydicom/blob/8550e610f5b50088fab25e82e2cea1e2f786b00c/pydicom/tests/test_rle_pixel_data.py) !


scaramallion: Not sure why travis isn't showing up, its [here](https://travis-ci.org/pydicom/pydicom/builds/426992555)
scaramallion: I've changed it so that `image_handlers` is still compatible with the current use.
scaramallion: I can't think of a way to show a deprecation warning on using the module attribute. 
darcymason: ...and if there were a way it would probably be deep black magic and quite different for python 3 and 2.  Not worth it.  I just wanted to make sure my recollection was correct.

Patch:
diff --git a/doc/whatsnew/v1.2.0.rst b/doc/whatsnew/v1.2.0.rst
index 45c12390d..c130343b9 100644
--- a/doc/whatsnew/v1.2.0.rst
+++ b/doc/whatsnew/v1.2.0.rst
@@ -16,6 +16,15 @@ Changes
 * ``uid.JPEG2000Lossy`` deprecated and will be removed in v1.3. Use
   ``uid.JPEG2000`` instead. (:issue:`726`)
 * Equality and inequality operator overrides removed from ``UID``.
+* ``config.image_handlers`` deprecated and will be removed in v1.3. use
+  ``config.pixel_data_handlers`` instead. There is also a change in behaviour
+  in that ``image_handlers`` previously used to only contain the pixel data
+  handlers that had their dependencies met. ``pixel_data_handlers`` contains
+  all handlers no matter whether or not their dependencies are met. To check
+  if a handler is available for use (it has its dependency met) then use the
+  handler's ``is_available`` method.
+* ``DeferredDataElement`` class deprecated and will be removed in v1.3
+  (:issue:`291`)
 
 
 Enhancements
@@ -23,8 +32,6 @@ Enhancements
 
 * Added possibility to set byte strings as value for VRs that use only the
   default character set (:issue:`624`)
-* ``DeferredDataElement`` class deprecated and will be removed in v1.3
-  (:issue:`291`)
 * Functions for encapsulating frames added to encaps module (:pull_request:`696`)
 * Added ``Dataset.fix_meta_info()`` (:issue:`584`)
 * Add new function for bit packing ``pack_bits`` for use with BitsAllocated
@@ -33,6 +40,11 @@ Enhancements
 * Added ``uid.JPEGLosslessP14`` for UID 1.2.840.10008.1.2.4.57
 * Added ``uid.JPEG2000MultiComponentLossless`` for UID 1.2.840.10008.1.2.4.92
 * Added ``uid.JPEG2000MultiComponent`` for UID 1.2.840.10008.1.2.4.93
+* Added full support for Planar Configuration (:issue:`713`)
+* Added support for single frame pixel data where BitsAllocated >8 and
+  SamplesPerPixel > 1 (:issue:`713`)
+* Small improvement in RLE decoding speed (~10%).
+* Add support for non-conformant RLE segment ordering (:pull_request:`729`)
 
 
 Fixes
@@ -45,3 +57,6 @@ Fixes
 * Improve performance of bit unpacking for non-PyPy2 interpreters
   (:pull_request:`715`)
 * First character set no longer removed (:issue:`707`)
+* Fixed RLE decoded data having the wrong byte order (:pull_request:`729`)
+* Fixed RLE decoded data having the wrong planar configuration
+  (:pull_request:`729`)
diff --git a/pydicom/config.py b/pydicom/config.py
index 3466c1b41..54449db3c 100644
--- a/pydicom/config.py
+++ b/pydicom/config.py
@@ -68,20 +68,32 @@ handler.setFormatter(formatter)
 logger.addHandler(handler)
 
 
-image_handlers = []
-"""Image handlers for converting pixel data.
-This is an ordered list that the dataset._get_pixel_array()
+import pydicom.pixel_data_handlers.numpy_handler as np_handler  # noqa
+import pydicom.pixel_data_handlers.rle_handler as rle_handler  # noqa
+import pydicom.pixel_data_handlers.pillow_handler as pillow_handler  # noqa
+import pydicom.pixel_data_handlers.jpeg_ls_handler as jpegls_handler  # noqa
+import pydicom.pixel_data_handlers.gdcm_handler as gdcm_handler  # noqa
+
+pixel_data_handlers = [
+    np_handler,
+    rle_handler,
+    gdcm_handler,
+    pillow_handler,
+    jpegls_handler,
+]
+image_handlers = [hh for hh in pixel_data_handlers if hh.is_available()]
+"""Handlers for converting (7fe0,0010) Pixel Data.
+This is an ordered list that the dataset.convert_pixel_data()
 method will try to extract a correctly sized numpy array from the
-PixelData attribute.
-If a handler lacks required dependencies or can not otherwise be loaded,
-it shall throw an ImportError.
+PixelData element.
+
 Handers shall have two methods:
 
-supports_transfer_syntax(dicom_dataset)
+def supports_transfer_syntax(ds)
   This returns True if the handler might support the transfer syntax
   indicated in the dicom_dataset
 
-def get_pixeldata(dicom_dataset):
+def get_pixeldata(ds):
   This shall either throw an exception or return a correctly sized numpy
   array derived from the PixelData.  Reshaping the array to the correct
   dimensions is handled outside the image handler
@@ -98,42 +110,6 @@ If no one throws an exception, but they all refuse to support the transfer
 syntax, then this fact is announced in a NotImplementedError exception.
 """
 
-have_numpy = True
-try:
-    import pydicom.pixel_data_handlers.numpy_handler as numpy_handler
-    image_handlers.append(numpy_handler)
-
-    import pydicom.pixel_data_handlers.rle_handler as rle_handler
-    image_handlers.append(rle_handler)
-
-except ImportError as e:
-    logger.debug("Could not import numpy")
-    have_numpy = False
-
-have_pillow = True
-try:
-    import pydicom.pixel_data_handlers.pillow_handler as pillow_handler
-    image_handlers.append(pillow_handler)
-except ImportError as e:
-    logger.debug("Could not import pillow")
-    have_pillow = False
-
-have_jpeg_ls = True
-try:
-    import pydicom.pixel_data_handlers.jpeg_ls_handler as jpeg_ls_handler
-    image_handlers.append(jpeg_ls_handler)
-except ImportError as e:
-    logger.debug("Could not import jpeg_ls")
-    have_jpeg_ls = False
-
-have_gdcm = True
-try:
-    import pydicom.pixel_data_handlers.gdcm_handler as gdcm_handler
-    image_handlers.append(gdcm_handler)
-except ImportError as e:
-    logger.debug("Could not import gdcm")
-    have_gdcm = False
-
 
 def debug(debug_on=True):
     """Turn debugging of DICOM file reading and writing on or off.
diff --git a/pydicom/dataset.py b/pydicom/dataset.py
index b37a5e399..a2e762a02 100644
--- a/pydicom/dataset.py
+++ b/pydicom/dataset.py
@@ -40,6 +40,11 @@ from pydicom.tag import Tag, BaseTag, tag_in_exception
 from pydicom.uid import (ExplicitVRLittleEndian, ImplicitVRLittleEndian,
                          ExplicitVRBigEndian, PYDICOM_IMPLEMENTATION_UID)
 
+if compat.in_py2:
+    from pkgutil import find_loader as have_package
+else:
+    from importlib.util import find_spec as have_package
+
 have_numpy = True
 try:
     import numpy
@@ -759,24 +764,52 @@ class Dataset(dict):
         if already_have:
             return
 
-        # Find all pixel data handlers that support the transfer syntax
-        suitable_handlers = [hh for hh in pydicom.config.image_handlers
-                             if hh and hh.supports_transfer_syntax(self)]
+        # Find all possible handlers that support the transfer syntax
+        transfer_syntax = self.file_meta.TransferSyntaxUID
+        possible_handlers = [hh for hh in pydicom.config.pixel_data_handlers
+                             if hh.supports_transfer_syntax(transfer_syntax)]
 
-        # No suitable handlers are available
-        if not suitable_handlers:
+        # No handlers support the transfer syntax
+        if not possible_handlers:
             raise NotImplementedError(
                 "Unable to decode pixel data with a transfer syntax UID of "
-                "'{0}' ({1}) as there are no suitable pixel data handlers "
-                "available. Please see the list of supported Transfer "
-                "Syntaxes in the pydicom documentation for information on "
-                "which additional packages might be required"
+                "'{0}' ({1}) as there are no pixel data handlers "
+                "available that support it. Please see the pydicom "
+                "documentation for information on supported transfer syntaxes "
                 .format(self.file_meta.TransferSyntaxUID,
                         self.file_meta.TransferSyntaxUID.name)
             )
 
+        # Handlers that both support the transfer syntax and have their
+        #   dependencies met
+        available_handlers = [hh for hh in possible_handlers if
+                              hh.is_available()]
+
+        # There are handlers that support the transfer syntax but none of them
+        #   can be used as missing dependencies
+        if not available_handlers:
+            # For each of the possible handlers we want to find which
+            #   dependencies are missing
+            msg = (
+                "The following handlers are available to decode the pixel "
+                "data however they are missing required dependencies: "
+            )
+            pkg_msg = []
+            for hh in possible_handlers:
+                hh_deps = hh.DEPENDENCIES
+                # Missing packages
+                missing = [dd for dd in hh_deps if have_package(dd) is None]
+                # Package names
+                names = [hh_deps[name][1] for name in missing]
+                pkg_msg.append(
+                    "{} (req. {})"
+                    .format(hh.HANDLER_NAME, ', '.join(names))
+                )
+
+            raise RuntimeError(msg + ', '.join(pkg_msg))
+
         last_exception = None
-        for handler in suitable_handlers:
+        for handler in available_handlers:
             try:
                 # Use the handler to get a 1D numpy array of the pixel data
                 arr = handler.get_pixeldata(self)
@@ -786,8 +819,8 @@ class Dataset(dict):
                 #   convert the color space from YCbCr to RGB
                 if handler.needs_to_convert_to_RGB(self):
                     self._pixel_array = convert_color_space(self._pixel_array,
-                                                             'YBR_FULL',
-                                                             'RGB')
+                                                            'YBR_FULL',
+                                                            'RGB')
 
                 self._pixel_id = id(self.PixelData)
 
@@ -808,7 +841,7 @@ class Dataset(dict):
             "Please see the list of supported Transfer Syntaxes in the "
             "pydicom documentation for alternative packages that might "
             "be able to decode the data"
-            .format(", ".join([str(hh) for hh in suitable_handlers]))
+            .format(", ".join([str(hh) for hh in available_handlers]))
         )
 
         raise last_exception
diff --git a/pydicom/pixel_data_handlers/gdcm_handler.py b/pydicom/pixel_data_handlers/gdcm_handler.py
index b662005c5..fc747716c 100644
--- a/pydicom/pixel_data_handlers/gdcm_handler.py
+++ b/pydicom/pixel_data_handlers/gdcm_handler.py
@@ -2,28 +2,51 @@
 """Use the gdcm python package to decode pixel transfer syntaxes."""
 
 import sys
-from pydicom import compat
-import pydicom
-have_numpy = True
+
 try:
     import numpy
+    HAVE_NP = True
 except ImportError:
-    have_numpy = False
-    raise
+    HAVE_NP = False
 
-have_gdcm = True
 try:
     import gdcm
+    HAVE_GDCM = True
 except ImportError:
-    have_gdcm = False
-    raise
-can_use_gdcm = have_gdcm and have_numpy
+    HAVE_GDCM = False
+
+import pydicom
+from pydicom import compat
 
 
+HANDLER_NAME = 'GDCM'
+
+DEPENDENCIES = {
+    'numpy': ('http://www.numpy.org/', 'NumPy'),
+    'gdcm': ('http://gdcm.sourceforge.net/wiki/index.php/Main_Page', 'GDCM'),
+}
+
+SUPPORTED_TRANSFER_SYNTAXES = [
+    pydicom.uid.JPEGBaseline,
+    pydicom.uid.JPEGExtended,
+    pydicom.uid.JPEGLosslessP14,
+    pydicom.uid.JPEGLossless,
+    pydicom.uid.JPEGLSLossless,
+    pydicom.uid.JPEGLSLossy,
+    pydicom.uid.JPEG2000Lossless,
+    pydicom.uid.JPEG2000,
+]
+
 should_convert_these_syntaxes_to_RGB = [
     pydicom.uid.JPEGBaseline, ]
 
 
+def is_available():
+    """Return True if the handler has its dependencies met."""
+    return HAVE_NP and HAVE_GDCM
+
+
+
 def needs_to_convert_to_RGB(dicom_dataset):
     should_convert = (dicom_dataset.file_meta.TransferSyntaxUID in
                       should_convert_these_syntaxes_to_RGB)
@@ -38,7 +61,7 @@ def should_change_PhotometricInterpretation_to_RGB(dicom_dataset):
     return False
 
 
-def supports_transfer_syntax(dicom_dataset):
+def supports_transfer_syntax(transfer_syntax):
     """
     Returns
     -------
@@ -48,7 +71,7 @@ def supports_transfer_syntax(dicom_dataset):
         False to prevent any attempt to try to use this handler
         to decode the given transfer syntax
     """
-    return True
+    return transfer_syntax in SUPPORTED_TRANSFER_SYNTAXES
 
 
 def get_pixeldata(dicom_dataset):
@@ -79,7 +102,7 @@ def get_pixeldata(dicom_dataset):
     # FIXME this should just use dicom_dataset.PixelData
     # instead of dicom_dataset.filename
     #       but it is unclear how this should be achieved using GDCM
-    if not can_use_gdcm:
+    if not HAVE_GDCM:
         msg = ("GDCM requires both the gdcm package and numpy "
                "and one or more could not be imported")
         raise ImportError(msg)
diff --git a/pydicom/pixel_data_handlers/jpeg_ls_handler.py b/pydicom/pixel_data_handlers/jpeg_ls_handler.py
index 33c512aed..29c919dcc 100644
--- a/pydicom/pixel_data_handlers/jpeg_ls_handler.py
+++ b/pydicom/pixel_data_handlers/jpeg_ls_handler.py
@@ -1,33 +1,43 @@
 # Copyright 2008-2018 pydicom authors. See LICENSE file for details.
 """
-Use the jpeg_ls (CharPyLS) python package
-to decode pixel transfer syntaxes.
+Use the jpeg_ls (CharPyLS) python package to decode pixel transfer syntaxes.
 """
 
-import pydicom
-import pydicom.uid
-from pydicom.pixel_data_handlers.util import dtype_corrected_for_endianness
-
-have_numpy = True
 try:
     import numpy
+    HAVE_NP = True
 except ImportError:
-    have_numpy = False
-    raise
+    HAVE_NP = False
 
-have_jpeg_ls = True
 try:
     import jpeg_ls
+    HAVE_JPEGLS = True
 except ImportError:
-    have_jpeg_ls = False
-    raise
+    HAVE_JPEGLS = False
 
-JPEGLSSupportedTransferSyntaxes = [
+import pydicom
+from pydicom.pixel_data_handlers.util import dtype_corrected_for_endianness
+import pydicom.uid
+
+
+HANDLER_NAME = 'JPEG-LS'
+
+DEPENDENCIES = {
+    'numpy': ('http://www.numpy.org/', 'NumPy'),
+    'jpeg_ls': ('https://github.com/Who8MyLunch/CharPyLS', 'CharPyLS'),
+}
+
+SUPPORTED_TRANSFER_SYNTAXES = [
     pydicom.uid.JPEGLSLossless,
     pydicom.uid.JPEGLSLossy,
 ]
 
 
+def is_available():
+    """Return True if the handler has its dependencies met."""
+    return HAVE_NP and HAVE_JPEGLS
+
+
 def needs_to_convert_to_RGB(dicom_dataset):
     return False
 
@@ -37,7 +47,7 @@ def should_change_PhotometricInterpretation_to_RGB(dicom_dataset):
     return False
 
 
-def supports_transfer_syntax(dicom_dataset):
+def supports_transfer_syntax(transfer_syntax):
     """
     Returns
     -------
@@ -47,8 +57,7 @@ def supports_transfer_syntax(dicom_dataset):
         False to prevent any attempt to try to use this handler
         to decode the given transfer syntax
     """
-    return (dicom_dataset.file_meta.TransferSyntaxUID
-            in JPEGLSSupportedTransferSyntaxes)
+    return transfer_syntax in SUPPORTED_TRANSFER_SYNTAXES
 
 
 def get_pixeldata(dicom_dataset):
@@ -74,13 +83,13 @@ def get_pixeldata(dicom_dataset):
         if the pixel data type is unsupported
     """
     if (dicom_dataset.file_meta.TransferSyntaxUID
-            not in JPEGLSSupportedTransferSyntaxes):
+            not in SUPPORTED_TRANSFER_SYNTAXES):
         msg = ("The jpeg_ls does not support "
                "this transfer syntax {0}.".format(
                    dicom_dataset.file_meta.TransferSyntaxUID.name))
         raise NotImplementedError(msg)
 
-    if not have_jpeg_ls:
+    if not HAVE_JPEGLS:
         msg = ("The jpeg_ls package is required to use pixel_array "
                "for this transfer syntax {0}, and jpeg_ls could not "
                "be imported.".format(
diff --git a/pydicom/pixel_data_handlers/numpy_handler.py b/pydicom/pixel_data_handlers/numpy_handler.py
index ecfe1970d..10508aa5f 100644
--- a/pydicom/pixel_data_handlers/numpy_handler.py
+++ b/pydicom/pixel_data_handlers/numpy_handler.py
@@ -39,29 +39,45 @@ elements have values given in the table below.
 from platform import python_implementation
 from sys import byteorder
 
-import numpy as np
+try:
+    import numpy as np
+    HAVE_NP = True
+except ImportError:
+    HAVE_NP = False
 
 from pydicom.compat import in_py2 as IN_PYTHON2
 from pydicom.pixel_data_handlers.util import pixel_dtype
-from pydicom.uid import (
-    ExplicitVRLittleEndian,
-    ImplicitVRLittleEndian,
-    DeflatedExplicitVRLittleEndian,
-    ExplicitVRBigEndian,
-)
+import pydicom.uid
 
+HANDLER_NAME = 'Numpy'
+
+DEPENDENCIES = {
+    'numpy': ('http://www.numpy.org/', 'NumPy'),
+}
 
 SUPPORTED_TRANSFER_SYNTAXES = [
-    ExplicitVRLittleEndian,
-    ImplicitVRLittleEndian,
-    DeflatedExplicitVRLittleEndian,
-    ExplicitVRBigEndian,
+    pydicom.uid.ExplicitVRLittleEndian,
+    pydicom.uid.ImplicitVRLittleEndian,
+    pydicom.uid.DeflatedExplicitVRLittleEndian,
+    pydicom.uid.ExplicitVRBigEndian,
 ]
 
 
-def supports_transfer_syntax(ds):
-    """Return True if the handler supports the transfer syntax used in `ds`."""
-    return ds.file_meta.TransferSyntaxUID in SUPPORTED_TRANSFER_SYNTAXES
+def is_available():
+    """Return True if the handler has its dependencies met."""
+    return HAVE_NP
+
+
+def supports_transfer_syntax(transfer_syntax):
+    """Return True if the handler supports the `transfer_syntax`.
+
+    Parameters
+    ----------
+    transfer_syntax : UID
+        The Transfer Syntax UID of the Pixel Data that is to be used with
+        the handler.
+    """
+    return transfer_syntax in SUPPORTED_TRANSFER_SYNTAXES
 
 
 def needs_to_convert_to_RGB(ds):
diff --git a/pydicom/pixel_data_handlers/pillow_handler.py b/pydicom/pixel_data_handlers/pillow_handler.py
index 78f716624..ba78c1b71 100644
--- a/pydicom/pixel_data_handlers/pillow_handler.py
+++ b/pydicom/pixel_data_handlers/pillow_handler.py
@@ -2,25 +2,34 @@
 """Use the pillow python package to decode pixel transfer syntaxes."""
 
 import io
-import pydicom.encaps
-import pydicom.uid
 import logging
 
-from pydicom.pixel_data_handlers.util import dtype_corrected_for_endianness
-
-have_numpy = True
-logger = logging.getLogger('pydicom')
 try:
     import numpy
+    HAVE_NP = True
+except ImportError:
+    HAVE_NP = False
+
+try:
+    from PIL import Image
+    HAVE_PIL = True
 except ImportError:
-    have_numpy = False
-    raise
+    HAVE_PIL = False
 
-have_pillow = True
 try:
-    from PIL import Image as PILImg
+    from PIL import _imaging
+    HAVE_JPEG = getattr(_imaging, "jpeg_decoder", False)
+    HAVE_JPEG2K = getattr(_imaging, "jpeg2k_decoder", False)
 except ImportError:
-    have_pillow = False
+    HAVE_JPEG = False
+    HAVE_JPEG2K = False
+
+import pydicom.encaps
+from pydicom.pixel_data_handlers.util import dtype_corrected_for_endianness
+import pydicom.uid
+
+
+logger = logging.getLogger('pydicom')
 
 PillowSupportedTransferSyntaxes = [
     pydicom.uid.JPEGBaseline,
@@ -36,17 +45,20 @@ PillowJPEGTransferSyntaxes = [
     pydicom.uid.JPEGExtended,
 ]
 
-have_pillow_jpeg_plugin = False
-have_pillow_jpeg2000_plugin = False
-try:
-    from PIL import _imaging as pillow_core
-    have_pillow_jpeg_plugin = hasattr(pillow_core, "jpeg_decoder")
-    have_pillow_jpeg2000_plugin = hasattr(pillow_core, "jpeg2k_decoder")
-except Exception:
-    pass
+HANDLER_NAME = 'Pillow'
+
+DEPENDENCIES = {
+    'numpy': ('http://www.numpy.org/', 'NumPy'),
+    'PIL': ('https://python-pillow.org/', 'Pillow'),
+}
+
 
+def is_available():
+    """Return True if the handler has its dependencies met."""
+    return HAVE_NP and HAVE_PIL
 
-def supports_transfer_syntax(dicom_dataset):
+
+def supports_transfer_syntax(transfer_syntax):
     """
     Returns
     -------
@@ -56,15 +68,7 @@ def supports_transfer_syntax(dicom_dataset):
         False to prevent any attempt to try to use this handler
         to decode the given transfer syntax
     """
-    if (have_pillow_jpeg_plugin and
-            (dicom_dataset.file_meta.TransferSyntaxUID in
-             PillowJPEGTransferSyntaxes)):
-        return True
-    if (have_pillow_jpeg2000_plugin and
-            (dicom_dataset.file_meta.TransferSyntaxUID in
-             PillowJPEG2000TransferSyntaxes)):
-        return True
-    return False
+    return transfer_syntax in PillowSupportedTransferSyntaxes
 
 
 def needs_to_convert_to_RGB(dicom_dataset):
@@ -96,32 +100,30 @@ def get_pixeldata(dicom_dataset):
         if the pixel data type is unsupported
     """
     logger.debug("Trying to use Pillow to read pixel array "
-                 "(has pillow = %s)", have_pillow)
-    if not have_pillow:
+                 "(has pillow = %s)", HAVE_PIL)
+    transfer_syntax = dicom_dataset.file_meta.TransferSyntaxUID
+    if not HAVE_PIL:
         msg = ("The pillow package is required to use pixel_array for "
                "this transfer syntax {0}, and pillow could not be "
-               "imported.".format(
-                   dicom_dataset.file_meta.TransferSyntaxUID.name))
+               "imported.".format(transfer_syntax.name))
         raise ImportError(msg)
-    if (not have_pillow_jpeg_plugin and
-            dicom_dataset.file_meta.TransferSyntaxUID in
-            PillowJPEGTransferSyntaxes):
+
+    if not HAVE_JPEG and transfer_syntax in PillowJPEGTransferSyntaxes:
         msg = ("this transfer syntax {0}, can not be read because "
-               "Pillow lacks the jpeg decoder plugin".format(
-                   dicom_dataset.file_meta.TransferSyntaxUID.name))
+               "Pillow lacks the jpeg decoder plugin"
+               .format(transfer_syntax.name))
         raise NotImplementedError(msg)
-    if (not have_pillow_jpeg2000_plugin and
-            dicom_dataset.file_meta.TransferSyntaxUID in
-            PillowJPEG2000TransferSyntaxes):
+
+    if not HAVE_JPEG2K and transfer_syntax in PillowJPEG2000TransferSyntaxes:
         msg = ("this transfer syntax {0}, can not be read because "
-               "Pillow lacks the jpeg 2000 decoder plugin".format(
-                   dicom_dataset.file_meta.TransferSyntaxUID.name))
+               "Pillow lacks the jpeg 2000 decoder plugin"
+               .format(transfer_syntax.name))
         raise NotImplementedError(msg)
-    if (dicom_dataset.file_meta.TransferSyntaxUID not in
-            PillowSupportedTransferSyntaxes):
+
+    if transfer_syntax not in PillowSupportedTransferSyntaxes:
         msg = ("this transfer syntax {0}, can not be read because "
-               "Pillow does not support this syntax".format(
-                   dicom_dataset.file_meta.TransferSyntaxUID.name))
+               "Pillow does not support this syntax"
+               .format(transfer_syntax.name))
         raise NotImplementedError(msg)
 
     # Make NumPy format code, e.g. "uint16", "int32" etc
@@ -149,16 +151,14 @@ def get_pixeldata(dicom_dataset):
         dicom_dataset.is_little_endian, numpy_format)
 
     # decompress here
-    if (dicom_dataset.file_meta.TransferSyntaxUID in
-            PillowJPEGTransferSyntaxes):
+    if transfer_syntax in PillowJPEGTransferSyntaxes:
         logger.debug("This is a JPEG lossy format")
         if dicom_dataset.BitsAllocated > 8:
             raise NotImplementedError("JPEG Lossy only supported if "
                                       "Bits Allocated = 8")
         generic_jpeg_file_header = b''
         frame_start_from = 0
-    elif (dicom_dataset.file_meta.TransferSyntaxUID in
-          PillowJPEG2000TransferSyntaxes):
+    elif transfer_syntax in PillowJPEG2000TransferSyntaxes:
         logger.debug("This is a JPEG 2000 format")
         generic_jpeg_file_header = b''
         # generic_jpeg_file_header = b'\x00\x00\x00\x0C\x6A'
@@ -182,7 +182,7 @@ def get_pixeldata(dicom_dataset):
                     frame[frame_start_from:]
                 fio = io.BytesIO(data)
                 try:
-                    decompressed_image = PILImg.open(fio)
+                    decompressed_image = Image.open(fio)
                 except IOError as e:
                     raise NotImplementedError(e.strerror)
                 UncompressedPixelData.extend(decompressed_image.tobytes())
@@ -194,7 +194,7 @@ def get_pixeldata(dicom_dataset):
                 UncompressedPixelData[frame_start_from:]
             try:
                 fio = io.BytesIO(UncompressedPixelData)
-                decompressed_image = PILImg.open(fio)
+                decompressed_image = Image.open(fio)
             except IOError as e:
                 raise NotImplementedError(e.strerror)
             UncompressedPixelData = decompressed_image.tobytes()
@@ -205,7 +205,7 @@ def get_pixeldata(dicom_dataset):
         len(UncompressedPixelData))
     pixel_array = numpy.copy(
         numpy.frombuffer(UncompressedPixelData, numpy_format))
-    if (dicom_dataset.file_meta.TransferSyntaxUID in
+    if (transfer_syntax in
             PillowJPEG2000TransferSyntaxes and
             dicom_dataset.BitsStored == 16):
         # WHY IS THIS EVEN NECESSARY??
diff --git a/pydicom/pixel_data_handlers/rle_handler.py b/pydicom/pixel_data_handlers/rle_handler.py
index d1617f52f..7cd81fc49 100644
--- a/pydicom/pixel_data_handlers/rle_handler.py
+++ b/pydicom/pixel_data_handlers/rle_handler.py
@@ -35,21 +35,36 @@ elements have values given in the table below.
 
 from struct import unpack
 
-import numpy as np
+try:
+    import numpy as np
+    HAVE_RLE = True
+except ImportError:
+    HAVE_RLE = False
 
 from pydicom.encaps import decode_data_sequence, defragment_data
 from pydicom.pixel_data_handlers.util import pixel_dtype
-from pydicom.uid import RLELossless
+import pydicom.uid
 
 
+HANDLER_NAME = 'RLE Lossless'
+
+DEPENDENCIES = {
+    'numpy': ('http://www.numpy.org/', 'NumPy'),
+}
+
 SUPPORTED_TRANSFER_SYNTAXES = [
-    RLELossless
+    pydicom.uid.RLELossless
 ]
 
 
-def supports_transfer_syntax(ds):
-    """Return True if the handler supports the transfer syntax used in `ds`."""
-    return ds.file_meta.TransferSyntaxUID in SUPPORTED_TRANSFER_SYNTAXES
+def is_available():
+    """Return True if the handler has its dependencies met."""
+    return HAVE_RLE
+
+
+def supports_transfer_syntax(transfer_syntax):
+    """Return True if the handler supports the `transfer_syntax`."""
+    return transfer_syntax in SUPPORTED_TRANSFER_SYNTAXES
 
 
 def needs_to_convert_to_RGB(ds):


Test patch:
diff --git a/pydicom/tests/test_JPEG_LS_transfer_syntax.py b/pydicom/tests/test_JPEG_LS_transfer_syntax.py
index d0f7bed47..b9c821043 100644
--- a/pydicom/tests/test_JPEG_LS_transfer_syntax.py
+++ b/pydicom/tests/test_JPEG_LS_transfer_syntax.py
@@ -16,32 +16,39 @@ numpy_missing_message = ("numpy is not available "
                          "in this test environment")
 jpeg_ls_missing_message = ("jpeg_ls is not available "
                            "in this test environment")
-pillow_handler = None
-numpy_handler = None
-gdcm_handler = None
-jpeg_ls_handler = None
 
-have_pillow_jpeg_plugin = False
-have_pillow_jpeg2000_plugin = False
+
 try:
     import pydicom.pixel_data_handlers.numpy_handler as numpy_handler
+    HAVE_NP = numpy_handler.HAVE_NP
 except ImportError:
-    have_numpy_handler = False
+    HAVE_NP = False
+    numpy_handler = None
+
 try:
     import pydicom.pixel_data_handlers.pillow_handler as pillow_handler
-    have_pillow_jpeg_plugin = pillow_handler.have_pillow_jpeg_plugin
-    have_pillow_jpeg2000_plugin = \
-        pillow_handler.have_pillow_jpeg2000_plugin
+    HAVE_PIL = pillow_handler.HAVE_PIL
+    HAVE_JPEG = pillow_handler.HAVE_JPEG
+    HAVE_JPEG2K = pillow_handler.HAVE_JPEG2K
 except ImportError:
-    have_pillow_handler = False
+    HAVE_PIL = False
+    pillow_handler = None
+    HAVE_JPEG = False
+    HAVE_JPEG2K = False
+
 try:
     import pydicom.pixel_data_handlers.jpeg_ls_handler as jpeg_ls_handler
+    HAVE_JPEGLS = jpeg_ls_handler.HAVE_JPEGLS
 except ImportError:
     jpeg_ls_handler = None
+    HAVE_JPEGLS = False
+
 try:
     import pydicom.pixel_data_handlers.gdcm_handler as gdcm_handler
+    HAVE_GDCM = gdcm_handler.HAVE_GDCM
 except ImportError:
     gdcm_handler = None
+    HAVE_GDCM = False
 
 mr_name = get_testdata_files("MR_small.dcm")[0]
 jpeg_ls_lossless_name = get_testdata_files("MR_small_jpeg_ls_lossless.dcm")[0]
@@ -58,89 +65,85 @@ class Test_JPEG_LS_Lossless_transfer_syntax():
         self.mr_small = dcmread(mr_name)
         self.emri_jpeg_ls_lossless = dcmread(emri_jpeg_ls_lossless)
         self.emri_small = dcmread(emri_name)
-        self.original_handlers = pydicom.config.image_handlers
+        self.original_handlers = pydicom.config.pixel_data_handlers
 
     def teardown_method(self, method):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
-    @pytest.mark.skipif(numpy_handler is None, reason=numpy_missing_message)
+    @pytest.mark.skipif(not HAVE_NP, reason=numpy_missing_message)
     def test_read_mr_with_numpy(self):
-        pydicom.config.image_handlers = [numpy_handler]
+        pydicom.config.pixel_data_handlers = [numpy_handler]
         msg = (
             r"Unable to decode pixel data with a transfer syntax UID of "
             r"'1.2.840.10008.1.2.4.80' \(JPEG-LS Lossless Image Compression\) "
-            r"as there are no suitable pixel data handlers available."
+            r"as there are no pixel data handlers available."
         )
         with pytest.raises(NotImplementedError, match=msg):
             self.jpeg_ls_lossless.pixel_array
 
-    @pytest.mark.skipif(numpy_handler is None, reason=numpy_missing_message)
+    @pytest.mark.skipif(not HAVE_NP, reason=numpy_missing_message)
     def test_read_emri_with_numpy(self):
-        pydicom.config.image_handlers = [numpy_handler]
+        pydicom.config.pixel_data_handlers = [numpy_handler]
         msg = (
             r"Unable to decode pixel data with a transfer syntax UID of "
             r"'1.2.840.10008.1.2.4.80' \(JPEG-LS Lossless Image Compression\) "
-            r"as there are no suitable pixel data handlers available."
+            r"as there are no pixel data handlers available."
         )
         with pytest.raises(NotImplementedError, match=msg):
             self.emri_jpeg_ls_lossless.pixel_array
 
-    @pytest.mark.skipif(pillow_handler is None, reason=pillow_missing_message)
+    @pytest.mark.skipif(not HAVE_PIL, reason=pillow_missing_message)
     def test_read_mr_with_pillow(self):
-        pydicom.config.image_handlers = [pillow_handler]
+        pydicom.config.pixel_data_handlers = [pillow_handler]
         msg = (
             r"Unable to decode pixel data with a transfer syntax UID of "
             r"'1.2.840.10008.1.2.4.80' \(JPEG-LS Lossless Image Compression\) "
-            r"as there are no suitable pixel data handlers available."
+            r"as there are no pixel data handlers available."
         )
         with pytest.raises(NotImplementedError, match=msg):
             self.jpeg_ls_lossless.pixel_array
 
-    @pytest.mark.skipif(pillow_handler is None, reason=pillow_missing_message)
+    @pytest.mark.skipif(not HAVE_PIL, reason=pillow_missing_message)
     def test_read_emri_with_pillow(self):
-        pydicom.config.image_handlers = [pillow_handler]
+        pydicom.config.pixel_data_handlers = [pillow_handler]
         msg = (
             r"Unable to decode pixel data with a transfer syntax UID of "
             r"'1.2.840.10008.1.2.4.80' \(JPEG-LS Lossless Image Compression\) "
-            r"as there are no suitable pixel data handlers available."
+            r"as there are no pixel data handlers available."
         )
         with pytest.raises(NotImplementedError, match=msg):
             self.emri_jpeg_ls_lossless.pixel_array
 
-    @pytest.mark.skipif(gdcm_handler is None, reason=gdcm_missing_message)
+    @pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
     def test_read_mr_with_gdcm(self):
-        pydicom.config.image_handlers = [numpy_handler, gdcm_handler]
+        pydicom.config.pixel_data_handlers = [numpy_handler, gdcm_handler]
         a = self.jpeg_ls_lossless.pixel_array
         b = self.mr_small.pixel_array
         assert a.mean() == b.mean(), \
             "using GDCM Decoded pixel data is not " \
             "all {0} (mean == {1})".format(b.mean(), a.mean())
 
-    @pytest.mark.skipif(gdcm_handler is None, reason=gdcm_missing_message)
+    @pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
     def test_read_emri_with_gdcm(self):
-        pydicom.config.image_handlers = [numpy_handler, gdcm_handler]
+        pydicom.config.pixel_data_handlers = [numpy_handler, gdcm_handler]
         a = self.emri_jpeg_ls_lossless.pixel_array
         b = self.emri_small.pixel_array
         assert a.mean() == b.mean(), \
             "using GDCM Decoded pixel data is not " \
             "all {0} (mean == {1})".format(b.mean(), a.mean())
 
-    @pytest.mark.skipif(
-        jpeg_ls_handler is None,
-        reason=jpeg_ls_missing_message)
+    @pytest.mark.skipif(not HAVE_JPEGLS, reason=jpeg_ls_missing_message)
     def test_read_mr_with_jpeg_ls(self):
-        pydicom.config.image_handlers = [numpy_handler, jpeg_ls_handler]
+        pydicom.config.pixel_data_handlers = [numpy_handler, jpeg_ls_handler]
         a = self.jpeg_ls_lossless.pixel_array
         b = self.mr_small.pixel_array
         assert a.mean() == b.mean(), \
             "using jpeg_ls decoded pixel data is not " \
             "all {0} (mean == {1})".format(b.mean(), a.mean())
 
-    @pytest.mark.skipif(
-        jpeg_ls_handler is None,
-        reason=jpeg_ls_missing_message)
+    @pytest.mark.skipif(not HAVE_JPEGLS, reason=jpeg_ls_missing_message)
     def test_read_emri_with_jpeg_ls(self):
-        pydicom.config.image_handlers = [numpy_handler, jpeg_ls_handler]
+        pydicom.config.pixel_data_handlers = [numpy_handler, jpeg_ls_handler]
         a = self.emri_jpeg_ls_lossless.pixel_array
         b = self.emri_small.pixel_array
         assert a.mean() == b.mean(), \
@@ -148,21 +151,21 @@ class Test_JPEG_LS_Lossless_transfer_syntax():
             "(mean == {1})".format(b.mean(), a.mean())
 
     def test_read_mr_without_any_handler(self):
-        pydicom.config.image_handlers = []
+        pydicom.config.pixel_data_handlers = []
         msg = (
             r"Unable to decode pixel data with a transfer syntax UID of "
             r"'1.2.840.10008.1.2.4.80' \(JPEG-LS Lossless Image Compression\) "
-            r"as there are no suitable pixel data handlers available."
+            r"as there are no pixel data handlers available."
         )
         with pytest.raises(NotImplementedError, match=msg):
             self.jpeg_ls_lossless.pixel_array
 
     def test_read_emri_without_any_handler(self):
-        pydicom.config.image_handlers = []
+        pydicom.config.pixel_data_handlers = []
         msg = (
             r"Unable to decode pixel data with a transfer syntax UID of "
             r"'1.2.840.10008.1.2.4.80' \(JPEG-LS Lossless Image Compression\) "
-            r"as there are no suitable pixel data handlers available."
+            r"as there are no pixel data handlers available."
         )
         with pytest.raises(NotImplementedError, match=msg):
             self.emri_jpeg_ls_lossless.pixel_array
diff --git a/pydicom/tests/test_dataset.py b/pydicom/tests/test_dataset.py
index ae2c0ea4c..b2e200bf3 100644
--- a/pydicom/tests/test_dataset.py
+++ b/pydicom/tests/test_dataset.py
@@ -1018,16 +1018,29 @@ class DatasetTests(unittest.TestCase):
         """Test that we try to get new pixel data if the id has changed."""
         fpath = get_testdata_files("CT_small.dcm")[0]
         ds = dcmread(fpath)
+        ds.file_meta.TransferSyntaxUID = '1.2.3.4'
         ds._pixel_id = 1234
         assert ds._pixel_id != id(ds.PixelData)
         ds._pixel_array = 'Test Value'
         # If _pixel_id doesn't match then attempt to get new pixel data
-        orig_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = []
+        orig_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
         with pytest.raises(NotImplementedError):
             ds.convert_pixel_data()
 
-        pydicom.config.image_handlers = orig_handlers
+        pydicom.config.pixel_data_handlers = orig_handlers
+
+    def test_pixel_array_unknown_syntax(self):
+        """Test that pixel_array for an unknown syntax raises exception."""
+        ds = dcmread(get_testdata_files("CT_small.dcm")[0])
+        ds.file_meta.TransferSyntaxUID = '1.2.3.4'
+        msg = (
+            r"Unable to decode pixel data with a transfer syntax UID of "
+            r"'1.2.3.4' \(1.2.3.4\) as there are no pixel data handlers "
+            r"available that support it"
+        )
+        with pytest.raises(NotImplementedError, match=msg):
+            ds.pixel_array
 
     def test_formatted_lines(self):
         """Test Dataset.formatted_lines"""
diff --git a/pydicom/tests/test_gdcm_pixel_data.py b/pydicom/tests/test_gdcm_pixel_data.py
index fbacd869c..5c87f7e52 100644
--- a/pydicom/tests/test_gdcm_pixel_data.py
+++ b/pydicom/tests/test_gdcm_pixel_data.py
@@ -28,19 +28,20 @@ try:
 except AttributeError:
     have_pytest_param = False
 
-gdcm_handler = None
-have_gdcm_handler = True
 try:
     import pydicom.pixel_data_handlers.gdcm_handler as gdcm_handler
+    HAVE_GDCM = gdcm_handler.HAVE_GDCM
 except ImportError as e:
-    have_gdcm_handler = False
-numpy_handler = None
-have_numpy_handler = True
+    HAVE_GDCM = False
+    gdcm_handler = None
+
 try:
     import pydicom.pixel_data_handlers.numpy_handler as numpy_handler
+    HAVE_NP = numpy_handler.HAVE_NP
 except ImportError:
-    have_numpy_handler = False
-test_gdcm_decoder = have_gdcm_handler
+    HAVE_NP = False
+    numpy_handler = None
+
 
 empty_number_tags_name = get_testdata_files(
     "reportsi_with_empty_number_tags.dcm")[0]
@@ -117,11 +118,11 @@ class GDCM_JPEG_LS_Tests_no_gdcm(unittest.TestCase):
         self.mr_small = dcmread(mr_name)
         self.emri_jpeg_ls_lossless = dcmread(emri_jpeg_ls_lossless)
         self.emri_small = dcmread(emri_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
         os.remove(self.unicode_filename)
 
     def test_JPEG_LS_PixelArray(self):
@@ -141,11 +142,11 @@ class GDCM_JPEG2000Tests_no_gdcm(unittest.TestCase):
         self.emri_jpeg_2k_lossless = dcmread(emri_jpeg_2k_lossless)
         self.emri_small = dcmread(emri_name)
         self.sc_rgb_jpeg2k_gdcm_KY = dcmread(sc_rgb_jpeg2k_gdcm_KY)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEG2000(self):
         """JPEG2000: Returns correct values for sample data elements"""
@@ -184,11 +185,11 @@ class GDCM_JPEGlossyTests_no_gdcm(unittest.TestCase):
     def setUp(self):
         self.jpeg_lossy = dcmread(jpeg_lossy_name)
         self.color_3d_jpeg = dcmread(color_3d_jpeg_baseline)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEGlossy(self):
         """JPEG-lossy: Returns correct values for sample data elements"""
@@ -212,11 +213,11 @@ class GDCM_JPEGlossyTests_no_gdcm(unittest.TestCase):
 class GDCM_JPEGlosslessTests_no_gdcm(unittest.TestCase):
     def setUp(self):
         self.jpeg_lossless = dcmread(jpeg_lossless_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless(self):
         """JPEGlossless: Returns correct values for sample data elements"""
@@ -237,7 +238,7 @@ class GDCM_JPEGlosslessTests_no_gdcm(unittest.TestCase):
             _ = self.jpeg_lossless.pixel_array
 
 
-@pytest.mark.skipif(not test_gdcm_decoder, reason=gdcm_missing_message)
+@pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
 class GDCM_JPEG_LS_Tests_with_gdcm(unittest.TestCase):
     def setUp(self):
         if compat.in_py2:
@@ -254,11 +255,11 @@ class GDCM_JPEG_LS_Tests_with_gdcm(unittest.TestCase):
         self.mr_small = dcmread(mr_name)
         self.emri_jpeg_ls_lossless = dcmread(emri_jpeg_ls_lossless)
         self.emri_small = dcmread(emri_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [numpy_handler, gdcm_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler, gdcm_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
         os.remove(self.unicode_filename)
 
     def test_JPEG_LS_PixelArray(self):
@@ -280,7 +281,7 @@ class GDCM_JPEG_LS_Tests_with_gdcm(unittest.TestCase):
             "(mean == {1})".format(b.mean(), a.mean()))
 
 
-@pytest.mark.skipif(not test_gdcm_decoder, reason=gdcm_missing_message)
+@pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
 class GDCM_JPEG2000Tests_with_gdcm(unittest.TestCase):
     def setUp(self):
         self.jpeg_2k = dcmread(jpeg2000_name)
@@ -291,11 +292,11 @@ class GDCM_JPEG2000Tests_with_gdcm(unittest.TestCase):
         self.sc_rgb_jpeg2k_gdcm_KY = dcmread(sc_rgb_jpeg2k_gdcm_KY)
         self.ground_truth_sc_rgb_jpeg2k_gdcm_KY_gdcm = dcmread(
             ground_truth_sc_rgb_jpeg2k_gdcm_KY_gdcm)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [numpy_handler, gdcm_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler, gdcm_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEG2000(self):
         """JPEG2000: Returns correct values for sample data elements"""
@@ -347,17 +348,17 @@ class GDCM_JPEG2000Tests_with_gdcm(unittest.TestCase):
                 "(mean == {1})".format(b.mean(), a.mean()))
 
 
-@pytest.mark.skipif(not test_gdcm_decoder, reason=gdcm_missing_message)
+@pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
 class GDCM_JPEGlossyTests_with_gdcm(unittest.TestCase):
 
     def setUp(self):
         self.jpeg_lossy = dcmread(jpeg_lossy_name)
         self.color_3d_jpeg = dcmread(color_3d_jpeg_baseline)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [numpy_handler, gdcm_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler, gdcm_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless_odd_data_size(self):
         test_file = get_testdata_files('SC_rgb_small_odd_jpeg.dcm')[0]
@@ -400,10 +401,10 @@ class GDCM_JPEGlossyTests_with_gdcm(unittest.TestCase):
 
 @pytest.fixture(scope="module")
 def test_with_gdcm():
-    original_handlers = pydicom.config.image_handlers
-    pydicom.config.image_handlers = [numpy_handler, gdcm_handler]
+    original_handlers = pydicom.config.pixel_data_handlers
+    pydicom.config.pixel_data_handlers = [numpy_handler, gdcm_handler]
     yield original_handlers
-    pydicom.config.image_handlers = original_handlers
+    pydicom.config.pixel_data_handlers = original_handlers
 
 
 if have_pytest_param:
@@ -552,7 +553,7 @@ else:
 
 
 @pytest.mark.skipif(
-    not test_gdcm_decoder,
+    not HAVE_GDCM,
     reason=gdcm_missing_message)
 @pytest.mark.parametrize(
     "image,PhotometricInterpretation,results,convert_yuv_to_rgb",
@@ -583,15 +584,15 @@ def test_PI_RGB(test_with_gdcm,
     assert t.PhotometricInterpretation == PhotometricInterpretation
 
 
-@pytest.mark.skipif(not test_gdcm_decoder, reason=gdcm_missing_message)
+@pytest.mark.skipif(not HAVE_GDCM, reason=gdcm_missing_message)
 class GDCM_JPEGlosslessTests_with_gdcm(unittest.TestCase):
     def setUp(self):
         self.jpeg_lossless = dcmread(jpeg_lossless_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [numpy_handler, gdcm_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler, gdcm_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless(self):
         """JPEGlossless: Returns correct values for sample data elements"""
diff --git a/pydicom/tests/test_jpeg_ls_pixel_data.py b/pydicom/tests/test_jpeg_ls_pixel_data.py
index f23d1eae9..512acf1a5 100644
--- a/pydicom/tests/test_jpeg_ls_pixel_data.py
+++ b/pydicom/tests/test_jpeg_ls_pixel_data.py
@@ -10,18 +10,20 @@ from pydicom.data import get_testdata_files
 jpeg_ls_missing_message = ("jpeg_ls is not available "
                            "in this test environment")
 jpeg_ls_present_message = "jpeg_ls is being tested"
-jpeg_ls_handler = None
-have_jpeg_ls_handler = True
-numpy_handler = None
-have_numpy_handler = True
+
 try:
     import pydicom.pixel_data_handlers.numpy_handler as numpy_handler
+    have_numpy_handler = numpy_handler.HAVE_NP
 except ImportError:
     have_numpy_handler = False
+    numpy_handler = None
+
 try:
     import pydicom.pixel_data_handlers.jpeg_ls_handler as jpeg_ls_handler
+    have_jpeg_ls_handler = jpeg_ls_handler.HAVE_JPEGLS
 except ImportError:
     have_jpeg_ls_handler = False
+    jpeg_ls_handler = None
 
 test_jpeg_ls_decoder = have_numpy_handler and have_jpeg_ls_handler
 
@@ -73,14 +75,14 @@ class jpeg_ls_JPEG_LS_Tests_no_jpeg_ls(unittest.TestCase):
         self.mr_small = dcmread(mr_name)
         self.emri_jpeg_ls_lossless = dcmread(emri_jpeg_ls_lossless)
         self.emri_small = dcmread(emri_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEG_LS_PixelArray(self):
-        with self.assertRaises((NotImplementedError, )):
+        with self.assertRaises((RuntimeError, NotImplementedError)):
             _ = self.jpeg_ls_lossless.pixel_array
 
 
@@ -91,11 +93,11 @@ class jpeg_ls_JPEG2000Tests_no_jpeg_ls(unittest.TestCase):
         self.mr_small = dcmread(mr_name)
         self.emri_jpeg_2k_lossless = dcmread(emri_jpeg_2k_lossless)
         self.emri_small = dcmread(emri_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEG2000PixelArray(self):
         """JPEG2000: Now works"""
@@ -113,11 +115,11 @@ class jpeg_ls_JPEGlossyTests_no_jpeg_ls(unittest.TestCase):
     def setUp(self):
         self.jpeg_lossy = dcmread(jpeg_lossy_name)
         self.color_3d_jpeg = dcmread(color_3d_jpeg_baseline)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossy(self):
         """JPEG-lossy: Returns correct values for sample data elements"""
@@ -142,11 +144,11 @@ class jpeg_ls_JPEGlossyTests_no_jpeg_ls(unittest.TestCase):
 class jpeg_ls_JPEGlosslessTests_no_jpeg_ls(unittest.TestCase):
     def setUp(self):
         self.jpeg_lossless = dcmread(jpeg_lossless_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless(self):
         """JPEGlossless: Returns correct values for sample data elements"""
@@ -176,11 +178,11 @@ class jpeg_ls_JPEG_LS_Tests_with_jpeg_ls(unittest.TestCase):
         self.mr_small = dcmread(mr_name)
         self.emri_jpeg_ls_lossless = dcmread(emri_jpeg_ls_lossless)
         self.emri_small = dcmread(emri_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [jpeg_ls_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [jpeg_ls_handler, numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_raises_if_endianess_not_set(self):
         self.jpeg_ls_lossless.is_little_endian = None
@@ -216,11 +218,11 @@ class jpeg_ls_JPEG2000Tests_with_jpeg_ls(unittest.TestCase):
         self.mr_small = dcmread(mr_name)
         self.emri_jpeg_2k_lossless = dcmread(emri_jpeg_2k_lossless)
         self.emri_small = dcmread(emri_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [jpeg_ls_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [jpeg_ls_handler, numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEG2000PixelArray(self):
         with self.assertRaises((NotImplementedError, )):
@@ -239,11 +241,11 @@ class jpeg_ls_JPEGlossyTests_with_jpeg_ls(unittest.TestCase):
     def setUp(self):
         self.jpeg_lossy = dcmread(jpeg_lossy_name)
         self.color_3d_jpeg = dcmread(color_3d_jpeg_baseline)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [jpeg_ls_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [jpeg_ls_handler, numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossy(self):
         """JPEG-lossy: Returns correct values for sample data elements"""
@@ -270,11 +272,11 @@ class jpeg_ls_JPEGlossyTests_with_jpeg_ls(unittest.TestCase):
 class jpeg_ls_JPEGlosslessTests_with_jpeg_ls(unittest.TestCase):
     def setUp(self):
         self.jpeg_lossless = dcmread(jpeg_lossless_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [jpeg_ls_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [jpeg_ls_handler, numpy_handler]
 
     def tearDown(self):
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless(self):
         """JPEGlossless: Returns correct values for sample data elements"""
diff --git a/pydicom/tests/test_numpy_pixel_data.py b/pydicom/tests/test_numpy_pixel_data.py
index 1746283f5..09bd11813 100644
--- a/pydicom/tests/test_numpy_pixel_data.py
+++ b/pydicom/tests/test_numpy_pixel_data.py
@@ -3,11 +3,12 @@
 
 There are the following possibilities:
 
-* numpy is not available and the numpy handler is not available
+* numpy is not available and
+  * the numpy handler is not available
+  * the numpy handler is available
 * numpy is available and
-
-  * The numpy handler is not available
-  * The numpy handler is available
+  * the numpy handler is not available
+  * the numpy handler is available
 
 **Supported transfer syntaxes**
 
@@ -45,6 +46,11 @@ from pydicom.uid import (
 
 try:
     import numpy as np
+    HAVE_NP = True
+except ImportError:
+    HAVE_NP = False
+
+try:
     from pydicom.pixel_data_handlers import numpy_handler as NP_HANDLER
     from pydicom.pixel_data_handlers.numpy_handler import (
         get_pixeldata,
@@ -53,9 +59,7 @@ try:
         get_expected_length,
         pixel_dtype,
     )
-    HAVE_NP = True
 except ImportError:
-    HAVE_NP = False
     NP_HANDLER = None
 
 
@@ -174,17 +178,17 @@ class TestNoNumpy_NoNumpyHandler(object):
     """Tests for handling datasets without numpy and the handler."""
     def setup(self):
         """Setup the environment."""
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = []
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def teardown(self):
         """Restore the environment."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_environment(self):
         """Check that the testing environment is as expected."""
         assert not HAVE_NP
-        assert NP_HANDLER is None
+        assert NP_HANDLER is not None
 
     def test_can_access_supported_dataset(self):
         """Test that we can read and access elements in dataset."""
@@ -225,18 +229,88 @@ class TestNoNumpy_NoNumpyHandler(object):
                 ds.pixel_array
 
 
+# Numpy unavailable and the numpy handler is available
+@pytest.mark.skipif(HAVE_NP, reason='Numpy is available')
+class TestNoNumpy_NumpyHandler(object):
+    """Tests for handling datasets without numpy and the handler."""
+    def setup(self):
+        """Setup the environment."""
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [NP_HANDLER]
+
+    def teardown(self):
+        """Restore the environment."""
+        pydicom.config.pixel_data_handlers = self.original_handlers
+
+    def test_environment(self):
+        """Check that the testing environment is as expected."""
+        assert not HAVE_NP
+        assert NP_HANDLER is not None
+
+    def test_can_access_supported_dataset(self):
+        """Test that we can read and access elements in dataset."""
+        # Explicit little
+        ds = dcmread(EXPL_16_1_1F)
+        assert 'CompressedSamples^MR1' == ds.PatientName
+        assert 8192 == len(ds.PixelData)
+
+        # Implicit little
+        ds = dcmread(IMPL_16_1_1F)
+        assert 'CompressedSamples^MR1' == ds.PatientName
+        assert 8192 == len(ds.PixelData)
+
+        # Deflated little
+        ds = dcmread(DEFL_8_1_1F)
+        assert '^^^^' == ds.PatientName
+        assert 262144 == len(ds.PixelData)
+
+        # Explicit big
+        ds = dcmread(EXPB_16_1_1F)
+        assert 'CompressedSamples^MR1' == ds.PatientName
+        assert 8192 == len(ds.PixelData)
+
+    @pytest.mark.parametrize("fpath,data", REFERENCE_DATA_UNSUPPORTED)
+    def test_can_access_unsupported_dataset(self, fpath, data):
+        """Test can read and access elements in unsupported datasets."""
+        ds = dcmread(fpath)
+        assert data[0] == ds.file_meta.TransferSyntaxUID
+        assert data[1] == ds.PatientName
+
+    def test_unsupported_pixel_array_raises(self):
+        """Test pixel_array raises exception for unsupported syntaxes."""
+        ds = dcmread(EXPL_16_1_1F)
+        for uid in UNSUPPORTED_SYNTAXES:
+            ds.file_meta.TransferSyntaxUID = uid
+            with pytest.raises(NotImplementedError,
+                               match="UID of '{}'".format(uid)):
+                ds.pixel_array
+
+    def test_supported_pixel_array_raises(self):
+        """Test pixel_array raises exception for supported syntaxes."""
+        ds = dcmread(EXPL_16_1_1F)
+        for uid in SUPPORTED_SYNTAXES:
+            ds.file_meta.TransferSyntaxUID = uid
+            exc_msg = (
+                r"The following handlers are available to decode the pixel "
+                r"data however they are missing required dependencies: "
+                r"Numpy \(req. NumPy\)"
+            )
+            with pytest.raises(RuntimeError, match=exc_msg):
+                ds.pixel_array
+
+
 # Numpy is available, the numpy handler is unavailable
 @pytest.mark.skipif(not HAVE_NP, reason='Numpy is unavailable')
 class TestNumpy_NoNumpyHandler(object):
     """Tests for handling datasets without the handler."""
     def setup(self):
         """Setup the environment."""
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = []
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def teardown(self):
         """Restore the environment."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_environment(self):
         """Check that the testing environment is as expected."""
@@ -278,8 +352,7 @@ class TestNumpy_NoNumpyHandler(object):
         ds = dcmread(EXPL_16_1_1F)
         for uid in ALL_TRANSFER_SYNTAXES:
             ds.file_meta.TransferSyntaxUID = uid
-            with pytest.raises(NotImplementedError,
-                               match="UID of '{}'".format(uid)):
+            with pytest.raises((NotImplementedError, RuntimeError)):
                 ds.pixel_array
 
 
@@ -328,12 +401,12 @@ class TestNumpy_NumpyHandler(object):
     """Tests for handling Pixel Data with the handler."""
     def setup(self):
         """Setup the test datasets and the environment."""
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [NP_HANDLER]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [NP_HANDLER]
 
     def teardown(self):
         """Restore the environment."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_environment(self):
         """Check that the testing environment is as expected."""
@@ -346,8 +419,7 @@ class TestNumpy_NumpyHandler(object):
 
         for uid in UNSUPPORTED_SYNTAXES:
             ds.file_meta.TransferSyntaxUID = uid
-            with pytest.raises(NotImplementedError,
-                               match="UID of '{0}' ".format(uid)):
+            with pytest.raises((NotImplementedError, RuntimeError)):
                 ds.pixel_array
 
     def test_dataset_pixel_array_handler_needs_convert(self):
diff --git a/pydicom/tests/test_pillow_pixel_data.py b/pydicom/tests/test_pillow_pixel_data.py
index 8a85e6341..25e39cff0 100644
--- a/pydicom/tests/test_pillow_pixel_data.py
+++ b/pydicom/tests/test_pillow_pixel_data.py
@@ -15,32 +15,30 @@ have_pytest_param = hasattr(pytest, 'param')
 
 try:
     from pydicom.pixel_data_handlers import numpy_handler
-    have_numpy_handler = True
+    HAVE_NP = numpy_handler.HAVE_NP
 except ImportError:
-    have_numpy_handler = False
+    HAVE_NP = False
     numpy_handler = None
 
 try:
     from pydicom.pixel_data_handlers import pillow_handler
-    have_pillow_handler = True
-    have_pillow_jpeg_plugin = pillow_handler.have_pillow_jpeg_plugin
-    have_pillow_jpeg2000_plugin = pillow_handler.have_pillow_jpeg2000_plugin
+    HAVE_PIL = pillow_handler.HAVE_PIL
+    HAVE_JPEG = pillow_handler.HAVE_JPEG
+    HAVE_JPEG2K = pillow_handler.HAVE_JPEG2K
     import numpy as np
 except ImportError:
     pillow_handler = None
-    have_pillow_handler = False
-    have_pillow_jpeg_plugin = False
-    have_pillow_jpeg2000_plugin = False
+    HAVE_PIL = False
+    HAVE_JPEG = False
+    HAVE_JPEG2K = False
 
 
 pillow_missing_message = ("pillow is not available "
                           "in this test environment")
 
-test_pillow_decoder = have_numpy_handler and have_pillow_handler
-test_pillow_jpeg_decoder = (test_pillow_decoder and
-                            have_pillow_jpeg_plugin)
-test_pillow_jpeg2000_decoder = (test_pillow_decoder and
-                                have_pillow_jpeg2000_plugin)
+TEST_PIL = HAVE_NP and HAVE_PIL
+TEST_JPEG = TEST_PIL and HAVE_JPEG
+TEST_JPEG2K = TEST_PIL and HAVE_JPEG2K
 
 empty_number_tags_name = get_testdata_files(
     "reportsi_with_empty_number_tags.dcm")[0]
@@ -129,12 +127,12 @@ class Test_JPEGLS_no_pillow(object):
         """Setup the test datasets."""
         self.jpeg_ls_lossless = dcmread(jpeg_ls_lossless_name)
         self.emri_jpeg_ls_lossless = dcmread(emri_jpeg_ls_lossless)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEG_LS_PixelArray(self):
         """Test decoding JPEG LS with only numpy fails."""
@@ -155,12 +153,12 @@ class Test_JPEG2000Tests_no_pillow(object):
         self.jpeg_2k_lossless = dcmread(jpeg2000_lossless_name)
         self.emri_jpeg_2k_lossless = dcmread(emri_jpeg_2k_lossless)
         self.sc_rgb_jpeg2k_gdcm_KY = dcmread(sc_rgb_jpeg2k_gdcm_KY)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEG2000(self):
         """Test reading element values works OK without Pillow."""
@@ -193,12 +191,12 @@ class Test_JPEGlossyTests_no_pillow(object):
         """Setup the test datasets."""
         self.jpeg_lossy = dcmread(jpeg_lossy_name)
         self.color_3d_jpeg = dcmread(color_3d_jpeg_baseline)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossy(self):
         """Test reading element values works OK without Pillow."""
@@ -221,12 +219,12 @@ class Test_JPEGlosslessTests_no_pillow(object):
     def setup(self):
         """Setup the test datasets."""
         self.jpeg_lossless = dcmread(jpeg_lossless_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [None, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless(self):
         """Test reading element values works OK without Pillow."""
@@ -241,7 +239,7 @@ class Test_JPEGlosslessTests_no_pillow(object):
 
 
 @pytest.mark.skipif(
-    not test_pillow_decoder,
+    not TEST_PIL,
     reason=pillow_missing_message)
 class Test_JPEG_LS_with_pillow(object):
     """Tests for decoding JPEG LS if pillow pixel handler is available."""
@@ -249,12 +247,12 @@ class Test_JPEG_LS_with_pillow(object):
         """Setup the test datasets."""
         self.jpeg_ls_lossless = dcmread(jpeg_ls_lossless_name)
         self.emri_jpeg_ls_lossless = dcmread(emri_jpeg_ls_lossless)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [pillow_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [pillow_handler, numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_JPEG_LS_PixelArray(self):
         """Test decoding JPEG LS with pillow handler fails."""
@@ -268,7 +266,7 @@ class Test_JPEG_LS_with_pillow(object):
 
 
 @pytest.mark.skipif(
-    not test_pillow_jpeg2000_decoder,
+    not TEST_JPEG2K,
     reason=pillow_missing_message)
 class Test_JPEG2000Tests_with_pillow(object):
     """Test decoding JPEG2K if pillow JPEG2K plugin is available."""
@@ -282,12 +280,12 @@ class Test_JPEG2000Tests_with_pillow(object):
         self.sc_rgb_jpeg2k_gdcm_KY = dcmread(sc_rgb_jpeg2k_gdcm_KY)
         self.ground_truth_sc_rgb_jpeg2k_gdcm_KY_gdcm = dcmread(
             ground_truth_sc_rgb_jpeg2k_gdcm_KY_gdcm)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [pillow_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [pillow_handler, numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_raises_if_endianess_not_set(self):
         self.jpeg_2k_lossless.is_little_endian = None
@@ -322,7 +320,7 @@ class Test_JPEG2000Tests_with_pillow(object):
 
 
 @pytest.mark.skipif(
-    not test_pillow_jpeg_decoder,
+    not TEST_JPEG,
     reason=pillow_missing_message)
 class Test_JPEGlossyTests_with_pillow(object):
     """Test decoding JPEG if pillow JPEG plugin is available."""
@@ -330,12 +328,12 @@ class Test_JPEGlossyTests_with_pillow(object):
         """Setup the test datasets."""
         self.jpeg_lossy = dcmread(jpeg_lossy_name)
         self.color_3d_jpeg = dcmread(color_3d_jpeg_baseline)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [pillow_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [pillow_handler, numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless_odd_data_size(self):
         """Test decoding JPEG with pillow handler succeeds."""
@@ -370,10 +368,10 @@ class Test_JPEGlossyTests_with_pillow(object):
 
 @pytest.fixture(scope="module")
 def test_with_pillow():
-    original_handlers = pydicom.config.image_handlers
-    pydicom.config.image_handlers = [pillow_handler, numpy_handler]
+    original_handlers = pydicom.config.pixel_data_handlers
+    pydicom.config.pixel_data_handlers = [pillow_handler, numpy_handler]
     yield original_handlers
-    pydicom.config.image_handlers = original_handlers
+    pydicom.config.pixel_data_handlers = original_handlers
 
 
 if have_pytest_param:
@@ -539,7 +537,7 @@ else:
 
 
 @pytest.mark.skipif(
-    not test_pillow_jpeg_decoder,
+    not TEST_JPEG,
     reason=pillow_missing_message)
 @pytest.mark.parametrize(
     "image,PhotometricInterpretation,results,ground_truth",
@@ -579,19 +577,19 @@ def test_PI_RGB(test_with_pillow,
 
 
 @pytest.mark.skipif(
-    not test_pillow_jpeg_decoder,
+    not TEST_JPEG,
     reason=pillow_missing_message)
 class Test_JPEGlosslessTests_with_pillow(object):
     """Test decoding JPEG lossless if pillow JPEG plugin is available."""
     def setup(self):
         """Setup the test datasets."""
         self.jpeg_lossless = dcmread(jpeg_lossless_name)
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [pillow_handler, numpy_handler]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [pillow_handler, numpy_handler]
 
     def teardown(self):
         """Reset the pixel data handlers."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def testJPEGlossless(self):
         """Test reading element values works OK with pillow pixel handler."""
@@ -601,5 +599,5 @@ class Test_JPEGlosslessTests_with_pillow(object):
 
     def testJPEGlosslessPixelArray(self):
         """Test decoding JPEG lossless with pillow handler fails."""
-        with pytest.raises(NotImplementedError):
+        with pytest.raises(RuntimeError):
             self.jpeg_lossless.pixel_array
diff --git a/pydicom/tests/test_rle_pixel_data.py b/pydicom/tests/test_rle_pixel_data.py
index 095b8ad11..b94f0f430 100644
--- a/pydicom/tests/test_rle_pixel_data.py
+++ b/pydicom/tests/test_rle_pixel_data.py
@@ -3,9 +3,10 @@
 
 There are the following possibilities:
 
-* numpy is not available and the RLE handler is not available
+* numpy is not available and
+  * the RLE handler is not available
+  * the RLE handler is available
 * numpy is available and
-
   * The RLE handler is not available
   * The RLE handler is available
 
@@ -36,7 +37,7 @@ try:
     import numpy as np
     from pydicom.pixel_data_handlers import numpy_handler as NP_HANDLER
     from pydicom.pixel_data_handlers.util import reshape_pixel_array
-    HAVE_NP = True
+    HAVE_NP = NP_HANDLER.HAVE_NP
 except ImportError:
     NP_HANDLER = None
     HAVE_NP = False
@@ -49,7 +50,9 @@ try:
         _rle_decode_segment,
         _parse_rle_header,
     )
+    HAVE_RLE = RLE_HANDLER.HAVE_RLE
 except ImportError:
+    HAVE_RLE = False
     RLE_HANDLER = None
 
 
@@ -144,18 +147,18 @@ def _get_pixel_array(fpath):
     -------
     numpy.ndarray
     """
-    if NP_HANDLER is None:
+    if not HAVE_NP:
         raise RuntimeError(
             'Function only usable if the numpy handler is available'
         )
 
-    original_handlers = pydicom.config.image_handlers
-    pydicom.config.image_handlers = [NP_HANDLER]
+    original_handlers = pydicom.config.pixel_data_handlers
+    pydicom.config.pixel_data_handlers = [NP_HANDLER]
 
     ds = dcmread(fpath)
     arr = ds.pixel_array
 
-    pydicom.config.image_handlers = original_handlers
+    pydicom.config.pixel_data_handlers = original_handlers
 
     return arr
 
@@ -182,17 +185,18 @@ class TestNoNumpy_NoRLEHandler(object):
     """Tests for handling datasets without numpy and the handler."""
     def setup(self):
         """Setup the environment."""
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = []
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def teardown(self):
         """Restore the environment."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_environment(self):
         """Check that the testing environment is as expected."""
         assert not HAVE_NP
-        assert RLE_HANDLER is None
+        # The RLE handler should still be available
+        assert RLE_HANDLER is not None
 
     def test_can_access_supported_dataset(self):
         """Test that we can read and access elements in an RLE dataset."""
@@ -220,18 +224,76 @@ class TestNoNumpy_NoRLEHandler(object):
                 ds.pixel_array
 
 
+# Numpy unavailable and the RLE handler is available
+@pytest.mark.skipif(HAVE_NP, reason='Numpy is available')
+class TestNoNumpy_RLEHandler(object):
+    """Tests for handling datasets without numpy and the handler."""
+    def setup(self):
+        """Setup the environment."""
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [RLE_HANDLER]
+
+    def teardown(self):
+        """Restore the environment."""
+        pydicom.config.pixel_data_handlers = self.original_handlers
+
+    def test_environment(self):
+        """Check that the testing environment is as expected."""
+        assert not HAVE_NP
+        # The RLE handler should still be available
+        assert RLE_HANDLER is not None
+
+    def test_can_access_supported_dataset(self):
+        """Test that we can read and access elements in an RLE dataset."""
+        ds = dcmread(MR_RLE_1F)
+        assert 'CompressedSamples^MR1' == ds.PatientName
+        assert 6128 == len(ds.PixelData)
+
+    @pytest.mark.parametrize("fpath,data", REFERENCE_DATA_UNSUPPORTED)
+    def test_can_access_unsupported_dataset(self, fpath, data):
+        """Test can read and access elements in unsupported datasets."""
+        ds = dcmread(fpath)
+        assert data[0] == ds.file_meta.TransferSyntaxUID
+        assert data[1] == ds.PatientName
+
+    def test_unsupported_pixel_array_raises(self):
+        """Test pixel_array raises exception for unsupported syntaxes."""
+        ds = dcmread(MR_EXPL_LITTLE_1F)
+        for uid in UNSUPPORTED_SYNTAXES:
+            ds.file_meta.TransferSyntaxUID = uid
+            exc_msg = (
+                r"Unable to decode pixel data with a transfer syntax UID of "
+                r"'{}'".format(uid)
+            )
+            with pytest.raises(RuntimeError, match=exc_msg):
+                ds.pixel_array
+
+    def test_supported_pixel_array_raises(self):
+        """Test pixel_array raises exception for supported syntaxes."""
+        ds = dcmread(MR_EXPL_LITTLE_1F)
+        for uid in SUPPORTED_SYNTAXES:
+            ds.file_meta.TransferSyntaxUID = uid
+            exc_msg = (
+                r"The following handlers are available to decode the pixel "
+                r"data however they are missing required dependencies: "
+                r"RLE Lossless \(req. NumPy\)"
+            )
+            with pytest.raises(RuntimeError, match=exc_msg):
+                ds.pixel_array
+
+
 # Numpy is available, the RLE handler is unavailable
 @pytest.mark.skipif(not HAVE_NP, reason='Numpy is not available')
 class TestNumpy_NoRLEHandler(object):
     """Tests for handling datasets with no handler."""
     def setup(self):
         """Setup the environment."""
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = []
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = []
 
     def teardown(self):
         """Restore the environment."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_environment(self):
         """Check that the testing environment is as expected."""
@@ -271,12 +333,12 @@ class TestNumpy_RLEHandler(object):
     """Tests for handling datasets with the handler."""
     def setup(self):
         """Setup the environment."""
-        self.original_handlers = pydicom.config.image_handlers
-        pydicom.config.image_handlers = [RLE_HANDLER]
+        self.original_handlers = pydicom.config.pixel_data_handlers
+        pydicom.config.pixel_data_handlers = [RLE_HANDLER]
 
     def teardown(self):
         """Restore the environment."""
-        pydicom.config.image_handlers = self.original_handlers
+        pydicom.config.pixel_data_handlers = self.original_handlers
 
     def test_environment(self):
         """Check that the testing environment is as expected."""