From 15b814171ea048653f5bcd4db625c4b855557ef3 Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Wed, 29 Nov 2017 18:52:36 +0100 Subject: [PATCH 1/9] improvements in rpc.py to handle timeouts and fragmentation --- pyvisa-py/protocols/rpc.py | 153 +++++++++++++++++++++++++++---------- 1 file changed, 113 insertions(+), 40 deletions(-) diff --git a/pyvisa-py/protocols/rpc.py b/pyvisa-py/protocols/rpc.py index 5fe66c29..17c3db1f 100644 --- a/pyvisa-py/protocols/rpc.py +++ b/pyvisa-py/protocols/rpc.py @@ -29,6 +29,7 @@ import xdrlib import socket import select +import time from pyvisa.compat import struct @@ -293,39 +294,114 @@ def sendfrag(sock, last, frag): sock.send(header + frag) -def sendrecord(sock, record): +def _sendrecord(sock, record, fragsize = None, timeout = None): logger.debug('Sending record through %s: %r', sock, record) - sendfrag(sock, 1, record) - - -def recvfrag(sock): - header = sock.recv(4) - if len(header) < 4: - raise EOFError - x = struct.unpack(">I", header[0:4])[0] - last = ((x & 0x80000000) != 0) - n = int(x & 0x7fffffff) - frag = b'' - while n > 0: - buf = sock.recv(n) - if not buf: - raise EOFError - n -= len(buf) - frag += buf - - return last, frag - - -def recvrecord(sock): - record = b'' - last = 0 + if timeout is not None: + r, w, x = select.select([], [sock], [], timeout) + if sock not in w: + raise socket.timeout("socket.timeout: The instrument seems to have stopped responding.") + + last = False + if not fragsize: + fragsize = 0x7fffffff while not last: - last, frag = recvfrag(sock) - record = record + frag + record_len = len(record) + if record_len <= fragsize: + fragsize = record_len + last = True + if last: + fragsize = fragsize | 0x80000000 + header = struct.pack(">I", fragsize) + sock.send(header + record[:fragsize]) + record = record[fragsize:] + + +def _recvrecord(sock, timeout, read_fun = None): + + record = bytearray() + buffer = bytearray() + if not read_fun: + read_fun = sock.recv + + wait_header = True + last = False + exp_length = 4 + + # minimum is in interval 1 - 100ms based on timeout or for infinite it is 1 sec + min_select_timeout = max(min(timeout/100.0, 0.1), 0.001) if timeout is not None else 1.0 + # initial 'select_timout' is half of timeout or max 2 secs (max blocking time). + # min is from 'min_select_timeout' + select_timout = max(min(timeout/2.0, 2.0), min_select_timeout) if timeout is not None else 1.0 + # time, when loop shall finish + finish_time = time.time() + timeout if timeout is not None else 0 + while True: + + # use select to wait for read ready, max `select_timout` seconds + r, w, x = select.select([sock], [], [], select_timout) + read_data = b'' + if sock in r: + read_data = read_fun(exp_length) + buffer.extend(read_data) + + if not read_data: + if timeout is not None and time.time() >= finish_time: + # reached timeout + raise socket.timeout("socket.timeout: The instrument seems to have stopped responding.") + if record or not wait_header: + #received some data or header + raise EOFError + # `select_timout` decreased to 50% of previous or min_select_timeout + select_timout = max(select_timout/2.0, min_select_timeout) + + if wait_header: + # need tofind header + if len(buffer) >= exp_length: + header = buffer[:exp_length] + buffer = buffer[exp_length:] + x = struct.unpack(">I", header)[0] + last = ((x & 0x80000000) != 0) + exp_length = int(x & 0x7fffffff) + wait_header = False + else: + if len(buffer) >= exp_length: + record.extend(buffer[:exp_length]) + buffer = buffer[exp_length:] + if last: + logger.debug('Received record through %s: %r', sock, record) + return bytes(record) + else: + wait_header = True + exp_length = 4 + +def _connect(sock, host, port, timeout = 0): + try: + sock.setblocking(0) + sock.connect_ex((host, port)) + except Exception as e: + sock.close() + return False + finally: + sock.setblocking(1) + + # minimum is in interval 100 - 500ms based on timeout + min_select_timeout = max(min(timeout/10.0, 0.5), 0.1) + # initial 'select_timout' is half of timeout or max 2 secs (max blocking time). + # min is from 'min_select_timeout' + select_timout = max(min(timeout/2.0, 2.0), min_select_timeout) + # time, when loop shall finish + finish_time = time.time() + timeout + while True: + # use select to wait for socket ready, max `select_timout` seconds + r, w, x = select.select([sock], [sock], [], select_timout) + if sock in r or sock in w: + return True - logger.debug('Received record through %s: %r', sock, record) + if time.time() >= finish_time: + # reached timeout + return False - return record + # `select_timout` decreased to 50% of previous or min_select_timeout + select_timout = max(select_timout/2.0, min_select_timeout) class RawTCPClient(Client): @@ -333,6 +409,7 @@ class RawTCPClient(Client): """ def __init__(self, host, prog, vers, port): Client.__init__(self, host, prog, vers, port) + self.open_timeout = 5.0 self.connect() # self.timeout defaults higher than the default 2 second VISA timeout, # ensuring that VISA timeouts take precedence. @@ -359,7 +436,9 @@ def make_call(self, proc, args, pack_func, unpack_func): def connect(self): logger.debug('RawTCPClient: connecting to socket at (%s, %s)', self.host, self.port) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.sock.connect((self.host, self.port)) + if not _connect( self.sock, self.host, self.port, self.open_timeout): + raise RPCError('can\'t connect to server') + def close(self): logger.debug('RawTCPClient: closing socket') @@ -367,16 +446,10 @@ def close(self): def do_call(self): call = self.packer.get_buf() - r, w, x = select.select([], [self.sock], [], self.timeout) - if self.sock not in w: - raise socket.timeout("socket.timeout: The instrument seems to have stopped responding.") - sendrecord(self.sock, call) - r, w, x = select.select([self.sock], [], [], self.timeout) - if self.sock not in r: - raise socket.timeout("socket.timeout: The instrument seems to have stopped responding.") + _sendrecord(self.sock, call, timeout = self.timeout) - reply = recvrecord(self.sock) + reply = _recvrecord(self.sock, self.timeout) u = self.unpacker u.reset(reply) xid, verf = u.unpack_replyheader() @@ -795,7 +868,7 @@ def session(self, connection): sock, (host, port) = connection while 1: try: - call = recvrecord(sock) + call = _recvrecord(sock, None) except EOFError: break except socket.error: @@ -803,7 +876,7 @@ def session(self, connection): break reply = self.handle(call) if reply is not None: - sendrecord(sock, reply) + _sendrecord(sock, reply) def forkingloop(self): # Like loop but uses forksession() From eb51def5052276d85e3bb432fbc71a4dfb66e5bb Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Wed, 29 Nov 2017 19:41:11 +0100 Subject: [PATCH 2/9] open_timeout passed to rpc client --- pyvisa-py/protocols/rpc.py | 19 +++++++++---------- pyvisa-py/protocols/vxi11.py | 4 ++-- pyvisa-py/tcpip.py | 2 +- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/pyvisa-py/protocols/rpc.py b/pyvisa-py/protocols/rpc.py index 17c3db1f..61d81d8b 100644 --- a/pyvisa-py/protocols/rpc.py +++ b/pyvisa-py/protocols/rpc.py @@ -407,10 +407,9 @@ def _connect(sock, host, port, timeout = 0): class RawTCPClient(Client): """Client using TCP to a specific port. """ - def __init__(self, host, prog, vers, port): + def __init__(self, host, prog, vers, port, open_timeout = 5000): Client.__init__(self, host, prog, vers, port) - self.open_timeout = 5.0 - self.connect() + self.connect((open_timeout / 1000.0) + 1.0) # self.timeout defaults higher than the default 2 second VISA timeout, # ensuring that VISA timeouts take precedence. self.timeout = 4.0 @@ -433,10 +432,10 @@ def make_call(self, proc, args, pack_func, unpack_func): return super(RawTCPClient, self).make_call(proc, args, pack_func, unpack_func) - def connect(self): + def connect(self, timeout=5.0): logger.debug('RawTCPClient: connecting to socket at (%s, %s)', self.host, self.port) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if not _connect( self.sock, self.host, self.port, self.open_timeout): + if not _connect( self.sock, self.host, self.port, timeout): raise RPCError('can\'t connect to server') @@ -665,8 +664,8 @@ def callit(self, ca): class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): - def __init__(self, host): - RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT) + def __init__(self, host, open_timeout = 5000): + RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT, open_timeout) PartialPortMapperClient.__init__(self) @@ -687,13 +686,13 @@ def __init__(self, bcastaddr): class TCPClient(RawTCPClient): """A TCP Client that find their server through the Port mapper """ - def __init__(self, host, prog, vers): - pmap = TCPPortMapperClient(host) + def __init__(self, host, prog, vers, open_timeout = 5000): + pmap = TCPPortMapperClient(host, open_timeout) port = pmap.get_port((prog, vers, IPPROTO_TCP, 0)) pmap.close() if port == 0: raise RPCError('program not registered') - RawTCPClient.__init__(self, host, prog, vers, port) + RawTCPClient.__init__(self, host, prog, vers, port, open_timeout) class UDPClient(RawUDPClient): diff --git a/pyvisa-py/protocols/vxi11.py b/pyvisa-py/protocols/vxi11.py index f7b776c3..9f02df6d 100644 --- a/pyvisa-py/protocols/vxi11.py +++ b/pyvisa-py/protocols/vxi11.py @@ -191,10 +191,10 @@ def unpack_device_docmd_resp(self): class CoreClient(rpc.TCPClient): - def __init__(self, host): + def __init__(self, host, open_timeout = 5000): self.packer = Vxi11Packer() self.unpacker = Vxi11Unpacker('') - super(CoreClient, self).__init__(host, DEVICE_CORE_PROG, DEVICE_CORE_VERS) + super(CoreClient, self).__init__(host, DEVICE_CORE_PROG, DEVICE_CORE_VERS, open_timeout) def create_link(self, id, lock_device, lock_timeout, name): params = (id, lock_device, lock_timeout, name) diff --git a/pyvisa-py/tcpip.py b/pyvisa-py/tcpip.py index 8784609c..c621a813 100644 --- a/pyvisa-py/tcpip.py +++ b/pyvisa-py/tcpip.py @@ -45,7 +45,7 @@ def list_resources(): def after_parsing(self): # TODO: board_number not handled # TODO: lan_device_name not handled - self.interface = vxi11.CoreClient(self.parsed.host_address) + self.interface = vxi11.CoreClient(self.parsed.host_address, self.open_timeout) self.lock_timeout = 10000 self.client_id = random.getrandbits(31) From 473de3c667f03c321bae1b787b877b801bf7b84b Mon Sep 17 00:00:00 2001 From: Sveto Krchnavy Date: Wed, 6 Dec 2017 09:49:52 +0100 Subject: [PATCH 3/9] cleanup of unused variables --- pyvisa-py/tcpip.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pyvisa-py/tcpip.py b/pyvisa-py/tcpip.py index c621a813..b5111a13 100644 --- a/pyvisa-py/tcpip.py +++ b/pyvisa-py/tcpip.py @@ -32,11 +32,6 @@ class TCPIPInstrSession(Session): """ - lock_timeout = 1000 - client_id = None - link = None - max_recv_size = 1024 - @staticmethod def list_resources(): # TODO: is there a way to get this? @@ -47,6 +42,7 @@ def after_parsing(self): # TODO: lan_device_name not handled self.interface = vxi11.CoreClient(self.parsed.host_address, self.open_timeout) + self.max_recv_size = 1024 self.lock_timeout = 10000 self.client_id = random.getrandbits(31) From 5f3ec12b76992a33d99a04a635f52c2b63baebc1 Mon Sep 17 00:00:00 2001 From: skrchnavy Date: Wed, 20 Dec 2017 22:00:14 +0100 Subject: [PATCH 4/9] Corrected spaces in default values of arguments --- pyvisa-py/protocols/rpc.py | 12 ++++++------ pyvisa-py/protocols/vxi11.py | 2 +- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pyvisa-py/protocols/rpc.py b/pyvisa-py/protocols/rpc.py index 61d81d8b..81c59478 100644 --- a/pyvisa-py/protocols/rpc.py +++ b/pyvisa-py/protocols/rpc.py @@ -294,7 +294,7 @@ def sendfrag(sock, last, frag): sock.send(header + frag) -def _sendrecord(sock, record, fragsize = None, timeout = None): +def _sendrecord(sock, record, fragsize=None, timeout=None): logger.debug('Sending record through %s: %r', sock, record) if timeout is not None: r, w, x = select.select([], [sock], [], timeout) @@ -316,7 +316,7 @@ def _sendrecord(sock, record, fragsize = None, timeout = None): record = record[fragsize:] -def _recvrecord(sock, timeout, read_fun = None): +def _recvrecord(sock, timeout, read_fun=None): record = bytearray() buffer = bytearray() @@ -373,7 +373,7 @@ def _recvrecord(sock, timeout, read_fun = None): wait_header = True exp_length = 4 -def _connect(sock, host, port, timeout = 0): +def _connect(sock, host, port, timeout=0): try: sock.setblocking(0) sock.connect_ex((host, port)) @@ -407,7 +407,7 @@ def _connect(sock, host, port, timeout = 0): class RawTCPClient(Client): """Client using TCP to a specific port. """ - def __init__(self, host, prog, vers, port, open_timeout = 5000): + def __init__(self, host, prog, vers, port, open_timeout=5000): Client.__init__(self, host, prog, vers, port) self.connect((open_timeout / 1000.0) + 1.0) # self.timeout defaults higher than the default 2 second VISA timeout, @@ -664,7 +664,7 @@ def callit(self, ca): class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): - def __init__(self, host, open_timeout = 5000): + def __init__(self, host, open_timeout=5000): RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT, open_timeout) PartialPortMapperClient.__init__(self) @@ -686,7 +686,7 @@ def __init__(self, bcastaddr): class TCPClient(RawTCPClient): """A TCP Client that find their server through the Port mapper """ - def __init__(self, host, prog, vers, open_timeout = 5000): + def __init__(self, host, prog, vers, open_timeout=5000): pmap = TCPPortMapperClient(host, open_timeout) port = pmap.get_port((prog, vers, IPPROTO_TCP, 0)) pmap.close() diff --git a/pyvisa-py/protocols/vxi11.py b/pyvisa-py/protocols/vxi11.py index 9f02df6d..c5e0ddd1 100644 --- a/pyvisa-py/protocols/vxi11.py +++ b/pyvisa-py/protocols/vxi11.py @@ -191,7 +191,7 @@ def unpack_device_docmd_resp(self): class CoreClient(rpc.TCPClient): - def __init__(self, host, open_timeout = 5000): + def __init__(self, host, open_timeout=5000): self.packer = Vxi11Packer() self.unpacker = Vxi11Unpacker('') super(CoreClient, self).__init__(host, DEVICE_CORE_PROG, DEVICE_CORE_VERS, open_timeout) From e87660476f18b18a7af0189d75cc9e2394821d9b Mon Sep 17 00:00:00 2001 From: skrchnavy Date: Wed, 20 Dec 2017 22:38:42 +0100 Subject: [PATCH 5/9] small fix in argument also in call for --- pyvisa-py/protocols/rpc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyvisa-py/protocols/rpc.py b/pyvisa-py/protocols/rpc.py index 81c59478..2466d936 100644 --- a/pyvisa-py/protocols/rpc.py +++ b/pyvisa-py/protocols/rpc.py @@ -446,7 +446,7 @@ def close(self): def do_call(self): call = self.packer.get_buf() - _sendrecord(self.sock, call, timeout = self.timeout) + _sendrecord(self.sock, call, timeout=self.timeout) reply = _recvrecord(self.sock, self.timeout) u = self.unpacker From 231271a2331bc78a6762bf48804cf56902785dee Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Fri, 9 Feb 2018 16:27:22 +0100 Subject: [PATCH 6/9] pep8 and rebased on #126 --- pyvisa-py/sessions.py | 111 ++++++++++++++++++++++----------- pyvisa-py/tcpip.py | 138 +++++++++++++++++++++++++++--------------- 2 files changed, 162 insertions(+), 87 deletions(-) diff --git a/pyvisa-py/sessions.py b/pyvisa-py/sessions.py index 9092d6f7..ae3e17fe 100644 --- a/pyvisa-py/sessions.py +++ b/pyvisa-py/sessions.py @@ -49,7 +49,8 @@ class Session(compat.with_metaclass(abc.ABCMeta)): Just makes sure that common methods are defined and information is stored. - :param resource_manager_session: The session handle of the parent Resource Manager + :param resource_manager_session: The session handle of the parent Resource + Manager :param resource_name: The resource name. :param parsed: the parsed resource name (optional). If not provided, the resource_name will be parsed. @@ -62,7 +63,8 @@ def _get_attribute(self, attribute): Use to implement custom logic for attributes. :param attribute: Resource attribute for which the state query is made - :return: The state of the queried attribute for a specified resource, return value of the library call. + :return: The state of the queried attribute for a specified resource, + return value of the library call. :rtype: (unicode | str | list | int, VISAStatus) """ @@ -83,7 +85,8 @@ def close(self): """Close the session. Use it to do final clean ups. """ - #: Maps (Interface Type, Resource Class) to Python class encapsulating that resource. + #: Maps (Interface Type, Resource Class) to Python class encapsulating that + #: resource. #: dict[(Interface Type, Resource Class) , Session] _session_classes = dict() @@ -126,7 +129,8 @@ def get_session_class(cls, interface_type, resource_class): try: return cls._session_classes[(interface_type, resource_class)] except KeyError: - raise ValueError('No class registered for %s, %s' % (interface_type, resource_class)) + raise ValueError('No class registered for %s, %s' % + (interface_type, resource_class)) @classmethod def register(cls, interface_type, resource_class): @@ -137,17 +141,22 @@ def register(cls, interface_type, resource_class): """ def _internal(python_class): if (interface_type, resource_class) in cls._session_classes: - logger.warning('%s is already registered in the ResourceManager. ' - 'Overwriting with %s' % ((interface_type, resource_class), python_class)) + logger.warning('%s is already registered in the ' + 'ResourceManager. Overwriting with %s', + ((interface_type, resource_class), python_class) + ) python_class.session_type = (interface_type, resource_class) - cls._session_classes[(interface_type, resource_class)] = python_class + cls._session_classes[(interface_type, + resource_class)] = python_class return python_class return _internal @classmethod def register_unavailable(cls, interface_type, resource_class, msg): - """Register an unavailable session class for a given interface type and resource class. + """Register an unavailable session class for a given interface type and + resource class. + raising a ValueError if called. :type interface_type: constants.InterfaceType @@ -161,11 +170,13 @@ def _internal(*args, **kwargs): if (interface_type, resource_class) in cls._session_classes: logger.warning('%s is already registered in the ResourceManager. ' - 'Overwriting with unavailable %s' % ((interface_type, resource_class), msg)) + 'Overwriting with unavailable %s', + ((interface_type, resource_class), msg)) cls._session_classes[(interface_type, resource_class)] = _internal - def __init__(self, resource_manager_session, resource_name, parsed=None, open_timeout=None): + def __init__(self, resource_manager_session, resource_name, parsed=None, + open_timeout=None): if isinstance(resource_name, common.MockInterface): parsed = rname.parse_resource_name(resource_name.resource_name) parsed['mock'] = resource_name @@ -176,53 +187,71 @@ def __init__(self, resource_manager_session, resource_name, parsed=None, open_ti self.parsed = parsed self.open_timeout = open_timeout #: get default timeout from constants - self.timeout = attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default / 1000.0 + self.timeout =\ + (attributes.AttributesByID[constants.VI_ATTR_TMO_VALUE].default / + 1000.0) - #: Used as a place holder for the object doing the lowlevel communication. + #: Used as a place holder for the object doing the lowlevel + #: communication. self.interface = None #: Used for attributes not handled by the underlying interface. - #: Values are get or set automatically by get_attribute and set_attribute + #: Values are get or set automatically by get_attribute and + #: set_attribute #: Add your own by overriding after_parsing. self.attrs = {constants.VI_ATTR_RM_SESSION: resource_manager_session, constants.VI_ATTR_RSRC_NAME: str(parsed), constants.VI_ATTR_RSRC_CLASS: parsed.resource_class, constants.VI_ATTR_INTF_TYPE: parsed.interface_type, - constants.VI_ATTR_TMO_VALUE: (self._get_timeout, self._set_timeout)} + constants.VI_ATTR_TMO_VALUE: (self._get_timeout, + self._set_timeout)} self.after_parsing() def after_parsing(self): """Override this method to provide custom initialization code, to be called after the resourcename is properly parsed - ResourceSession can register resource specific attributes handling of them into self.attrs. - It is also possible to change handling of already registerd common attributes. List of attributes is available in pyvisa package: + ResourceSession can register resource specific attributes handling of + them into self.attrs. + It is also possible to change handling of already registerd common + attributes. List of attributes is available in pyvisa package: * name is in constants module as: VI_ATTR_ - * validity of attribute for resource is defined module attributes, AttrVI_ATTR_.resources + * validity of attribute for resource is defined module attributes, + AttrVI_ATTR_.resources - For static (read only) values, simple readonly and also readwrite attributes simplified construction can be used: + For static (read only) values, simple readonly and also readwrite + attributes simplified construction can be used: ` self.attrs[constants.VI_ATTR_] = 100` or ` self.attrs[constants.VI_ATTR_] = ` - For more complex handling of attributes, it is possible to register getter and/or setter. When Null is used, NotSupported error is returned. - Getter has same signature as see Session._get_attribute and setter has same signature as see Session._set_attribute. (It is possible to register also - see Session._get_attribute and see Session._set_attribute as getter/setter). Getter and Setter are registered as tupple. + For more complex handling of attributes, it is possible to register + getter and/or setter. When Null is used, NotSupported error is + returned. + Getter has same signature as see Session._get_attribute and setter has + same signature as see Session._set_attribute. (It is possible to + register also see Session._get_attribute and see Session._set_attribute + as getter/setter). Getter and Setter are registered as tupple. For readwrite attribute: - ` self.attrs[constants.VI_ATTR_] = (, )` + ` self.attrs[constants.VI_ATTR_] = (, + )` For readonly attribute: ` self.attrs[constants.VI_ATTR_] = (, None)` - For reusing of see Session._get_attribute and see Session._set_attribute - ` self.attrs[constants.VI_ATTR_] = (self._get_attribute, self._set_attribute)` + For reusing of see Session._get_attribute and see + Session._set_attribute + ` self.attrs[constants.VI_ATTR_] = (self._get_attribute, + self._set_attribute)` """ def get_attribute(self, attribute): """Get the value for a given VISA attribute for this session. - Does a few checks before and calls before dispatching to `_get_attribute`. + Does a few checks before and calls before dispatching to + `_get_attribute`. :param attribute: Resource attribute for which the state query is made - :return: The state of the queried attribute for a specified resource, return value of the library call. + :return: The state of the queried attribute for a specified resource, + return value of the library call. :rtype: (unicode | str | list | int, VISAStatus) """ @@ -240,16 +269,18 @@ def get_attribute(self, attribute): if not attr.read: raise Exception('Do not now how to handle write only attributes.') - # First try to answer those attributes that are registered in self.attrs, see Session.after_parsing + # First try to answer those attributes that are registered in + # self.attrs, see Session.after_parsing if attribute in self.attrs: value = self.attrs[attribute] status = StatusCode.success if isinstance(value, tuple): getter = value[0] - value, status = getter(attribute) if getter else (0, StatusCode.error_nonsupported_attribute) + value, status = (getter(attribute) if getter else + (0, StatusCode.error_nonsupported_attribute)) return value, status - # Dispatch to `_get_attribute`, which must be implemented by subclasses. + # Dispatch to `_get_attribute`, which must be implemented by subclasses try: return self._get_attribute(attribute) @@ -258,9 +289,11 @@ def get_attribute(self, attribute): return 0, StatusCode.error_nonsupported_attribute def set_attribute(self, attribute, attribute_state): - """Set the attribute_state value for a given VISA attribute for this session. + """Set the attribute_state value for a given VISA attribute for this + session. - Does a few checks before and calls before dispatching to `_gst_attribute`. + Does a few checks before and calls before dispatching to + `_gst_attribute`. :param attribute: Resource attribute for which the state query is made. :param attribute_state: value. @@ -282,18 +315,20 @@ def set_attribute(self, attribute, attribute_state): if not attr.write: return StatusCode.error_attribute_read_only - # First try to answer those attributes that are registered in self.attrs, see Session.after_parsing + # First try to answer those attributes that are registered in + # self.attrs, see Session.after_parsing if attribute in self.attrs: value = self.attrs[attribute] status = StatusCode.success if isinstance(value, tuple): setter = value[1] - status = setter(attribute, attribute_state) if setter else StatusCode.error_nonsupported_attribute + status = (setter(attribute, attribute_state) if setter else + StatusCode.error_nonsupported_attribute) else: self.attrs[attribute] = attribute_state return status - # Dispatch to `_set_attribute`, which must be implemented by subclasses. + # Dispatch to `_set_attribute`, which must be implemented by subclasses try: return self._set_attribute(attribute, attribute_state) @@ -317,7 +352,8 @@ def _read(self, reader, count, end_indicator_checker, suppress_end_en, :type reader: () -> bytes :param count: Number of bytes to be read. :type count: int - :param end_indicator_checker: Function to check if the message is complete. + :param end_indicator_checker: Function to check if the message is + complete. :type end_indicator_checker: (bytes) -> boolean :param suppress_end_en: suppress end. :type suppress_end_en: bool @@ -325,7 +361,8 @@ def _read(self, reader, count, end_indicator_checker, suppress_end_en, :type suppress_end_en: int or str :param termination_char_en: termination char enabled. :type termination_char_en: boolean - :param: timeout_exception: Exception to capture time out for the given interface. + :param: timeout_exception: Exception to capture time out for the given + interface. :type: Exception :return: data read, return value of the library call. :rtype: bytes, constants.StatusCode @@ -395,4 +432,4 @@ def _set_timeout(self, attribute, value): self.timeout = 0 else: self.timeout = value / 1000.0 - return StatusCode.success; + return StatusCode.success diff --git a/pyvisa-py/tcpip.py b/pyvisa-py/tcpip.py index b5111a13..135c94cf 100644 --- a/pyvisa-py/tcpip.py +++ b/pyvisa-py/tcpip.py @@ -40,7 +40,8 @@ def list_resources(): def after_parsing(self): # TODO: board_number not handled # TODO: lan_device_name not handled - self.interface = vxi11.CoreClient(self.parsed.host_address, self.open_timeout) + self.interface = vxi11.CoreClient(self.parsed.host_address, + self.open_timeout) self.max_recv_size = 1024 self.lock_timeout = 10000 @@ -57,7 +58,8 @@ def after_parsing(self): for name in ('SEND_END_EN', 'TERMCHAR', 'TERMCHAR_EN'): attribute = getattr(constants, 'VI_ATTR_' + name) - self.attrs[attribute] = attributes.AttributesByID[attribute].default + self.attrs[attribute] =\ + attributes.AttributesByID[attribute].default def close(self): try: @@ -127,7 +129,8 @@ def write(self, data): :param data: data to be written. :type data: str - :return: Number of bytes actually transferred, return value of the library call. + :return: Number of bytes actually transferred, return value of the + library call. :rtype: int, VISAStatus """ @@ -174,7 +177,8 @@ def _get_attribute(self, attribute): Use to implement custom logic for attributes. :param attribute: Resource attribute for which the state query is made - :return: The state of the queried attribute for a specified resource, return value of the library call. + :return: The state of the queried attribute for a specified resource, + return value of the library call. :rtype: (unicode | str | list | int, VISAStatus) """ @@ -206,8 +210,10 @@ def _set_attribute(self, attribute, attribute_state): Corresponds to viSetAttribute function of the VISA library. - :param attribute: Attribute for which the state is to be modified. (Attributes.*) - :param attribute_state: The state of the attribute to be set for the specified object. + :param attribute: Attribute for which the state is to be modified. + (Attributes.*) + :param attribute_state: The state of the attribute to be set for the + specified object. :return: return value of the library call. :rtype: VISAStatus """ @@ -219,7 +225,8 @@ def assert_trigger(self, protocol): Corresponds to viAssertTrigger function of the VISA library. - :param protocol: Trigger protocol to use during assertion. (Constants.PROT*) + :param protocol: Trigger protocol to use during assertion. + (Constants.PROT*) :return: return value of the library call. :rtype: VISAStatus """ @@ -275,11 +282,15 @@ def lock(self, lock_type, timeout, requested_key=None): Corresponds to viLock function of the VISA library. - :param lock_type: Specifies the type of lock requested, either Constants.EXCLUSIVE_LOCK or Constants.SHARED_LOCK. - :param timeout: Absolute time period (in milliseconds) that a resource waits to get unlocked by the - locking session before returning an error. - :param requested_key: This parameter is not used and should be set to VI_NULL when lockType is VI_EXCLUSIVE_LOCK. - :return: access_key that can then be passed to other sessions to share the lock, return value of the library call. + :param lock_type: Specifies the type of lock requested, either + Constants.EXCLUSIVE_LOCK or Constants.SHARED_LOCK. + :param timeout: Absolute time period (in milliseconds) that a resource + waits to get unlocked by the locking session before returning an + error. + :param requested_key: This parameter is not used and should be set to + VI_NULL when lockType is VI_EXCLUSIVE_LOCK. + :return: access_key that can then be passed to other sessions to share + the lock, return value of the library call. :rtype: str, VISAStatus """ @@ -309,16 +320,17 @@ def unlock(self): @Session.register(constants.InterfaceType.tcpip, 'SOCKET') class TCPIPSocketSession(Session): - """A TCPIP Session that uses the network standard library to do the low level communication. + """A TCPIP Session that uses the network standard library to do the low + level communication. + """ # Details about implementation: - # On Windows, select is not interrupted by KeyboardInterrupt, to avoid blocking - # for very long time, we use a decreasing timeout in select - # minimum select timeout to avoid too short select interval is also calculated - # and select timeout is not lower that that minimum timeout + # On Windows, select is not interrupted by KeyboardInterrupt, to avoid + # blocking for very long time, we use a decreasing timeout in select + # minimum select timeout to avoid too short select interval is also + # calculated and select timeout is not lower that that minimum timeout # Tis is valid for connect and read operations - @staticmethod def list_resources(): # TODO: is there a way to get this? @@ -333,28 +345,33 @@ def after_parsing(self): raise Exception("could not connect: {0}".format(str(ret_status))) self.max_recv_size = 4096 - # This buffer is used to store the bytes that appeared after termination char + # This buffer is used to store the bytes that appeared after + # termination char self._pending_buffer = bytearray() self.attrs[constants.VI_ATTR_TCPIP_ADDR] = self.parsed.host_address self.attrs[constants.VI_ATTR_TCPIP_PORT] = self.parsed.port self.attrs[constants.VI_ATTR_INTF_NUM] = self.parsed.board - self.attrs[constants.VI_ATTR_TCPIP_NODELAY] = (self._get_tcpip_nodelay, self._set_attribute) + self.attrs[constants.VI_ATTR_TCPIP_NODELAY] = (self._get_tcpip_nodelay, + self._set_attribute) self.attrs[constants.VI_ATTR_TCPIP_HOSTNAME] = '' - self.attrs[constants.VI_ATTR_TCPIP_KEEPALIVE] = (self._get_tcpip_keepalive, self._set_tcpip_keepalive) + self.attrs[constants.VI_ATTR_TCPIP_KEEPALIVE] = \ + (self._get_tcpip_keepalive, self._set_tcpip_keepalive) # to use default as ni visa driver (NI-VISA 15.0) self.attrs[constants.VI_ATTR_SUPPRESS_END_EN] = True for name in ('TERMCHAR', 'TERMCHAR_EN'): attribute = getattr(constants, 'VI_ATTR_' + name) - self.attrs[attribute] = attributes.AttributesByID[attribute].default + self.attrs[attribute] =\ + attributes.AttributesByID[attribute].default def _connect(self): timeout = self.open_timeout / 1000.0 if self.open_timeout else 10.0 try: self.interface = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.interface.setblocking(0) - self.interface.connect_ex((self.parsed.host_address, int(self.parsed.port))) + self.interface.connect_ex((self.parsed.host_address, + int(self.parsed.port))) except Exception as e: raise Exception("could not connect: {0}".format(str(e))) finally: @@ -362,14 +379,15 @@ def _connect(self): # minimum is in interval 100 - 500ms based on timeout min_select_timeout = max(min(timeout/10.0, 0.5), 0.1) - # initial 'select_timout' is half of timeout or max 2 secs (max blocking time). - # min is from 'min_select_timeout' + # initial 'select_timout' is half of timeout or max 2 secs + # (max blocking time). min is from 'min_select_timeout' select_timout = max(min(timeout/2.0, 2.0), min_select_timeout) # time, when loop shall finish finish_time = time.time() + timeout while True: # use select to wait for socket ready, max `select_timout` seconds - r, w, x = select.select([self.interface], [self.interface], [], select_timout) + r, w, x = select.select([self.interface], [self.interface], [], + select_timout) if self.interface in r or self.interface in w: return StatusCode.success @@ -377,7 +395,8 @@ def _connect(self): # reached timeout return StatusCode.error_timeout - # `select_timout` decreased to 50% of previous or min_select_timeout + # `select_timout` decreased to 50% of previous or + # min_select_timeout select_timout = max(select_timout / 2.0, min_select_timeout) def close(self): @@ -401,20 +420,27 @@ def read(self, count): term_char, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR) term_byte = common.int_to_byte(term_char) if term_char else b'' term_char_en, _ = self.get_attribute(constants.VI_ATTR_TERMCHAR_EN) - suppress_end_en, _ = self.get_attribute(constants.VI_ATTR_SUPPRESS_END_EN) + suppress_end_en, _ =\ + self.get_attribute(constants.VI_ATTR_SUPPRESS_END_EN) read_fun = self.interface.recv - # minimum is in interval 1 - 100ms based on timeout, 1sec if no timeut defined - min_select_timeout = 1 if self.timeout is None else max(min(self.timeout / 100.0, 0.1), 0.001) - # initial 'select_timout' is half of timeout or max 2 secs (max blocking time). - # min is from 'min_select_timeout' - select_timout = 2.0 if self.timeout is None else max(min(self.timeout / 2.0, 2.0), min_select_timeout) - # time, when loop shall finish, None means never ending story if no data arrives - finish_time = None if self.timeout is None else (time.time() + self.timeout) + # minimum is in interval 1 - 100ms based on timeout, 1sec if no timeout + # defined + min_select_timeout = (1 if self.timeout is None else + max(min(self.timeout / 100.0, 0.1), 0.001)) + # initial 'select_timout' is half of timeout or max 2 secs + # (max blocking time). min is from 'min_select_timeout' + select_timout = (2.0 if self.timeout is None else + max(min(self.timeout / 2.0, 2.0), min_select_timeout)) + # time, when loop shall finish, None means never ending story if no + # data arrives + finish_time = (None if self.timeout is None else + (time.time() + self.timeout)) while True: - # check, if we have any data received (from pending buffer or further reading) + # check, if we have any data received (from pending buffer or + # further reading) if term_char_en and term_byte in self._pending_buffer: term_byte_index = self._pending_buffer.index(term_byte) + 1 if term_byte_index > count: @@ -442,7 +468,8 @@ def read(self, count): if not read_data: # can't read chunk or timeout if self._pending_buffer and not suppress_end_en: - # we have some data without termchar but no further data expected + # we have some data without termchar but no further data + # expected out = bytes(self._pending_buffer[:count]) self._pending_buffer = self._pending_buffer[count:] return out, StatusCode.succes @@ -453,7 +480,8 @@ def read(self, count): self._pending_buffer = self._pending_buffer[count:] return out, StatusCode.error_timeout - # `select_timout` decreased to 50% of previous or min_select_timeout + # `select_timout` decreased to 50% of previous or + # min_select_timeout select_timout = max(select_timout / 2.0, min_select_timeout) def write(self, data): @@ -463,7 +491,8 @@ def write(self, data): :param data: data to be written. :type data: str - :return: Number of bytes actually transferred, return value of the library call. + :return: Number of bytes actually transferred, return value of the + library call. :rtype: int, VISAStatus """ @@ -494,26 +523,32 @@ def write(self, data): def _get_tcpip_nodelay(self, attribute): if self.interface: - value = self.interface.getsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY) - return constants.VI_TRUE if value == 1 else constants.VI_FALSE, StatusCode.succes + value = self.interface.getsockopt(socket.IPPROTO_TCP, + socket.TCP_NODELAY) + return (constants.VI_TRUE if value == 1 else + constants.VI_FALSE, StatusCode.succes) return 0, StatusCode.error_nonsupported_attribute def _set_tcpip_nodelay(self, attribute, attribute_state): if self.interface: - self.interface.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1 if attribute_state else 0) - return StatusCode.succes + self.interface.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, + 1 if attribute_state else 0) + return StatusCode.succes return 0, StatusCode.error_nonsupported_attribute def _get_tcpip_keepalive(self, attribute): if self.interface: - value = self.interface.getsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE) - return constants.VI_TRUE if value == 1 else constants.VI_FALSE, StatusCode.succes + value = self.interface.getsockopt(socket.SOL_SOCKET, + socket.SO_KEEPALIVE) + return (constants.VI_TRUE if value == 1 else + constants.VI_FALSE, StatusCode.succes) return 0, StatusCode.error_nonsupported_attribute def _set_tcpip_keepalive(self, attribute, attribute_state): if self.interface: - self.interface.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1 if attribute_state else 0) - return StatusCode.succes + self.interface.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, + 1 if attribute_state else 0) + return StatusCode.succes return 0, StatusCode.error_nonsupported_attribute def _get_attribute(self, attribute): @@ -522,7 +557,8 @@ def _get_attribute(self, attribute): Use to implement custom logic for attributes. :param attribute: Resource attribute for which the state query is made - :return: The state of the queried attribute for a specified resource, return value of the library call. + :return: The state of the queried attribute for a specified resource, + return value of the library call. :rtype: (unicode | str | list | int, VISAStatus) """ raise UnknownAttribute(attribute) @@ -532,8 +568,10 @@ def _set_attribute(self, attribute, attribute_state): Corresponds to viSetAttribute function of the VISA library. - :param attribute: Attribute for which the state is to be modified. (Attributes.*) - :param attribute_state: The state of the attribute to be set for the specified object. + :param attribute: Attribute for which the state is to be modified. + (Attributes.*) + :param attribute_state: The state of the attribute to be set for the + specified object. :return: return value of the library call. :rtype: VISAStatus """ From ec74f62d18560aa5101b477c7267a99411a6e0fa Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Tue, 27 Feb 2018 16:29:33 +0100 Subject: [PATCH 7/9] more pep8 formatting --- pyvisa-py/common.py | 3 +- pyvisa-py/protocols/rpc.py | 134 +++++++++++++++++++++-------------- pyvisa-py/protocols/vxi11.py | 25 ++++--- pyvisa-py/sessions.py | 6 +- 4 files changed, 102 insertions(+), 66 deletions(-) diff --git a/pyvisa-py/common.py b/pyvisa-py/common.py index 1f91543b..acf9edc2 100644 --- a/pyvisa-py/common.py +++ b/pyvisa-py/common.py @@ -9,7 +9,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 logging import sys diff --git a/pyvisa-py/protocols/rpc.py b/pyvisa-py/protocols/rpc.py index 2466d936..bfa13b5d 100644 --- a/pyvisa-py/protocols/rpc.py +++ b/pyvisa-py/protocols/rpc.py @@ -13,16 +13,16 @@ XXX The UDP version of the protocol resends requests when it does XXX not receive a timely reply -- use only for idempotent calls! - XXX There is no provision for call timeout on TCP connections - - Original source: http://svn.python.org/projects/python/trunk/Demo/rpc/rpc.py + Original source: + http://svn.python.org/projects/python/trunk/Demo/rpc/rpc.py :copyright: 2014 by PyVISA-py Authors, see AUTHORS for more details. :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 sys import enum @@ -199,7 +199,8 @@ def unpack_replyheader(self): if stat == RejectStatus.rpc_mismatch: low = self.unpack_uint() high = self.unpack_uint() - raise RPCUnpackError('denied: rpc_mismatch: %r' % ((low, high),)) + raise RPCUnpackError('denied: rpc_mismatch: %r' % + ((low, high),)) if stat == RejectStatus.auth_error: stat = self.unpack_uint() raise RPCUnpackError('denied: auth_error: %r' % (stat,)) @@ -213,7 +214,8 @@ def unpack_replyheader(self): if stat == AcceptStatus.program_mismatch: low = self.unpack_uint() high = self.unpack_uint() - raise RPCUnpackError('call failed: program_mismatch: %r' % ((low, high),)) + raise RPCUnpackError('call failed: program_mismatch: %r' % + ((low, high),)) if stat == AcceptStatus.procedure_unavailable: raise RPCUnpackError('call failed: procedure_unavailable') if stat == AcceptStatus.garbage_args: @@ -239,7 +241,8 @@ def __init__(self, host, prog, vers, port): def make_call(self, proc, args, pack_func, unpack_func): # Don't normally override this (but see Broadcast) - logger.debug('Make call %r, %r, %r, %r', proc, args, pack_func, unpack_func) + logger.debug('Make call %r, %r, %r, %r', + proc, args, pack_func, unpack_func) if pack_func is None and args is not None: raise TypeError('non-null args with null pack_func') @@ -299,7 +302,9 @@ def _sendrecord(sock, record, fragsize=None, timeout=None): if timeout is not None: r, w, x = select.select([], [sock], [], timeout) if sock not in w: - raise socket.timeout("socket.timeout: The instrument seems to have stopped responding.") + msg = ("socket.timeout: The instrument seems to have stopped " + "responding.") + raise socket.timeout(msg) last = False if not fragsize: @@ -327,11 +332,15 @@ def _recvrecord(sock, timeout, read_fun=None): last = False exp_length = 4 - # minimum is in interval 1 - 100ms based on timeout or for infinite it is 1 sec - min_select_timeout = max(min(timeout/100.0, 0.1), 0.001) if timeout is not None else 1.0 - # initial 'select_timout' is half of timeout or max 2 secs (max blocking time). + # minimum is in interval 1 - 100ms based on timeout or for infinite it is + # 1 sec + min_select_timeout = (max(min(timeout/100.0, 0.1), 0.001) + if timeout is not None else 1.0) + # initial 'select_timout' is half of timeout or max 2 secs + # (max blocking time). # min is from 'min_select_timeout' - select_timout = max(min(timeout/2.0, 2.0), min_select_timeout) if timeout is not None else 1.0 + select_timout = (max(min(timeout/2.0, 2.0), min_select_timeout) + if timeout is not None else 1.0) # time, when loop shall finish finish_time = time.time() + timeout if timeout is not None else 0 while True: @@ -342,15 +351,18 @@ def _recvrecord(sock, timeout, read_fun=None): if sock in r: read_data = read_fun(exp_length) buffer.extend(read_data) - + if not read_data: if timeout is not None and time.time() >= finish_time: # reached timeout - raise socket.timeout("socket.timeout: The instrument seems to have stopped responding.") + msg = ("socket.timeout: The instrument seems to have stopped " + "responding.") + raise socket.timeout(msg) if record or not wait_header: - #received some data or header + # received some data or header raise EOFError - # `select_timout` decreased to 50% of previous or min_select_timeout + # `select_timout` decreased to 50% of previous or + # min_select_timeout select_timout = max(select_timout/2.0, min_select_timeout) if wait_header: @@ -367,45 +379,49 @@ def _recvrecord(sock, timeout, read_fun=None): record.extend(buffer[:exp_length]) buffer = buffer[exp_length:] if last: - logger.debug('Received record through %s: %r', sock, record) + logger.debug('Received record through %s: %r', + sock, record) return bytes(record) else: wait_header = True exp_length = 4 + def _connect(sock, host, port, timeout=0): - try: - sock.setblocking(0) - sock.connect_ex((host, port)) - except Exception as e: - sock.close() + try: + sock.setblocking(0) + sock.connect_ex((host, port)) + except Exception as e: + sock.close() + return False + finally: + sock.setblocking(1) + + # minimum is in interval 100 - 500ms based on timeout + min_select_timeout = max(min(timeout/10.0, 0.5), 0.1) + # initial 'select_timout' is half of timeout or max 2 secs + # (max blocking time). + # min is from 'min_select_timeout' + select_timout = max(min(timeout/2.0, 2.0), min_select_timeout) + # time, when loop shall finish + finish_time = time.time() + timeout + while True: + # use select to wait for socket ready, max `select_timout` seconds + r, w, x = select.select([sock], [sock], [], select_timout) + if sock in r or sock in w: + return True + + if time.time() >= finish_time: + # reached timeout return False - finally: - sock.setblocking(1) - - # minimum is in interval 100 - 500ms based on timeout - min_select_timeout = max(min(timeout/10.0, 0.5), 0.1) - # initial 'select_timout' is half of timeout or max 2 secs (max blocking time). - # min is from 'min_select_timeout' - select_timout = max(min(timeout/2.0, 2.0), min_select_timeout) - # time, when loop shall finish - finish_time = time.time() + timeout - while True: - # use select to wait for socket ready, max `select_timout` seconds - r, w, x = select.select([sock], [sock], [], select_timout) - if sock in r or sock in w: - return True - - if time.time() >= finish_time: - # reached timeout - return False - # `select_timout` decreased to 50% of previous or min_select_timeout - select_timout = max(select_timout/2.0, min_select_timeout) + # `select_timout` decreased to 50% of previous or min_select_timeout + select_timout = max(select_timout/2.0, min_select_timeout) class RawTCPClient(Client): """Client using TCP to a specific port. + """ def __init__(self, host, prog, vers, port, open_timeout=5000): Client.__init__(self, host, prog, vers, port) @@ -415,7 +431,8 @@ def __init__(self, host, prog, vers, port, open_timeout=5000): self.timeout = 4.0 def make_call(self, proc, args, pack_func, unpack_func): - """Overridden to allow for utilizing io_timeout (passed in args) + """Overridden to allow for utilizing io_timeout (passed in args). + """ if proc == 11: # vxi11.DEVICE_WRITE @@ -430,15 +447,16 @@ def make_call(self, proc, args, pack_func, unpack_func): else: self.timeout = 4.0 - return super(RawTCPClient, self).make_call(proc, args, pack_func, unpack_func) + return super(RawTCPClient, self).make_call(proc, args, pack_func, + unpack_func) def connect(self, timeout=5.0): - logger.debug('RawTCPClient: connecting to socket at (%s, %s)', self.host, self.port) + logger.debug('RawTCPClient: connecting to socket at (%s, %s)', + self.host, self.port) self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - if not _connect( self.sock, self.host, self.port, timeout): + if not _connect(self.sock, self.host, self.port, timeout): raise RPCError('can\'t connect to server') - def close(self): logger.debug('RawTCPClient: closing socket') self.sock.close() @@ -454,7 +472,8 @@ def do_call(self): xid, verf = u.unpack_replyheader() if xid != self.lastxid: # Can't really happen since this is TCP... - raise RPCError('wrong xid in reply {0} instead of {1}'.format(xid, self.lastxid)) + msg = 'wrong xid in reply {0} instead of {1}' + raise RPCError(msg.format(xid, self.lastxid)) class RawUDPClient(Client): @@ -465,7 +484,8 @@ def __init__(self, host, prog, vers, port): self.connect() def connect(self): - logger.debug('RawTCPClient: connecting to socket at (%s, %s)', self.host, self.port) + logger.debug('RawTCPClient: connecting to socket at (%s, %s)', + self.host, self.port) self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) self.sock.connect((self.host, self.port)) @@ -580,8 +600,8 @@ class PortMapperVersion(enum.IntEnum): #: (call_args) -> call_result call_it = 5 -# A mapping is (prog, vers, prot, port) and prot is one of: +# A mapping is (prog, vers, prot, port) and prot is one of: IPPROTO_TCP = 6 IPPROTO_UDP = 17 @@ -665,7 +685,8 @@ def callit(self, ca): class TCPPortMapperClient(PartialPortMapperClient, RawTCPClient): def __init__(self, host, open_timeout=5000): - RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT, open_timeout) + RawTCPClient.__init__(self, host, PMAP_PROG, PMAP_VERS, PMAP_PORT, + open_timeout) PartialPortMapperClient.__init__(self) @@ -676,10 +697,12 @@ def __init__(self, host): PartialPortMapperClient.__init__(self) -class BroadcastUDPPortMapperClient(PartialPortMapperClient, RawBroadcastUDPClient): +class BroadcastUDPPortMapperClient(PartialPortMapperClient, + RawBroadcastUDPClient): def __init__(self, bcastaddr): - RawBroadcastUDPClient.__init__(self, bcastaddr, PMAP_PROG, PMAP_VERS, PMAP_PORT) + RawBroadcastUDPClient.__init__(self, bcastaddr, PMAP_PROG, PMAP_VERS, + PMAP_PORT) PartialPortMapperClient.__init__(self) @@ -748,7 +771,8 @@ def dummy(): pass self.unpack_func = unpack_func self.replies = [] packed_args = self.packer.get_buf() - dummy_replies = self.pmap.Callit((self.prog, self.vers, proc, packed_args)) + dummy_replies = self.pmap.Callit((self.prog, self.vers, proc, + packed_args)) return self.replies diff --git a/pyvisa-py/protocols/vxi11.py b/pyvisa-py/protocols/vxi11.py index c5e0ddd1..ea570bec 100644 --- a/pyvisa-py/protocols/vxi11.py +++ b/pyvisa-py/protocols/vxi11.py @@ -13,7 +13,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 enum @@ -69,6 +70,7 @@ class ErrorCodes(enum.IntEnum): abort = 23 channel_already_established = 29 + # Flags OP_FLAG_WAIT_BLOCK = 1 OP_FLAG_END = 8 @@ -142,7 +144,8 @@ def pack_device_lock_parms(self, params): self.pack_uint(lock_timeout) def pack_device_docmd_parms(self, params): - link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in = params + (link, flags, io_timeout, lock_timeout, + cmd, network_order, datasize, data_in) = params self.pack_int(link) self.pack_int(flags) self.pack_uint(io_timeout) @@ -194,7 +197,8 @@ class CoreClient(rpc.TCPClient): def __init__(self, host, open_timeout=5000): self.packer = Vxi11Packer() self.unpacker = Vxi11Unpacker('') - super(CoreClient, self).__init__(host, DEVICE_CORE_PROG, DEVICE_CORE_VERS, open_timeout) + super(CoreClient, self).__init__(host, DEVICE_CORE_PROG, + DEVICE_CORE_VERS, open_timeout) def create_link(self, id, lock_device, lock_timeout, name): params = (id, lock_device, lock_timeout, name) @@ -208,8 +212,10 @@ def device_write(self, link, io_timeout, lock_timeout, flags, data): self.packer.pack_device_write_parms, self.unpacker.unpack_device_write_resp) - def device_read(self, link, request_size, io_timeout, lock_timeout, flags, term_char): - params = (link, request_size, io_timeout, lock_timeout, flags, term_char) + def device_read(self, link, request_size, io_timeout, lock_timeout, flags, + term_char): + params = (link, request_size, io_timeout, lock_timeout, flags, + term_char) return self.make_call(DEVICE_READ, params, self.packer.pack_device_read_parms, self.unpacker.unpack_device_read_resp) @@ -261,8 +267,10 @@ def device_enable_srq(self, link, enable, handle): self.packer.pack_device_enable_srq_parms, self.unpacker.unpack_device_error) - def device_docmd(self, link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in): - params = (link, flags, io_timeout, lock_timeout, cmd, network_order, datasize, data_in) + def device_docmd(self, link, flags, io_timeout, lock_timeout, cmd, + network_order, datasize, data_in): + params = (link, flags, io_timeout, lock_timeout, cmd, network_order, + datasize, data_in) return self.make_call(DEVICE_DOCMD, params, self.packer.pack_device_docmd_parms, self.unpacker.unpack_device_docmd_resp) @@ -272,7 +280,8 @@ def destroy_link(self, link): self.packer.pack_device_link, self.unpacker.unpack_device_error) - def create_intr_chan(self, host_addr, host_port, prog_num, prog_vers, prog_family): + def create_intr_chan(self, host_addr, host_port, prog_num, prog_vers, + prog_family): params = (host_addr, host_port, prog_num, prog_vers, prog_family) return self.make_call(CREATE_INTR_CHAN, params, self.packer.pack_device_docmd_parms, diff --git a/pyvisa-py/sessions.py b/pyvisa-py/sessions.py index ae3e17fe..f0eae965 100644 --- a/pyvisa-py/sessions.py +++ b/pyvisa-py/sessions.py @@ -242,6 +242,7 @@ def after_parsing(self): ` self.attrs[constants.VI_ATTR_] = (self._get_attribute, self._set_attribute)` """ + pass def get_attribute(self, attribute): """Get the value for a given VISA attribute for this session. @@ -412,7 +413,7 @@ def _read(self, reader, count, end_indicator_checker, suppress_end_en, return bytes(out), StatusCode.error_timeout def _get_timeout(self, attribute): - """ Returns timeout calculated value from python way to VI_ way + """ Returns timeout calculated value from python way to VI_ way """ if self.timeout is None: @@ -424,7 +425,8 @@ def _get_timeout(self, attribute): return ret_value, StatusCode.success def _set_timeout(self, attribute, value): - """ Sets timeout calculated value from python way to VI_ way + """ Sets timeout calculated value from python way to VI_ way + """ if value == constants.VI_TMO_INFINITE: self.timeout = None From eabc4c6dd3b992fc5a82879a32c490d0a04ed410 Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Tue, 27 Feb 2018 16:46:48 +0100 Subject: [PATCH 8/9] tcpip: enable read and write timeout Inifinite timeout is about 1000 hours ! --- pyvisa-py/tcpip.py | 28 +++++++++++++++------------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/pyvisa-py/tcpip.py b/pyvisa-py/tcpip.py index 135c94cf..59d20e75 100644 --- a/pyvisa-py/tcpip.py +++ b/pyvisa-py/tcpip.py @@ -40,6 +40,7 @@ def list_resources(): def after_parsing(self): # TODO: board_number not handled # TODO: lan_device_name not handled + # vx11 expect all timeouts to be expressed in ms and should be integers self.interface = vxi11.CoreClient(self.parsed.host_address, self.open_timeout) @@ -47,8 +48,9 @@ def after_parsing(self): self.lock_timeout = 10000 self.client_id = random.getrandbits(31) - error, link, abort_port, max_recv_size = self.interface.create_link( - self.client_id, 0, self.lock_timeout, self.parsed.lan_device_name) + error, link, abort_port, max_recv_size =\ + self.interface.create_link(self.client_id, 0, self.lock_timeout, + self.parsed.lan_device_name) if error: raise Exception("error creating link: %d" % error) @@ -91,9 +93,6 @@ def read(self, count): else: term_char = flags = 0 - # TODO: self.timeout shall be used as timeout, requires changes in read_fun - timeout, _ = self.get_attribute(constants.VI_ATTR_TMO_VALUE) - read_data = bytearray() reason = 0 # Stop on end of message or when a termination character has been @@ -102,8 +101,10 @@ def read(self, count): read_fun = self.interface.device_read status = StatusCode.success + timeout = int(self.timeout*1000) if self.timeout else 2**32-1 while reason & end_reason == 0: - error, reason, data = read_fun(self.link, chunk_length, timeout, + error, reason, data = read_fun(self.link, chunk_length, + timeout, self.lock_timeout, flags, term_char) if error == vxi11.ErrorCodes.io_timeout: @@ -136,8 +137,6 @@ def write(self, data): send_end, _ = self.get_attribute(constants.VI_ATTR_SEND_END_EN) chunk_size = 1024 - # TODO: self.timeout shall be used as timeout, requires changes in read_fun - timeout, _ = self.get_attribute(constants.VI_ATTR_TMO_VALUE) try: if send_end: @@ -148,6 +147,7 @@ def write(self, data): num = len(data) offset = 0 + timeout = int(self.timeout*1000) if self.timeout else 2**32-1 while num > 0: if num <= chunk_size: flags |= vxi11.OP_FLAG_END @@ -155,7 +155,8 @@ def write(self, data): block = data[offset:offset + self.max_recv_size] error, size = self.interface.device_write( - self.link, timeout, self.lock_timeout, flags, block) + self.link, timeout, self.lock_timeout, + flags, block) if error == vxi11.ErrorCodes.io_timeout: return offset, StatusCode.error_timeout @@ -326,10 +327,11 @@ class TCPIPSocketSession(Session): """ # Details about implementation: # On Windows, select is not interrupted by KeyboardInterrupt, to avoid - # blocking for very long time, we use a decreasing timeout in select - # minimum select timeout to avoid too short select interval is also - # calculated and select timeout is not lower that that minimum timeout - # Tis is valid for connect and read operations + # blocking for very long time, we use a decreasing timeout in select. + # A minimum select timeout which prevents using too short select interval + # is also calculated and select timeout is not lower that that minimum + # timeout. The absolute minimum is 1 ms as a consequence. + # This is valid for connect and read operations @staticmethod def list_resources(): From b1d0c513532eac963016f2b30bdcc7c881b8b917 Mon Sep 17 00:00:00 2001 From: Matthieu Dartiailh Date: Tue, 27 Feb 2018 19:38:01 +0100 Subject: [PATCH 9/9] tests: drop 2.6 and 3.2, 3.3 as we did on PyVISA --- .travis.yml | 9 ++------- README | 2 +- pyvisa-py/testsuite/__init__.py | 6 +++--- setup.py | 5 ----- 4 files changed, 6 insertions(+), 16 deletions(-) diff --git a/.travis.yml b/.travis.yml index ef105087..d78c8ded 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,22 +10,17 @@ branches: - master python: - - "2.6" - "2.7" - - "3.2" - - "3.3" - "3.4" - "3.5" - "3.6" install: - - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then pip install unittest2; fi - - if [ $TRAVIS_PYTHON_VERSION == '3.2' ]; then pip install coverage==3.7.1; else pip install coverage; fi + - pip install coverage - pip install coveralls script: - - if [ $TRAVIS_PYTHON_VERSION == '2.6' ]; then coverage run -p --source=pyvisa-py --omit="*test*" setup.py test; fi - - if [ $TRAVIS_PYTHON_VERSION != '2.6' ]; then python -bb -m coverage run -p --source=pyvisa-py --omit="*test*" setup.py test; fi + - python -bb -m coverage run -p --source=pyvisa-py --omit="*test*" setup.py test - coverage combine - coverage report -m diff --git a/README b/README index 88404e73..1e35b586 100644 --- a/README +++ b/README @@ -38,7 +38,7 @@ Python has a couple of features that make it very interesting for measurement co Requirements ------------ -- Python (tested with 2.6 and 2.7, 3.2+) +- Python (tested with 2.7, 3.4+) - PyVISA 1.6+ Optionally diff --git a/pyvisa-py/testsuite/__init__.py b/pyvisa-py/testsuite/__init__.py index 6157c886..27784b25 100644 --- a/pyvisa-py/testsuite/__init__.py +++ b/pyvisa-py/testsuite/__init__.py @@ -1,10 +1,11 @@ # -*- 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 -from pyvisa.compat import unittest +import unittest def testsuite(): @@ -29,4 +30,3 @@ def run(): """ test_runner = unittest.TextTestRunner() return test_runner.run(testsuite()) - diff --git a/setup.py b/setup.py index 5f0f7347..0485ff0f 100644 --- a/setup.py +++ b/setup.py @@ -30,8 +30,6 @@ def read(filename): requirements = ['pyvisa>=1.8'] -if sys.version_info < (2, 7): - requirements.append('importlib') setup(name='PyVISA-py', description='Python VISA bindings for GPIB, RS232, and USB instruments', @@ -57,10 +55,7 @@ def read(filename): 'Programming Language :: Python', 'Topic :: Scientific/Engineering :: Interface Engine/Protocol Translator', 'Topic :: Software Development :: Libraries :: Python Modules', - 'Programming Language :: Python :: 2.6', 'Programming Language :: Python :: 2.7', - 'Programming Language :: Python :: 3.2', - 'Programming Language :: Python :: 3.3', 'Programming Language :: Python :: 3.4', 'Programming Language :: Python :: 3.5', 'Programming Language :: Python :: 3.6',