Repository: pyvisa/pyvisa

Issue:
'Can't concat bytes to str' error when calling write_ascii_values
It's very strange, as message.encode(enco) is bytes and util.to_ascii_block() returns string, so line 239 of messagebased.py `message = message.encode(enco) + block` fails with an error specified in the title. 
I use "d" for converter and "," for separator
Why is this happening, wasn't it tested at all or am I doing something wrong?

I could fix it trivially by calling .encode on the block variable.

Discussion:
MatthieuDartiailh: The broken tests come from Python 2.6. As we are likely to drop support for it in January I will not try to fix it. This is ready for review from my end. ping @hgrecco, @skrchnavy
thliebig: @MatthieuDartiailh, I have added a missing fixup for the improved header support:
https://github.com/thliebig/pyvisa/commit/f9879997ef87f6d903be63bb90ab9efa5d74a43e
It catches the case that a termination character happens to be the last valid data byte(s). It occurs only very very rarly, but it does and it then crashes the read... Please consider adding the path to this PR 
MatthieuDartiailh: Done. And I set you as author. This somehow always bothered me but as it worked in my tests as I forgot to spend more time on it. If you can do a complete review it would be awesome.
MatthieuDartiailh: @thliebig Do you want to do a final review before I merge ?

Patch:
diff --git a/.travis.yml b/.travis.yml
index f79e28d..291df6f 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -16,8 +16,8 @@ python:
   - "3.6"
 
 install:
-  - pip install coverage
-  - pip install coveralls
+  - if [ $TRAVIS_PYTHON_VERSION == '2.7' ] || [ $TRAVIS_PYTHON_VERSION == '3.6' ]; then pip install numpy; fi
+  - pip install coverage coveralls
 
 script:
   - coverage run -p --source=pyvisa --omit="*test*","*compat*" setup.py test
diff --git a/docs/conf.py b/docs/conf.py
index 1a1d32d..dd66e73 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -44,10 +44,10 @@ master_doc = 'index'
 # General information about the project.
 project = 'PyVISA'
 author = 'PyVISA Authors'
+
 # The version info for the project you're documenting, acts as replacement for
 # |version| and |release|, also used in various other places throughout the
 # built documents.
-
 version = pkg_resources.get_distribution(project).version
 release = version
 this_year = datetime.date.today().year
diff --git a/docs/rvalues.rst b/docs/rvalues.rst
index 383585c..aecb6a6 100644
--- a/docs/rvalues.rst
+++ b/docs/rvalues.rst
@@ -77,6 +77,11 @@ If you have doubles `d` in big endian the call will be::
 
 You can also specify the output container type, just as it was shown before.
 
+By default, PyVISA will assume that the data block is formatted according to
+the IEEE convention. If your instrument uses HP data block you can pass
+``header_fmt='hp'`` to ``read_binary_values``. If your instrument does not use
+any header for the data simply ``header_fmt='empty'``.
+
 
 Writing ASCII values
 --------------------
@@ -178,4 +183,12 @@ In those cases, you need to get the data::
 
 and then you need to implement the logic to parse it.
 
+Alternatively if the `read_raw` call fails you can try to read just a few bytes
+using::
+
+        >>> inst.write('CURV?')
+        >>> data = inst.read_bytes(1)
 
+If this call fails it may mean that your instrument did not answer, either
+because it needs more time or because your first instruction was not
+understood.
diff --git a/pyvisa/compat/__init__.py b/pyvisa/compat/__init__.py
index 9aacf7d..abd0f78 100644
--- a/pyvisa/compat/__init__.py
+++ b/pyvisa/compat/__init__.py
@@ -9,11 +9,10 @@
     :license: BSD, see LICENSE for more details.
 """
 
-from __future__ import division, unicode_literals, print_function, absolute_import
-import sys
-import unittest
+from __future__ import (division, unicode_literals, print_function,
+                        absolute_import)
 
-from subprocess import check_output
+import sys
 
 
 PYTHON3 = sys.version >= '3'
@@ -27,6 +26,10 @@ if PYTHON3:
     integer_types = (int, )
 
     input = input
+
+    int_to_bytes = int.to_bytes
+    int_from_bytes = int.from_bytes
+
 else:
     string_types = basestring
 
@@ -39,6 +42,86 @@ else:
 
     input = raw_input
 
+    # The 2 following function implementation extracted from the python-future
+    # project
+    import collections
+
+    def int_to_bytes(integer, length, byteorder, signed=False):
+        """
+        Return an array of bytes representing an integer.
+        The integer is represented using length bytes.  An OverflowError is
+        raised if the integer is not representable with the given number of
+        bytes.
+        The byteorder argument determines the byte order used to represent the
+        integer.  If byteorder is 'big', the most significant byte is at the
+        beginning of the byte array.  If byteorder is 'little', the most
+        significant byte is at the end of the byte array.  To request the
+        native byte order of the host system, use `sys.byteorder' as the byte
+        order value.
+        The signed keyword-only argument determines whether two's complement is
+        used to represent the integer.  If signed is False and a negative
+        integer is given, an OverflowError is raised.
+        """
+        if length < 0:
+            raise ValueError("length argument must be non-negative")
+        if length == 0 and integer == 0:
+            return bytes()
+        if signed and integer < 0:
+            bits = length * 8
+            num = (2**bits) + integer
+            if num <= 0:
+                raise OverflowError("int too smal to convert")
+        else:
+            if integer < 0:
+                raise OverflowError("can't convert negative int to unsigned")
+            num = integer
+        if byteorder not in ('little', 'big'):
+            raise ValueError("byteorder must be either 'little' or 'big'")
+        h = b'%x' % num
+        s = bytes((b'0'*(len(h) % 2) + h).zfill(length*2).decode('hex'))
+        if signed:
+            high_set = s[0] & 0x80
+            if integer > 0 and high_set:
+                raise OverflowError("int too big to convert")
+            if integer < 0 and not high_set:
+                raise OverflowError("int too small to convert")
+        if len(s) > length:
+            raise OverflowError("int too big to convert")
+        return s if byteorder == 'big' else s[::-1]
+
+    def int_from_bytes(mybytes, byteorder='big', signed=False):
+        """
+        Return the integer represented by the given array of bytes.
+        The mybytes argument must either support the buffer protocol or be an
+        iterable object producing bytes.  Bytes and bytearray are examples of
+        built-in objects that support the buffer protocol.
+        The byteorder argument determines the byte order used to represent the
+        integer.  If byteorder is 'big', the most significant byte is at the
+        beginning of the byte array.  If byteorder is 'little', the most
+        significant byte is at the end of the byte array.  To request the
+        native byte order of the host system, use `sys.byteorder' as the byte
+        order value.
+        The signed keyword-only argument indicates whether two's complement is
+        used to represent the integer.
+        """
+        if byteorder not in ('little', 'big'):
+            raise ValueError("byteorder must be either 'little' or 'big'")
+        if isinstance(mybytes, unicode):
+            raise TypeError("cannot convert unicode objects to bytes")
+        # mybytes can also be passed as a sequence of integers on Py3.
+        # Test for this:
+        elif isinstance(mybytes, collections.Iterable):
+            mybytes = bytes(mybytes)
+        b = mybytes if byteorder == 'big' else mybytes[::-1]
+        if len(b) == 0:
+            b = b'\x00'
+        # The encode() method has been disabled by newbytes, but Py2's
+        # str has it:
+        num = int(b.encode('hex'), 16)
+        if signed and (b[0] & 0x80):
+            num = num - (2 ** (len(b)*8))
+        return num
+
 try:
     from collections import OrderedDict
 except ImportError:
diff --git a/pyvisa/compat/struct.py b/pyvisa/compat/struct.py
index 154ddbf..2d0e429 100644
--- a/pyvisa/compat/struct.py
+++ b/pyvisa/compat/struct.py
@@ -9,7 +9,8 @@
     :license: PSF License
 """
 
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+                        absolute_import)
 
 import sys
 import struct
diff --git a/pyvisa/constants.py b/pyvisa/constants.py
index 2172306..c2e907f 100644
--- a/pyvisa/constants.py
+++ b/pyvisa/constants.py
@@ -688,7 +688,7 @@ class InterfaceType(enum.IntEnum):
 
 class AddressState(enum.IntEnum):
 
-    unaddressed =VI_GPIB_UNADDRESSED
+    unaddressed = VI_GPIB_UNADDRESSED
     talker = VI_GPIB_TALKER
     listenr = VI_GPIB_LISTENER
 
diff --git a/pyvisa/resources/messagebased.py b/pyvisa/resources/messagebased.py
index ea98bf4..588ed26 100644
--- a/pyvisa/resources/messagebased.py
+++ b/pyvisa/resources/messagebased.py
@@ -11,7 +11,8 @@
     :license: MIT, see LICENSE for more details.
 """
 
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+                        absolute_import)
 
 import contextlib
 import time
@@ -47,7 +48,8 @@ class ValuesFormat(object):
         self.container = container
         self.is_binary = False
 
-    def use_binary(self, datatype, is_big_endian, container=list, header_fmt='ieee'):
+    def use_binary(self, datatype, is_big_endian, container=list,
+                   header_fmt='ieee'):
         self.datatype = datatype
         self.is_big_endian = is_big_endian
         self.container = container
@@ -56,19 +58,21 @@ class ValuesFormat(object):
 
 
 class ControlRenMixin(object):
-    """Common controlt_ren method of some messaged based resources.
+    """Common control_ren method of some messaged based resources.
+
     """
     # It should work for GPIB, USB and some TCPIP
-    # For TCPIP I found some (all?) NI's VISA library do not handle control_ren, but
-    # it works for Agilent's VISA library (at least some of them)
+    # For TCPIP I found some (all?) NI's VISA library do not handle
+    # control_ren, but it works for Agilent's VISA library (at least some of
+    # them)
     def control_ren(self, mode):
-        """Controls the state of the GPIB Remote Enable (REN) interface line, and optionally the remote/local
-        state of the device.
+        """Controls the state of the GPIB Remote Enable (REN) interface line,
+        and optionally the remote/local state of the device.
 
         Corresponds to viGpibControlREN function of the VISA library.
 
-        :param mode: Specifies the state of the REN line and optionally the device remote/local state.
-                     (Constants.GPIB_REN*)
+        :param mode: Specifies the state of the REN line and optionally the
+                     device remote/local state. (Constants.GPIB_REN*)
         :return: return value of the library call.
         :rtype: VISAStatus
         """
@@ -95,17 +99,21 @@ class MessageBasedResource(Resource):
         self._values_format = ValuesFormat()
         super(MessageBasedResource, self).__init__(*args, **kwargs)
 
-    # This is done for backwards compatibility but will be removed in 1.7
+    # This is done for backwards compatibility but will be removed in 1.10
     @property
     def values_format(self):
+        warnings.warn('values_format is deprecated and will be removed in '
+                      '1.10', FutureWarning)
         return self._values_format
 
     @values_format.setter
     def values_format(self, fmt):
+        warnings.warn('values_format is deprecated and will be removed in '
+                      '1.10', FutureWarning)
         self._values_format.is_binary = not (fmt & 0x01 == 0)
-        if fmt & 0x03 == 1: #single
+        if fmt & 0x03 == 1:  # single
             self._values_format.datatype = 'f'
-        elif fmt & 0x03 == 3: #double:
+        elif fmt & 0x03 == 3:  # double:
             self._values_format.datatype = 'd'
         else:
             raise ValueError("unknown data values fmt requested")
@@ -118,10 +126,14 @@ class MessageBasedResource(Resource):
     def ask_delay(self):
         """An alias for query_delay kept for backwards compatibility.
         """
+        warnings.warn('ask_delay is deprecated and will be removed in '
+                      '1.10, use query_delay instead', FutureWarning)
         return self.query_delay
 
     @ask_delay.setter
     def ask_delay(self, value):
+        warnings.warn('ask_delay is deprecated and will be removed in '
+                      '1.10, use query_delay instead', FutureWarning)
         self.query_delay = value
 
     @property
@@ -132,7 +144,8 @@ class MessageBasedResource(Resource):
 
     @encoding.setter
     def encoding(self, encoding):
-        _ = 'test encoding'.encode(encoding).decode(encoding)
+        # Test that the encoding specified makes sense.
+        'test encoding'.encode(encoding).decode(encoding)
         self._encoding = encoding
 
     @property
@@ -145,22 +158,25 @@ class MessageBasedResource(Resource):
     def read_termination(self, value):
 
         if value:
-            # termination character, the rest is just used for verification after
-            # each read operation.
+            # termination character, the rest is just used for verification
+            # after each read operation.
             last_char = value[-1:]
-            # Consequently, it's illogical to have the real termination character
-            # twice in the sequence (otherwise reading would stop prematurely).
+            # Consequently, it's illogical to have the real termination
+            # character twice in the sequence (otherwise reading would stop
+            # prematurely).
 
             if last_char in value[:-1]:
                 raise ValueError("ambiguous ending in termination characters")
 
             self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, ord(last_char))
-            self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN, constants.VI_TRUE)
+            self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN,
+                                    constants.VI_TRUE)
         else:
-            # The termchar is also used in VI_ATTR_ASRL_END_IN (for serial termination)
-            # so return it to its default.
+            # The termchar is also used in VI_ATTR_ASRL_END_IN (for serial
+            # termination) so return it to its default.
             self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, ord(self.LF))
-            self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN, constants.VI_FALSE)
+            self.set_visa_attribute(constants.VI_ATTR_TERMCHAR_EN,
+                                    constants.VI_FALSE)
 
         self._read_termination = value
 
@@ -208,8 +224,10 @@ class MessageBasedResource(Resource):
 
         return count
 
-    def write_ascii_values(self, message, values, converter='f', separator=',', termination=None, encoding=None):
-        """Write a string message to the device followed by values in ascii format.
+    def write_ascii_values(self, message, values, converter='f', separator=',',
+                           termination=None, encoding=None):
+        """Write a string message to the device followed by values in ascii
+        format.
 
         The write_termination is always appended to it.
 
@@ -220,8 +238,8 @@ class MessageBasedResource(Resource):
                           String formatting codes are also accepted.
                           Defaults to str.
         :type converter: callable | str
-        :param separator: a callable that split the str into individual elements.
-                          If a str is given, data.split(separator) is used.
+        :param separator: a callable that join the values in a single str.
+                          If a str is given, separator.join(values) is used.
         :type: separator: (collections.Iterable[T]) -> str | str
         :return: number of bytes written.
         :rtype: int
@@ -236,7 +254,7 @@ class MessageBasedResource(Resource):
 
         block = util.to_ascii_block(values, converter, separator)
 
-        message = message.encode(enco) + block
+        message = message.encode(enco) + block.encode(enco)
 
         if term:
             message += term.encode(enco)
@@ -245,15 +263,19 @@ class MessageBasedResource(Resource):
 
         return count
 
-    def write_binary_values(self, message, values, datatype='f', is_big_endian=False, termination=None, encoding=None):
-        """Write a string message to the device followed by values in binary format.
+    def write_binary_values(self, message, values, datatype='f',
+                            is_big_endian=False, termination=None,
+                            encoding=None):
+        """Write a string message to the device followed by values in binary
+        format.
 
         The write_termination is always appended to it.
 
         :param message: the message to be sent.
         :type message: unicode (Py2) or str (Py3)
         :param values: data to be writen to the device.
-        :param datatype: the format string for a single element. See struct module.
+        :param datatype: the format string for a single element. See struct
+                         module.
         :param is_big_endian: boolean indicating endianess.
         :return: number of bytes written.
         :rtype: int
@@ -277,28 +299,76 @@ class MessageBasedResource(Resource):
         return count
 
     def write_values(self, message, values, termination=None, encoding=None):
-
+        warnings.warn('write_values is deprecated and will be removed in '
+                      '1.10, use write_ascii_values or write_binary_values '
+                      'instead.', FutureWarning)
         vf = self.values_format
 
         if vf.is_binary:
-            return self.write_binary_values(message, values, vf.datatype, vf.is_big_endian, termination, encoding)
+            return self.write_binary_values(message, values, vf.datatype,
+                                            vf.is_big_endian, termination,
+                                            encoding)
 
-        return self.write_ascii_values(message, values, vf.converter, vf.separator, termination, encoding)
+        return self.write_ascii_values(message, values, vf.converter,
+                                       vf.separator, termination, encoding)
+
+    def read_bytes(self, count, chunk_size=None, break_on_termchar=False):
+        """Read a certain number of bytes from the instrument.
+
+        :param count: The number of bytes to read from the instrument.
+        :type count: int
+        :param chunk_size: The chunk size to use to perform the reading.
+        :type chunk_size: int
+        :param break_on_termchar: Should the reading stop when a termination
+            character is encountered.
+        :type brak_on_termchar: bool
+
+        :rtype: bytes
+
+        """
+        chunk_size = chunk_size or self.chunk_size
+        ret = bytearray()
+        left_to_read = count
+        termchar_read = constants.StatusCode.success_termination_character_read
+
+        with self.ignore_warning(constants.VI_SUCCESS_DEV_NPRESENT,
+                                 constants.VI_SUCCESS_MAX_CNT):
+            try:
+                status = None
+                while len(ret) < count:
+                    size = min(chunk_size, left_to_read)
+                    logger.debug('%s - reading %d bytes (last status %r)',
+                                 self._resource_name, size, status)
+                    chunk, status = self.visalib.read(self.session, size)
+                    ret.extend(chunk)
+                    left_to_read -= len(chunk)
+                    if break_on_termchar and status == termchar_read:
+                        break
+            except errors.VisaIOError as e:
+                logger.debug('%s - exception while reading: %s\n'
+                             'Buffer content: %r',  self._resource_name, e,
+                             ret)
+                raise
+        return bytes(ret)
 
     def read_raw(self, size=None):
         """Read the unmodified string sent from the instrument to the computer.
 
         In contrast to read(), no termination characters are stripped.
 
+        :param size: The chunk size to use when reading the data.
+
         :rtype: bytes
         """
         return bytes(self._read_raw(size))
 
     def _read_raw(self, size=None):
         """Read the unmodified string sent from the instrument to the computer.
-        
+
         In contrast to read(), no termination characters are stripped.
-        
+
+        :param size: The chunk size to use when reading the data.
+
         :rtype: bytearray
         """
         size = self.chunk_size if size is None else size
@@ -306,7 +376,8 @@ class MessageBasedResource(Resource):
         loop_status = constants.StatusCode.success_max_count_read
 
         ret = bytearray()
-        with self.ignore_warning(constants.VI_SUCCESS_DEV_NPRESENT, constants.VI_SUCCESS_MAX_CNT):
+        with self.ignore_warning(constants.VI_SUCCESS_DEV_NPRESENT,
+                                 constants.VI_SUCCESS_MAX_CNT):
             try:
                 status = loop_status
                 while status == loop_status:
@@ -315,8 +386,8 @@ class MessageBasedResource(Resource):
                     chunk, status = self.visalib.read(self.session, size)
                     ret.extend(chunk)
             except errors.VisaIOError as e:
-                logger.debug('%s - exception while reading: %s\nBuffer content: %r',
-                             self._resource_name, e, ret)
+                logger.debug('%s - exception while reading: %s\nBuffer '
+                             'content: %r', self._resource_name, e, ret)
                 raise
 
         return ret
@@ -353,6 +424,78 @@ class MessageBasedResource(Resource):
 
         return message[:-len(termination)]
 
+    def read_ascii_values(self, converter='f', separator=',', container=list,
+                          delay=None):
+        """Read values from the device in ascii format returning an iterable of
+        values.
+
+        :param delay: delay in seconds between write and read operations.
+                      if None, defaults to self.query_delay
+        :param converter: function used to convert each element.
+                          Defaults to float
+        :type converter: callable
+        :param separator: a callable that split the str into individual
+                          elements. If a str is given, data.split(separator) is
+                          used.
+        :type: separator: (str) -> collections.Iterable[int] | str
+        :param container: container type to use for the output data.
+        :returns: the answer from the device.
+        :rtype: list
+
+        """
+        # Use read rather than _read_raw because we cannot handle a bytearray
+        block = self.read()
+
+        return util.from_ascii_block(block, converter, separator, container)
+
+    def read_binary_values(self, datatype='f', is_big_endian=False,
+                           container=list, header_fmt='ieee'):
+        """Read values from the device in binary format returning an iterable
+        of values.
+
+        :param datatype: the format string for a single element. See struct
+                         module.
+        :param is_big_endian: boolean indicating endianess.
+                              Defaults to False.
+        :param container: container type to use for the output data.
+        :param header_fmt: format of the header prefixing the data. Possible
+                           values are: 'ieee', 'hp', 'empty'
+        :returns: the answer from the device.
+        :rtype: type(container)
+
+        """
+        block = self._read_raw()
+        expected_length = 0
+
+        if header_fmt == 'ieee':
+            offset, data_length = util.parse_ieee_block_header(block)
+            expected_length = offset + data_length
+        elif header_fmt == 'hp':
+            offset, data_length = util.parse_hp_block_header(block,
+                                                             is_big_endian)
+            expected_length = offset + data_length
+
+        if self._read_termination is not None:
+            expected_length += len(self._read_termination)
+
+        while len(block) < expected_length:
+            block.extend(self._read_raw())
+
+        try:
+            if header_fmt == 'ieee':
+                return util.from_ieee_block(block, datatype, is_big_endian,
+                                            container)
+            elif header_fmt == 'hp':
+                return util.from_hp_block(block, datatype, is_big_endian,
+                                          container)
+            elif header_fmt == 'empty':
+                return util.from_binary_block(block, 0, None, datatype,
+                                              is_big_endian, container)
+            else:
+                raise
+        except ValueError as e:
+            raise errors.InvalidBinaryFormat(e.args)
+
     def read_values(self, fmt=None, container=list):
         """Read a list of floating point values from the device.
 
@@ -365,13 +508,17 @@ class MessageBasedResource(Resource):
         :return: the list of read values
         :rtype: list
         """
+        warnings.warn('read_values is deprecated and will be removed in '
+                      '1.10, use read_ascii_values or read_binary_values '
+                      'instead.', FutureWarning)
         if not fmt:
             vf = self.values_format
             if not vf.is_binary:
                 return util.from_ascii_block(self.read(), container)
             data = self._read_raw()
             try:
-                return util.parse_binary(data, vf.is_big_endian, vf.datatype=='f')
+                return util.parse_binary(data, vf.is_big_endian,
+                                         vf.datatype == 'f')
             except ValueError as e:
                 try:
                     msg = e.args[0]
@@ -379,20 +526,20 @@ class MessageBasedResource(Resource):
                     msg = ''
                 raise errors.InvalidBinaryFormat(msg)
 
-        if fmt & 0x01 == 0: # ascii
+        if fmt & 0x01 == 0:  # ascii
             return util.from_ascii_block(self.read())
 
         data = self._read_raw()
 
         try:
-            if fmt & 0x03 == 1: #single
+            if fmt & 0x03 == 1:  # single
                 is_single = True
-            elif fmt & 0x03 == 3: #double:
+            elif fmt & 0x03 == 3:  # double:
                 is_single = False
             else:
                 raise ValueError("unknown data values fmt requested")
 
-            is_big_endian = fmt & 0x04 # big endian
+            is_big_endian = fmt & 0x04  # big endian
             return util.parse_binary(data, is_big_endian, is_single)
         except ValueError as e:
             raise errors.InvalidBinaryFormat(e.args)
@@ -416,8 +563,10 @@ class MessageBasedResource(Resource):
             time.sleep(delay)
         return self.read()
 
-    # Kept for backwards compatibility.
-    ask = query
+    def ask(self, message, delay=None):
+        warnings.warn('ask is deprecated and will be removed in '
+                      '1.10, use query instead.', FutureWarning)
+        return self.query(message, delay)
 
     def query_values(self, message, delay=None):
         """Query the device for values returning an iterable of values.
@@ -431,15 +580,23 @@ class MessageBasedResource(Resource):
         :returns: the answer from the device.
         :rtype: list
         """
+        warnings.warn('query_values is deprecated and will be removed in '
+                      '1.10, use query_ascii_values or quey_binary_values '
+                      'instead.', FutureWarning)
         vf = self.values_format
 
         if vf.is_binary:
-            return self.query_binary_values(message, vf.datatype, vf.is_big_endian, vf.container, delay, vf.header_fmt)
+            return self.query_binary_values(message, vf.datatype,
+                                            vf.is_big_endian, vf.container,
+                                            delay, vf.header_fmt)
 
-        return self.query_ascii_values(message, vf.converter, vf.separator, vf.container, delay)
+        return self.query_ascii_values(message, vf.converter, vf.separator,
+                                       vf.container, delay)
 
-    def query_ascii_values(self, message, converter='f', separator=',', container=list, delay=None):
-        """Query the device for values in ascii format returning an iterable of values.
+    def query_ascii_values(self, message, converter='f', separator=',',
+                           container=list, delay=None):
+        """Query the device for values in ascii format returning an iterable of
+        values.
 
         :param message: the message to send.
         :type message: str
@@ -448,8 +605,9 @@ class MessageBasedResource(Resource):
         :param converter: function used to convert each element.
                           Defaults to float
         :type converter: callable
-        :param separator: a callable that split the str into individual elements.
-                          If a str is given, data.split(separator) is used.
+        :param separator: a callable that split the str into individual
+                          elements. If a str is given, data.split(separator) is
+                          used.
         :type: separator: (str) -> collections.Iterable[int] | str
         :param container: container type to use for the output data.
         :returns: the answer from the device.
@@ -462,25 +620,28 @@ class MessageBasedResource(Resource):
         if delay > 0.0:
             time.sleep(delay)
 
-        block = self.read()
-
-        return util.from_ascii_block(block, converter, separator, container)
+        return self.read_ascii_values(converter, separator, container,
+                                      delay)
 
-    def query_binary_values(self, message, datatype='f', is_big_endian=False, container=list, delay=None, header_fmt='ieee'):
-        """Converts an iterable of numbers into a block of data in the ieee format.
+    def query_binary_values(self, message, datatype='f', is_big_endian=False,
+                            container=list, delay=None, header_fmt='ieee'):
+        """Query the device for values in binary format returning an iterable
+        of values.
 
         :param message: the message to send to the instrument.
-        :param datatype: the format string for a single element. See struct module.
+        :param datatype: the format string for a single element. See struct
+                         module.
         :param is_big_endian: boolean indicating endianess.
                               Defaults to False.
         :param container: container type to use for the output data.
         :param delay: delay in seconds between write and read operations.
                       if None, defaults to self.query_delay
-        :rtype: bytes
+        :returns: the answer from the device.
+        :rtype: list
         """
-
         if header_fmt not in ('ieee', 'empty', 'hp'):
-            raise ValueError("Invalid header format. Valid options are 'ieee', 'empty', 'hp'")
+            raise ValueError("Invalid header format. Valid options are 'ieee',"
+                             " 'empty', 'hp'")
 
         self.write(message)
         if delay is None:
@@ -488,24 +649,8 @@ class MessageBasedResource(Resource):
         if delay > 0.0:
             time.sleep(delay)
 
-        block = self._read_raw()
-
-        if header_fmt == 'ieee':
-            offset, data_length = util.parse_ieee_block_header(block)
-            expected_length = offset + data_length
-
-            while len(block) < expected_length:
-                block.extend(self._read_raw())
-
-        try:
-            if header_fmt == 'ieee':
-                return util.from_ieee_block(block, datatype, is_big_endian, container)
-            elif header_fmt == 'empty':
-                return util.from_binary_block(block, 0, None, datatype, is_big_endian, container)
-            elif header_fmt == 'hp':
-                return util.from_binary_block(block, 4, None, datatype, is_big_endian, container)
-        except ValueError as e:
-            raise errors.InvalidBinaryFormat(e.args)
+        return self.read_binary_values(datatype, is_big_endian, container,
+                                       header_fmt)
 
     def ask_for_values(self, message, fmt=None, delay=None):
         """A combination of write(message) and read_values()
@@ -517,7 +662,9 @@ class MessageBasedResource(Resource):
         :returns: the answer from the device.
         :rtype: list
         """
-
+        warnings.warn('ask_values is deprecated and will be removed in '
+                      '1.10, use query_ascii_values or quey_binary_values '
+                      'instead.', FutureWarning)
         self.write(message)
         if delay is None:
             delay = self.query_delay
@@ -529,7 +676,8 @@ class MessageBasedResource(Resource):
         """Sends a software trigger to the device.
         """
 
-        self.visalib.assert_trigger(self.session, constants.VI_TRIG_PROT_DEFAULT)
+        self.visalib.assert_trigger(self.session,
+                                    constants.VI_TRIG_PROT_DEFAULT)
 
     @property
     def stb(self):
@@ -546,10 +694,12 @@ class MessageBasedResource(Resource):
     @contextlib.contextmanager
     def read_termination_context(self, new_termination):
         term = self.get_visa_attribute(constants.VI_ATTR_TERMCHAR)
-        self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, ord(new_termination[-1]))
+        self.set_visa_attribute(constants.VI_ATTR_TERMCHAR,
+                                ord(new_termination[-1]))
         yield
         self.set_visa_attribute(constants.VI_ATTR_TERMCHAR, term)
 
 
 # Rohde and Schwarz Device via Passport. Not sure which Resource should be.
-MessageBasedResource.register(constants.InterfaceType.rsnrp, 'INSTR')(MessageBasedResource)
+MessageBasedResource.register(constants.InterfaceType.rsnrp,
+                              'INSTR')(MessageBasedResource)
diff --git a/pyvisa/util.py b/pyvisa/util.py
index 8fcbf1f..7b01435 100644
--- a/pyvisa/util.py
+++ b/pyvisa/util.py
@@ -11,7 +11,8 @@
     :license: MIT, see LICENSE for more details.
 """
 
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+                        absolute_import)
 
 import functools
 import io
@@ -20,11 +21,34 @@ import platform
 import sys
 import subprocess
 import warnings
+import inspect
+from subprocess import check_output
 
-from .compat import check_output, string_types, OrderedDict, struct
+from .compat import (string_types, OrderedDict, struct,
+                     int_to_bytes, int_from_bytes, PYTHON3)
 from . import __version__, logger
 
 
+try:
+    import numpy as np
+except ImportError:
+    np = None
+
+
+def _use_numpy_routines(container):
+    """Should optimized numpy routines be used to extract the data.
+
+    """
+    if np is None or container in (tuple, list):
+        return False
+
+    if (container is np.array or (inspect.isclass(container) and
+                                  issubclass(container, np.ndarray))):
+        return True
+
+    return False
+
+
 def read_user_library_path():
     """Return the library path stored in one of the following configuration files:
 
@@ -43,13 +67,16 @@ def read_user_library_path():
 
     """
     try:
-        from ConfigParser import SafeConfigParser as ConfigParser, NoSectionError
+        from ConfigParser import (SafeConfigParser as ConfigParser,
+                                  NoSectionError)
     except ImportError:
         from configparser import ConfigParser, NoSectionError
 
     config_parser = ConfigParser()
-    files = config_parser.read([os.path.join(sys.prefix, "share", "pyvisa", ".pyvisarc"),
-                                os.path.join(os.path.expanduser("~"), ".pyvisarc")])
+    files = config_parser.read([os.path.join(sys.prefix, "share", "pyvisa",
+                                             ".pyvisarc"),
+                                os.path.join(os.path.expanduser("~"),
+                                             ".pyvisarc")])
 
     if not files:
         logger.debug('No user defined library files')
@@ -151,6 +178,16 @@ _converters = {
     'G': float,
 }
 
+_np_converters = {
+    'd': 'i',
+    'e': 'f',
+    'E': 'f',
+    'f': 'f',
+    'F': 'f',
+    'g': 'f',
+    'G': 'f',
+}
+
 
 def from_ascii_block(ascii_data, converter='f', separator=',', container=list):
     """Parse ascii data and return an iterable of numbers.
@@ -165,12 +202,19 @@ def from_ascii_block(ascii_data, converter='f', separator=',', container=list):
     :type: separator: (str) -> collections.Iterable[T] | str
     :param container: container type to use for the output data.
     """
+    if (_use_numpy_routines(container) and
+            isinstance(converter, string_types) and
+            isinstance(separator, string_types) and
+            converter in _np_converters):
+        return np.fromstring(ascii_data, _np_converters[converter],
+                             sep=separator)
 
     if isinstance(converter, string_types):
         try:
             converter = _converters[converter]
         except KeyError:
-            raise ValueError('Invalid code for converter: %s not in %s' % (converter, str(tuple(_converters.keys()))))
+            raise ValueError('Invalid code for converter: %s not in %s' %
+                             (converter, str(tuple(_converters.keys()))))
 
     if isinstance(separator, string_types):
         data = ascii_data.split(separator)
@@ -181,7 +225,7 @@ def from_ascii_block(ascii_data, converter='f', separator=',', container=list):
 
 
 def to_ascii_block(iterable, converter='f', separator=','):
-    """Parse ascii data and return an iterable of numbers.
+    """Turn an iterable of numbers in an ascii block of data.
 
     :param iterable: data to be parsed.
     :type iterable: collections.Iterable[T]
@@ -192,6 +236,8 @@ def to_ascii_block(iterable, converter='f', separator=','):
     :param separator: a callable that split the str into individual elements.
                       If a str is given, data.split(separator) is used.
     :type: separator: (collections.Iterable[T]) -> str | str
+
+    :rtype: str
     """
 
     if isinstance(separator, string_types):
@@ -199,9 +245,10 @@ def to_ascii_block(iterable, converter='f', separator=','):
 
     if isinstance(converter, string_types):
         converter = '%' + converter
-        return separator(converter % val for val in iterable)
+        block = separator(converter % val for val in iterable)
     else:
-        return separator(converter(val) for val in iterable)
+        block = separator(converter(val) for val in iterable)
+    return block
 
 
 def parse_binary(bytes_data, is_big_endian=False, is_single=False):
@@ -214,6 +261,9 @@ def parse_binary(bytes_data, is_big_endian=False, is_single=False):
     :param is_single: boolean indicating the type (if not is double)
     :return:
     """
+    warnings.warn('parse_binary is deprecated and will be removed in '
+                  '1.10, use read_ascii_values or read_binary_values '
+                  'instead.', FutureWarning)
     data = bytes_data
 
     hash_sign_position = bytes_data.find(b"#")
@@ -269,20 +319,20 @@ def parse_ieee_block_header(block):
     #0<data>
 
     :param block: IEEE block.
-    :type block: bytes
+    :type block: bytes | bytearray
     :return: (offset, data_length)
     :rtype: (int, int)
     """
     begin = block.find(b'#')
     if begin < 0:
-        raise ValueError("Could not find hash sign (#) indicating the start of the block.")
+        raise ValueError("Could not find hash sign (#) indicating the start of"
+                         " the block.")
 
     try:
         # int(block[begin+1]) != int(block[begin+1:begin+2]) in Python 3
         header_length = int(block[begin+1:begin+2])
     except ValueError:
         header_length = 0
-
     offset = begin + 2 + header_length
 
     if header_length > 0:
@@ -297,6 +347,35 @@ def parse_ieee_block_header(block):
     return offset, data_length
 
 
+def parse_hp_block_header(block, is_big_endian):
+    """Parse the header of a HP block.
+
+    Definite Length Arbitrary Block:
+    #A<data_length><data>
+
+    The header ia always 4 bytes long.
+    The data_length field specifies the size of the data.
+
+    :param block: HP block.
+    :type block: bytes | bytearray
+    :param is_big_endian: boolean indicating endianess.
+    :return: (offset, data_length)
+    :rtype: (int, int)
+
+    """
+    begin = block.find(b'#A')
+    if begin < 0:
+        raise ValueError("Could not find the standard block header (#A) "
+                         "indicating the start of the block.")
+    offset = begin + 4
+
+    data_length = int_from_bytes(block[begin+2:offset],
+                                 byteorder='big' if is_big_endian else 'little'
+                                 )
+
+    return offset, data_length
+
+
 def from_ieee_block(block, datatype='f', is_big_endian=False, container=list):
     """Convert a block in the IEEE format into an iterable of numbers.
 
@@ -309,38 +388,68 @@ def from_ieee_block(block, datatype='f', is_big_endian=False, container=list):
     Indefinite Length Arbitrary Block:
     #0<data>
 
-    :param block: IEEE block.
-    :type block: bytes
+    :param block: HP block.
+    :type block: bytes | bytearray
     :param datatype: the format string for a single element. See struct module.
     :param is_big_endian: boolean indicating endianess.
     :param container: container type to use for the output data.
     :return: items
     :rtype: type(container)
     """
-
     offset, data_length = parse_ieee_block_header(block)
 
     if len(block) < offset + data_length:
-        raise ValueError("Binary data is incomplete. The header states %d data bytes, "
-                         "but %d where received." % (data_length, len(block) - offset))
+        raise ValueError("Binary data is incomplete. The header states %d data"
+                         " bytes, but %d where received." %
+                         (data_length, len(block) - offset))
+
+    return from_binary_block(block, offset, data_length, datatype,
+                             is_big_endian, container)
 
-    return from_binary_block(block, offset, data_length, datatype, is_big_endian, container)
 
+def from_hp_block(block, datatype='f', is_big_endian=False, container=list):
+    """Convert a block in the HP format into an iterable of numbers.
 
-def from_binary_block(block, offset=0, data_length=None, datatype='f', is_big_endian=False, container=list):
+    Definite Length Arbitrary Block:
+    #A<data_length><data>
+
+    The header ia always 4 bytes long.
+    The data_length field specifies the size of the data.
+
+    :param block: IEEE block.
+    :type block: bytes | bytearray
+    :param datatype: the format string for a single element. See struct module.
+    :param is_big_endian: boolean indicating endianess.
+    :param container: container type to use for the output data.
+    :return: items
+    :rtype: type(container)
+    """
+    offset, data_length = parse_hp_block_header(block, is_big_endian)
+
+    if len(block) < offset + data_length:
+        raise ValueError("Binary data is incomplete. The header states %d data"
+                         " bytes, but %d where received." %
+                         (data_length, len(block) - offset))
+
+    return from_binary_block(block, offset, data_length, datatype,
+                             is_big_endian, container)
+
+
+def from_binary_block(block, offset=0, data_length=None, datatype='f',
+                      is_big_endian=False, container=list):
     """Convert a binary block into an iterable of numbers.
 
     :param block: binary block.
-    :type block: bytes
+    :type block: bytes | bytearray
     :param offset: offset at which the data block starts (default=0)
-    :param data_length: size in bytes of the data block (default=len(block) - offset)
+    :param data_length: size in bytes of the data block
+                        (default=len(block) - offset)
     :param datatype: the format string for a single element. See struct module.
     :param is_big_endian: boolean indicating endianess.
     :param container: container type to use for the output data.
     :return: items
     :rtype: type(container)
     """
-
     if data_length is None:
         data_length = len(block) - offset
 
@@ -349,6 +458,9 @@ def from_binary_block(block, offset=0, data_length=None, datatype='f', is_big_en
 
     endianess = '>' if is_big_endian else '<'
 
+    if _use_numpy_routines(container):
+        return np.frombuffer(block, endianess+datatype, array_length, offset)
+
     fullfmt = '%s%d%s' % (endianess, array_length, datatype)
 
     try:
@@ -357,6 +469,27 @@ def from_binary_block(block, offset=0, data_length=None, datatype='f', is_big_en
         raise ValueError("Binary data was malformed")
 
 
+def to_binary_block(iterable, header, datatype, is_big_endian):
+    """Convert an iterable of numbers into a block of data with a given header.
+
+    :param iterable: an iterable of numbers.
+    :param header: the header which should prefix the binary block
+    :param datatype: the format string for a single element. See struct module.
+    :param is_big_endian: boolean indicating endianess.
+    :return: IEEE block.
+    :rtype: bytes
+    """
+    array_length = len(iterable)
+
+    endianess = '>' if is_big_endian else '<'
+    fullfmt = '%s%d%s' % (endianess, array_length, datatype)
+
+    if isinstance(header, string_types):
+        header = bytes(header, 'ascii') if PYTHON3 else str(header)
+
+    return header + struct.pack(fullfmt, *iterable)
+
+
 def to_ieee_block(iterable, datatype='f', is_big_endian=False):
     """Convert an iterable of numbers into a block of data in the IEEE format.
 
@@ -366,21 +499,34 @@ def to_ieee_block(iterable, datatype='f', is_big_endian=False):
     :return: IEEE block.
     :rtype: bytes
     """
-
     array_length = len(iterable)
     element_length = struct.calcsize(datatype)
     data_length = array_length * element_length
 
     header = '%d' % data_length
-    header = '#%d%s'%(len(header),header)
+    header = '#%d%s' % (len(header), header)
 
-    endianess = '>' if is_big_endian else '<'
-    fullfmt = '%s%d%s' % (endianess, array_length, datatype)
+    return to_binary_block(iterable, header, datatype, is_big_endian)
 
-    if sys.version >= '3':
-        return bytes(header, 'ascii') + struct.pack(fullfmt, *iterable)
-    else:
-        return str(header) + struct.pack(fullfmt, *iterable)
+
+def to_hp_block(iterable, datatype='f', is_big_endian=False):
+    """Convert an iterable of numbers into a block of data in the HP format.
+
+    :param iterable: an iterable of numbers.
+    :param datatype: the format string for a single element. See struct module.
+    :param is_big_endian: boolean indicating endianess.
+    :return: IEEE block.
+    :rtype: bytes
+    """
+
+    array_length = len(iterable)
+    element_length = struct.calcsize(datatype)
+    data_length = array_length * element_length
+
+    header = b'#A' + (int_to_bytes(data_length, 2,
+                                   'big' if is_big_endian else 'little'))
+
+    return to_binary_block(iterable, header, datatype, is_big_endian)
 
 
 def get_system_details(backends=True):
@@ -530,7 +676,7 @@ machine_types = {
     0x0200: 'IA64',
     0x0266: 'MIPS16',
     0x0284: 'ALPHA64',
-    #0x0284: 'AXP64', # same
+    # 0x0284: 'AXP64', # same
     0x0366: 'MIPSFPU',
     0x0466: 'MIPSFPU16',
     0x0520: 'TRICORE',
@@ -573,7 +719,7 @@ def get_arch(filename):
             return 64,
         else:
             return ()
-    elif not this_platform in ('linux2', 'linux3', 'linux', 'darwin'):
+    elif this_platform not in ('linux2', 'linux3', 'linux', 'darwin'):
         raise OSError('')
 
     out = check_output(["file", filename], stderr=subprocess.STDOUT)


Test patch:
diff --git a/pyvisa/testsuite/__init__.py b/pyvisa/testsuite/__init__.py
index e46a262..acd8991 100644
--- a/pyvisa/testsuite/__init__.py
+++ b/pyvisa/testsuite/__init__.py
@@ -1,15 +1,18 @@
 # -*- coding: utf-8 -*-
 
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+                        absolute_import)
 
 import os
 import logging
+import warnings
+import unittest
 from contextlib import contextmanager
 
 from logging.handlers import BufferingHandler
 
 from pyvisa import logger
-from pyvisa.compat import unittest
+from pyvisa.compat import PYTHON3
 
 
 class TestHandler(BufferingHandler):
@@ -62,6 +65,21 @@ class BaseTestCase(unittest.TestCase):
             msg = '\n'.join(record.get('msg', str(record)) for record in buf)
             self.assertEqual(l, 0, msg='%d warnings raised.\n%s' % (l, msg))
 
+    if not PYTHON3:
+        @contextmanager
+        def assertWarns(self, category):
+            """Backport for Python 2
+
+            """
+            with warnings.catch_warnings(record=True) as w:
+                # Cause all warnings to always be triggered.
+                warnings.simplefilter("always")
+                # Trigger a warning.
+                yield
+                # Verify some things
+                assert len(w) == 1, 'No warning raised'
+                assert issubclass(w[-1].category, category)
+
 
 def testsuite():
     """A testsuite that has all the pyvisa tests.
@@ -85,4 +103,3 @@ def run():
     """
     test_runner = unittest.TextTestRunner()
     return test_runner.run(testsuite())
-
diff --git a/pyvisa/testsuite/test_rname.py b/pyvisa/testsuite/test_rname.py
index f6c694b..55193ba 100644
--- a/pyvisa/testsuite/test_rname.py
+++ b/pyvisa/testsuite/test_rname.py
@@ -1,8 +1,9 @@
 # -*- coding: utf-8 -*-
 
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+                        absolute_import)
 
-from pyvisa.compat import unittest
+import unittest
 from pyvisa.testsuite import BaseTestCase
 
 from pyvisa import rname
diff --git a/pyvisa/testsuite/test_util.py b/pyvisa/testsuite/test_util.py
index 85ff8c7..00501eb 100644
--- a/pyvisa/testsuite/test_util.py
+++ b/pyvisa/testsuite/test_util.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 
-from __future__ import division, unicode_literals, print_function, absolute_import
+from __future__ import (division, unicode_literals, print_function,
+                        absolute_import)
 
 from pyvisa.testsuite import BaseTestCase
 
@@ -12,49 +13,102 @@ try:
 except ImportError:
     np = None
 
+
 class TestParser(BaseTestCase):
 
     def test_parse_binary(self):
-        s = b'#A@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xde\x8b<@\xde\x8b<@\xde\x8b<' \
-            b'@\xde\x8b<@\xe0\x8b<@\xe0\x8b<@\xdc\x8b<@\xde\x8b<@\xe2\x8b<@\xe0\x8b<'
+        s = (b'#A@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xe2\x8b<@\xde\x8b<@\xde\x8b<@'
+             b'\xde\x8b<@\xde\x8b<@\xe0\x8b<@\xe0\x8b<@\xdc\x8b<@\xde\x8b<@'
+             b'\xe2\x8b<@\xe0\x8b<')
         e = [0.01707566, 0.01707566, 0.01707566, 0.01707566, 0.01707375,
              0.01707375, 0.01707375, 0.01707375, 0.01707470, 0.01707470,
              0.01707280, 0.01707375, 0.01707566, 0.01707470]
-        p = util.parse_binary(s, is_big_endian=False, is_single=True)
+        with self.assertWarns(FutureWarning):
+            p = util.parse_binary(s, is_big_endian=False, is_single=True)
         for a, b in zip(p, e):
             self.assertAlmostEqual(a, b)
+
+        # Test handling indefinite length block
         p = util.from_ieee_block(s, datatype='f', is_big_endian=False)
         for a, b in zip(p, e):
             self.assertAlmostEqual(a, b)
 
-    def test_ieee_integer(self):
+        # Test handling definite length block
+        p = util.from_ieee_block(b'#214' + s[2:], datatype='f',
+                                 is_big_endian=False)
+        for a, b in zip(p, e):
+            self.assertAlmostEqual(a, b)
+
+        p = util.from_hp_block(b'#A\x0e\x00' + s[2:], datatype='f',
+                               is_big_endian=False)
+        for a, b in zip(p, e):
+            self.assertAlmostEqual(a, b)
+
+    def test_integer_ascii_block(self):
         values = list(range(99))
-        containers = (list, tuple) #+ ((np.asarray,) if np else ())
-        for fmt in 'bBhHiIfd':
-            for endi in (True, False):
-                for cont in containers:
-                    conv = cont(values)
-                    msg = 'fmt=%s, endianness=%s, container=%s' % (fmt, endi, cont.__name__)
-                    try:
-                        block = util.to_ieee_block(conv, fmt, endi)
-                        parsed = util.from_ieee_block(block, fmt, endi, cont)
-                    except Exception as e:
-                        raise Exception(msg + '\n' + repr(e))
-
-                    self.assertEqual(conv, parsed, msg)
-
-    def test_ieee_noninteger(self):
+        for fmt in 'd':
+            msg = 'block=%s, fmt=%s'
+            msg = msg % ('ascii', fmt)
+            tb = lambda values: util.to_ascii_block(values, fmt, ',')
+            fb = lambda block, cont: util.from_ascii_block(block, fmt, ',',
+                                                           cont)
+            self.round_trip_block_converstion(values, tb, fb, msg)
+
+    def test_non_integer_ascii_block(self):
         values = [val + 0.5 for val in range(99)]
-        containers = (list, tuple) #+ ((np.asarray,) if np else ())
-        for fmt in 'fd':
-            for endi in (True, False):
-                for cont in containers:
-                    conv = cont(values)
-                    msg = 'fmt=%s, endianness=%s, container=%s' % (fmt, endi, cont.__name__)
-                    try:
-                        block = util.to_ieee_block(conv, fmt, endi)
-                        parsed = util.from_ieee_block(block, fmt, endi, cont)
-                    except Exception as e:
-                        raise Exception(msg + '\n' + repr(e))
-
-                    self.assertEqual(conv, parsed, msg)
+        values = list(range(99))
+        for fmt in 'fFeEgG':
+            msg = 'block=%s, fmt=%s'
+            msg = msg % ('ascii', fmt)
+            tb = lambda values: util.to_ascii_block(values, fmt, ',')
+            fb = lambda block, cont: util.from_ascii_block(block, fmt, ',',
+                                                           cont)
+            self.round_trip_block_converstion(values, tb, fb, msg)
+
+    def test_integer_binary_block(self):
+        values = list(range(99))
+        for block, tb, fb in zip(('ieee', 'hp'),
+                                 (util.to_ieee_block, util.to_hp_block),
+                                 (util.from_ieee_block, util.from_hp_block)):
+            for fmt in 'bBhHiIfd':
+                for endi in (True, False):
+                    msg = 'block=%s, fmt=%s, endianness=%s'
+                    msg = msg % (block, fmt, endi)
+                    tblock = lambda values: tb(values, fmt, endi)
+                    fblock = lambda block, cont: fb(block, fmt, endi, cont)
+                    self.round_trip_block_converstion(values, tblock, fblock,
+                                                      msg)
+
+    def test_noninteger_binary_block(self):
+        values = [val + 0.5 for val in range(99)]
+        for block, tb, fb in zip(('ieee', 'hp'),
+                                 (util.to_ieee_block, util.to_hp_block),
+                                 (util.from_ieee_block, util.from_hp_block)):
+            for fmt in 'fd':
+                for endi in (True, False):
+                    msg = 'block=%s, fmt=%s, endianness=%s'
+                    msg = msg % (block, fmt, endi)
+                    tblock = lambda values: bytearray(tb(values, fmt, endi))
+                    fblock = lambda block, cont: fb(block, fmt, endi, cont)
+                    self.round_trip_block_converstion(values, tblock, fblock,
+                                                      msg)
+
+    def round_trip_block_converstion(self, values, to_block, from_block, msg):
+        """Test that block conversion round trip as expected.
+
+        """
+        containers = (list, tuple) + ((np.array,) if np else ())
+        for cont in containers:
+            conv = cont(values)
+            msg += ', container=%s'
+            msg = msg % cont.__name__
+            try:
+                block = to_block(conv)
+                parsed = from_block(block, cont)
+            except Exception as e:
+                raise Exception(msg + '\n' + repr(e))
+
+            if np and cont in (np.array,):
+                np.testing.assert_array_equal(conv, parsed, msg)
+            else:
+                self.assertEqual(conv, parsed, msg)