Repository: ni/nixnet-python

Issue:
`session.signals` and `session.frames` might not correctly be implementing Mapping semantics
These containers allow someone to
- Iterate on the contained signals
- Lookup signals by index and name (but not slice)

They were [defined](https://github.com/ni/nixnet-python/blob/master/nixnet/_session/collection.py) as implementing the [`Mapping`](https://docs.python.org/3/library/collections.abc.html) abc.  The concern is that when you iterate on other mappings, you get the keys back.  To iterate on the values, you need to call `mapping.values()`.

Discussion:
coveralls: 
[![Coverage Status](https://coveralls.io/builds/12733148/badge)](https://coveralls.io/builds/12733148)

Coverage remained the same at 64.175% when pulling **fbb2a886dbceb309684decb37c4785c427a83072 on epage:mapping** into **5590c92682e9a33adc823396a3565fa4dbb47055 on ni:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/12733182/badge)](https://coveralls.io/builds/12733182)

Coverage remained the same at 64.175% when pulling **fbb2a886dbceb309684decb37c4785c427a83072 on epage:mapping** into **5590c92682e9a33adc823396a3565fa4dbb47055 on ni:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/12744517/badge)](https://coveralls.io/builds/12744517)

Coverage remained the same at 64.175% when pulling **e2e8f3eaba54e918193a6816a0d89f3d87441593 on epage:mapping** into **5590c92682e9a33adc823396a3565fa4dbb47055 on ni:master**.

coveralls: 
[![Coverage Status](https://coveralls.io/builds/12744517/badge)](https://coveralls.io/builds/12744517)

Coverage remained the same at 64.175% when pulling **e2e8f3eaba54e918193a6816a0d89f3d87441593 on epage:mapping** into **5590c92682e9a33adc823396a3565fa4dbb47055 on ni:master**.


Patch:
diff --git a/nixnet/_session/collection.py b/nixnet/_session/collection.py
index 60aa361..7929db3 100644
--- a/nixnet/_session/collection.py
+++ b/nixnet/_session/collection.py
@@ -12,7 +12,7 @@ from nixnet import _props
 
 
 @six.add_metaclass(abc.ABCMeta)
-class Collection(collections.Mapping):
+class Collection(collections.Sequence):
     """Collection of items in a session."""
 
     def __init__(self, handle):
@@ -42,7 +42,6 @@ class Collection(collections.Mapping):
             raise TypeError(index)
 
     def __getitem__(self, index):
-        # type: (typing.Union[int, typing.Text]) -> Item
         if isinstance(index, six.integer_types):
             name = self._list_cache[index]
         elif isinstance(index, six.string_types):
@@ -59,6 +58,12 @@ class Collection(collections.Mapping):
 
     def get(self, index, default=None):
         # type: (typing.Union[int, typing.Text], typing.Any) -> Item
+        """Access an item, returning ``default`` on failure.
+
+        Args:
+            index(str or int): Item name or index
+            default: Value to return when lookup fails
+        """
         if isinstance(index, six.integer_types):
             try:
                 name = self._list_cache[index]
diff --git a/nixnet/_session/signals.py b/nixnet/_session/signals.py
index f0af203..ed31b22 100644
--- a/nixnet/_session/signals.py
+++ b/nixnet/_session/signals.py
@@ -5,6 +5,7 @@ from __future__ import print_function
 import typing  # NOQA: F401
 
 from nixnet import _funcs
+from nixnet import _props
 
 from nixnet._session import collection
 
@@ -18,6 +19,20 @@ class Signals(collection.Collection):
     def _create_item(self, handle, index, name):
         return Signal(handle, index, name)
 
+    @property
+    def resamp_rate(self):
+        # type: () -> float
+        """float: Rate used to resample frame data to/from signal data in waveforms.
+
+        The units are in Hertz (samples per second).
+        """
+        return _props.get_session_resamp_rate(self._handle)
+
+    @resamp_rate.setter
+    def resamp_rate(self, value):
+        # type: (float) -> None
+        _props.set_session_resamp_rate(self._handle, value)
+
 
 class SinglePointInSignals(Signals):
     """Writeable signals in a session."""
diff --git a/nixnet/session.py b/nixnet/session.py
index 6849e40..0600160 100644
--- a/nixnet/session.py
+++ b/nixnet/session.py
@@ -312,7 +312,7 @@ class FrameOutQueuedSession(base.SessionBase):
     @property
     def frames(self):
         # type: () -> session_frames.OutFrames
-        """:any:`nixnet._session.frames.OutFrames`: Operate on session's frames"""
+        """:any:`nixnet._session.frames.InFrames`: Operate on session's frames"""
         return self._frames
 
 
diff --git a/nixnet/types.py b/nixnet/types.py
index a668da9..715c6e5 100644
--- a/nixnet/types.py
+++ b/nixnet/types.py
@@ -231,7 +231,7 @@ class RawFrame(Frame):
         "info",
         "payload"]
 
-    def __init__(self, timestamp, identifier, type, flags=0, info=0, payload=b""):
+    def __init__(self, timestamp, identifier, type, flags, info, payload=b""):
         # type: (int, int, constants.FrameType, int, int, bytes) -> None
         self.timestamp = timestamp
         self.identifier = identifier
@@ -271,24 +271,14 @@ class RawFrame(Frame):
         """RawFrame debug representation.
 
         >>> RawFrame(1, 2, constants.FrameType.CAN_DATA, 3, 4)
-        RawFrame(timestamp=0x1, identifier=0x2, type=FrameType.CAN_DATA, flags=0x3, info=0x4)
+        RawFrame(timestamp=0x1, identifier=0x2, type=FrameType.CAN_DATA, flags=0x3, info=0x4, payload=...)
         """
-        optional = []
-        if self.flags != 0:
-            optional.append('flags=0x{:x}'.format(self.flags))
-        if self.info != 0:
-            optional.append('info=0x{:x}'.format(self.info))
-        if self.payload:
-            optional.append('len(payload)={}'.format(len(self.payload)))
-        if optional:
-            optional_params = ', {}'.format(", ".join(optional))
-        else:
-            optional_params = ''
-        return "RawFrame(timestamp=0x{:x}, identifier=0x{:x}, type={}{})".format(
+        return "RawFrame(timestamp=0x{:x}, identifier=0x{:x}, type={}, flags=0x{:x}, info=0x{:x}, payload=...)".format(
             self.timestamp,
             self.identifier,
             self.type,
-            optional_params)
+            self.flags,
+            self.info)
 
 
 class CanFrame(Frame):
@@ -310,7 +300,7 @@ class CanFrame(Frame):
         "timestamp",
         "payload"]
 
-    def __init__(self, identifier, type=constants.FrameType.CAN_DATA, payload=b""):
+    def __init__(self, identifier, type, payload=b""):
         # type: (typing.Union[CanIdentifier, int], constants.FrameType, bytes) -> None
         if isinstance(identifier, int):
             self.identifier = CanIdentifier(identifier)
@@ -327,7 +317,7 @@ class CanFrame(Frame):
 
         >>> raw = RawFrame(5, 0x20000001, constants.FrameType.CAN_DATA, _cconsts.NX_FRAME_FLAGS_TRANSMIT_ECHO, 0, b'')
         >>> CanFrame.from_raw(raw)
-        CanFrame(CanIdentifier(0x1, extended=True), echo=True, timestamp=0x5)
+        CanFrame(CanIdentifier(0x1, extended=True), echo=True, type=FrameType.CAN_DATA, timestamp=0x5, payload=...)
         """
         identifier = CanIdentifier.from_raw(frame.identifier)
         can_frame = CanFrame(identifier, constants.FrameType(frame.type), frame.payload)
@@ -339,11 +329,11 @@ class CanFrame(Frame):
         """Convert to RawFrame.
 
         >>> CanFrame(CanIdentifier(1, True), constants.FrameType.CAN_DATA).to_raw()
-        RawFrame(timestamp=0x0, identifier=0x20000001, type=FrameType.CAN_DATA)
+        RawFrame(timestamp=0x0, identifier=0x20000001, type=FrameType.CAN_DATA, flags=0x0, info=0x0, payload=...)
         >>> c = CanFrame(CanIdentifier(1, True), constants.FrameType.CAN_DATA)
         >>> c.echo = True
         >>> c.to_raw()
-        RawFrame(timestamp=0x0, identifier=0x20000001, type=FrameType.CAN_DATA, flags=0x80)
+        RawFrame(timestamp=0x0, identifier=0x20000001, type=FrameType.CAN_DATA, flags=0x80, info=0x0, payload=...)
         """
         identifier = int(self.identifier)
         flags = 0
@@ -371,27 +361,14 @@ class CanFrame(Frame):
         # type: () -> typing.Text
         """CanFrame debug representation.
 
-        >>> CanFrame(1)
-        CanFrame(CanIdentifier(0x1))
-        >>> CanFrame(1, constants.FrameType.CANFD_DATA, b'\x01')
-        CanFrame(CanIdentifier(0x1), type=FrameType.CANFD_DATA, len(payload)=1)
+        >>> CanFrame(1, constants.FrameType.CAN_DATA)
+        CanFrame(CanIdentifier(0x1), echo=False, type=FrameType.CAN_DATA, timestamp=0x0, payload=...)
         """
-        optional = []
-        if self.echo:
-            optional.append('echo={}'.format(self.echo))
-        if self.type != constants.FrameType.CAN_DATA:
-            optional.append('type={}'.format(self.type))
-        if self.timestamp != 0:
-            optional.append('timestamp=0x{:x}'.format(self.timestamp))
-        if self.payload:
-            optional.append('len(payload)={}'.format(len(self.payload)))
-        if optional:
-            optional_params = ', {}'.format(", ".join(optional))
-        else:
-            optional_params = ''
-        return "CanFrame({}{})".format(
+        return "CanFrame({}, echo={}, type={}, timestamp=0x{:x}, payload=...)".format(
             self.identifier,
-            optional_params)
+            self.echo,
+            self.type,
+            self.timestamp)
 
 
 class CanBusErrorFrame(Frame):
@@ -446,7 +423,7 @@ class CanBusErrorFrame(Frame):
         """Convert to RawFrame.
 
         >>> CanBusErrorFrame(100, constants.CanCommState.BUS_OFF, True, constants.CanLastErr.STUFF, 1, 2).to_raw()
-        RawFrame(timestamp=0x64, identifier=0x0, type=FrameType.CAN_BUS_ERROR, len(payload)=5)
+        RawFrame(timestamp=0x64, identifier=0x0, type=FrameType.CAN_BUS_ERROR, flags=0x0, info=0x0, payload=...)
         """
         identifier = 0
         flags = 0
@@ -459,7 +436,7 @@ class CanBusErrorFrame(Frame):
             self.tx_err_count,
             self.rx_err_count,
         ]
-        payload = bytes(bytearray(payload_data))
+        payload = bytes(payload_data)
         return RawFrame(self.timestamp, identifier, self.type, flags, info, payload)
 
     @property
@@ -526,7 +503,7 @@ class DelayFrame(Frame):
         """Convert to RawFrame.
 
         >>> DelayFrame(250).to_raw()
-        RawFrame(timestamp=0xfa, identifier=0x0, type=FrameType.SPECIAL_DELAY)
+        RawFrame(timestamp=0xfa, identifier=0x0, type=FrameType.SPECIAL_DELAY, flags=0x0, info=0x0, payload=...)
         """
         identifier = 0
         flags = 0
@@ -590,7 +567,7 @@ class LogTriggerFrame(Frame):
         """Convert to RawFrame.
 
         >>> LogTriggerFrame(250).to_raw()
-        RawFrame(timestamp=0xfa, identifier=0x0, type=FrameType.SPECIAL_LOG_TRIGGER)
+        RawFrame(timestamp=0xfa, identifier=0x0, type=FrameType.SPECIAL_LOG_TRIGGER, flags=0x0, info=0x0, payload=...)
         """
         identifier = 0
         flags = 0
@@ -650,7 +627,7 @@ class StartTriggerFrame(Frame):
         """Convert to RawFrame.
 
         >>> StartTriggerFrame(250).to_raw()
-        RawFrame(timestamp=0xfa, identifier=0x0, type=FrameType.SPECIAL_START_TRIGGER)
+        RawFrame(timestamp=0xfa, identifier=0x0, type=FrameType.SPECIAL_START_TRIGGER, flags=0x0, info=0x0, payload=...)
         """
         identifier = 0
         flags = 0
diff --git a/nixnet_examples/can_frame_queued_io.py b/nixnet_examples/can_frame_queued_io.py
index ba9b893..0dfcdcf 100644
--- a/nixnet_examples/can_frame_queued_io.py
+++ b/nixnet_examples/can_frame_queued_io.py
@@ -2,15 +2,18 @@ from __future__ import absolute_import
 from __future__ import division
 from __future__ import print_function
 
-import time
-
+import pprint
 import six
+import time
 
 import nixnet
 from nixnet import constants
 from nixnet import types
 
 
+pp = pprint.PrettyPrinter(indent=4)
+
+
 def main():
     database_name = 'NIXNET_example'
     cluster_name = 'CAN_Cluster'
@@ -72,8 +75,8 @@ def main():
                 count = 1
                 frames = input_session.frames.read(count)
                 for frame in frames:
-                    print('Received frame: {}'.format(frame))
-                    print('    payload={}'.format(list(six.iterbytes(frame.payload))))
+                    print('Received frame: ')
+                    pp.pprint(frame)
 
                 i += 1
                 if max(payload) + i > 0xFF:
diff --git a/nixnet_examples/can_frame_stream_io.py b/nixnet_examples/can_frame_stream_io.py
index 464455e..60769d2 100644
--- a/nixnet_examples/can_frame_stream_io.py
+++ b/nixnet_examples/can_frame_stream_io.py
@@ -2,15 +2,18 @@ from __future__ import absolute_import
 from __future__ import division
 from __future__ import print_function
 
-import time
-
+import pprint
 import six
+import time
 
 import nixnet
 from nixnet import constants
 from nixnet import types
 
 
+pp = pprint.PrettyPrinter(indent=4)
+
+
 def main():
     interface1 = 'CAN1'
     interface2 = 'CAN2'
@@ -64,8 +67,8 @@ def main():
                 count = 1
                 frames = input_session.frames.read(count)
                 for frame in frames:
-                    print('Received frame: {}'.format(frame))
-                    print('    payload={}'.format(list(six.iterbytes(frame.payload))))
+                    print('Received frame: ')
+                    pp.pprint(frame)
 
                 i += 1
                 if max(payload) + i > 0xFF:
diff --git a/nixnet_examples/can_signal_conversion.py b/nixnet_examples/can_signal_conversion.py
index 98cd6c1..2378577 100644
--- a/nixnet_examples/can_signal_conversion.py
+++ b/nixnet_examples/can_signal_conversion.py
@@ -2,11 +2,15 @@ from __future__ import absolute_import
 from __future__ import division
 from __future__ import print_function
 
+import pprint
 import six
 
 from nixnet import convert
 
 
+pp = pprint.PrettyPrinter(indent=4)
+
+
 def main():
     database_name = 'NIXNET_example'
     cluster_name = 'CAN_Cluster'
@@ -31,11 +35,12 @@ def main():
         frames = session.convert_signals_to_frames(expected_signals)
         print('Frames:')
         for frame in frames:
-            print('    {}'.format(frame))
-            print('        payload={}'.format(list(six.iterbytes(frame.payload))))
+            print('    {}'.format(pp.pformat(frame)))
 
         converted_signals = session.convert_frames_to_signals(frames)
-        print('Signals: {}'.format([v for (_, v) in converted_signals]))
+        print('Signals:')
+        for expected, (_, converted) in zip(expected_signals, converted_signals):
+            print('    {} {}'.format(expected, converted))
 
 
 if __name__ == '__main__':
diff --git a/nixnet_examples/can_signal_single_point_io.py b/nixnet_examples/can_signal_single_point_io.py
index e9a5725..4b031c4 100644
--- a/nixnet_examples/can_signal_single_point_io.py
+++ b/nixnet_examples/can_signal_single_point_io.py
@@ -3,15 +3,18 @@ from __future__ import division
 from __future__ import print_function
 
 import datetime
+import pprint
+import six
 import sys
 import time
 
-import six
-
 import nixnet
 from nixnet import constants
 
 
+pp = pprint.PrettyPrinter(indent=4)
+
+
 def convert_timestamp(timestamp):
     system_epoch = time.gmtime(0)
     system_epock_datetime = datetime.datetime(system_epoch.tm_year, system_epoch.tm_mon, system_epoch.tm_mday)
@@ -81,7 +84,7 @@ def main():
                 signals = input_session.signals.read()
                 for timestamp, value in signals:
                     date = convert_timestamp(timestamp)
-                    print('Received signal with timestamp {} and value {}'.format(date, value))
+                    print('Received signal with timepstamp {} and value {}'.format(date, value))
 
                 i += 1
                 if max(value_buffer) + i > sys.float_info.max:


Test patch:
diff --git a/tests/test_examples.py b/tests/test_examples.py
index 1e4f532..6fe9400 100644
--- a/tests/test_examples.py
+++ b/tests/test_examples.py
@@ -2,7 +2,6 @@ from __future__ import absolute_import
 from __future__ import division
 from __future__ import print_function
 
-import copy
 import mock  # type: ignore
 
 import pytest  # type: ignore
@@ -32,8 +31,6 @@ MockXnetLibrary.nx_clear.return_value = _ctypedefs.u32(0)
 
 
 def six_input(queue):
-    # Leave `input_values` alone for easier debugging
-    queue = copy.copy(queue)
     queue.reverse()
 
     def _six_input(prompt=""):
diff --git a/tests/test_frames.py b/tests/test_frames.py
index 0380ade..ce8d50b 100644
--- a/tests/test_frames.py
+++ b/tests/test_frames.py
@@ -35,7 +35,7 @@ def test_iterate_frames_with_empty_payload():
     payload = b'\x00\x00\x00\x00\x00\x00\x00\x00'
     empty_bytes = b'\x01\x00\x00\x00\x00\x00\x00\x00\x02\x00\x00\x00\x00\x04\x05\x00' + payload
     (empty_frame, ) = list(_frames.iterate_frames(empty_bytes))
-    assert repr(empty_frame) == 'RawFrame(timestamp=0x1, identifier=0x2, type=FrameType.CAN_DATA, flags=0x4, info=0x5)'
+    assert repr(empty_frame) == 'RawFrame(timestamp=0x1, identifier=0x2, type=FrameType.CAN_DATA, flags=0x4, info=0x5, payload=...)'  # NOQA: E501
     assert empty_frame.payload == b''
 
 
@@ -43,14 +43,14 @@ def test_iterate_frames_with_base_payload():
     payload = b'\x01\x02\x03\x04\x05\x06\x07\x08'
     base_bytes = b'\x06\x00\x00\x00\x00\x00\x00\x00\x07\x00\x00\x00\x00\x08\x09\x08' + payload
     (base_frame, ) = list(_frames.iterate_frames(base_bytes))
-    assert repr(base_frame) == 'RawFrame(timestamp=0x6, identifier=0x7, type=FrameType.CAN_DATA, flags=0x8, info=0x9, len(payload)=8)'  # NOQA: E501
+    assert repr(base_frame) == 'RawFrame(timestamp=0x6, identifier=0x7, type=FrameType.CAN_DATA, flags=0x8, info=0x9, payload=...)'  # NOQA: E501
     assert base_frame.payload == b'\x01\x02\x03\x04\x05\x06\x07\x08'
 
 
 def test_iterate_frames_with_partial_base_payload():
     frame_bytes = b'\xd8\xb7@B\xeb\xff\xd2\x01\x00\x00\x00\x00\x00\x00\x00\x04\x02\x04\x08\x10\x00\x00\x00\x00'
     (frame, ) = list(_frames.iterate_frames(frame_bytes))
-    assert repr(frame) == 'RawFrame(timestamp=0x1d2ffeb4240b7d8, identifier=0x0, type=FrameType.CAN_DATA, len(payload)=4)'  # NOQA: E501
+    assert repr(frame) == 'RawFrame(timestamp=0x1d2ffeb4240b7d8, identifier=0x0, type=FrameType.CAN_DATA, flags=0x0, info=0x0, payload=...)'  # NOQA: E501
     assert frame.payload == b'\x02\x04\x08\x10'