Beispiel #1
0
def test_wrong_socket_type():

    address = '127.0.0.1'
    port = get_available_port()
    term = b'\r\n'

    t = threading.Thread(target=echo_server_udp, args=(address, port, term))
    t.start()

    time.sleep(0.1)  # allow some time for the echo server to start

    with pytest.raises(MSLConnectionError):
        EquipmentRecord(connection=ConnectionRecord(
            address='TCP::{}::{}'.format(address,
                                         port),  # trying TCP for a UDP server
            backend=Backend.MSL,
            properties=dict(termination=term, timeout=5),
        )).connect()

    record = EquipmentRecord(connection=ConnectionRecord(
        address='UDP::{}::{}'.format(address, port),
        backend=Backend.MSL,
        properties=dict(termination=term, timeout=5),
    ))

    # use the correct socket type to shutdown the server
    dev = record.connect()
    dev.write('SHUTDOWN')
Beispiel #2
0
def test_port_functions():
    port = utils.get_available_port()
    assert not utils.is_port_in_use(port)
    sock = socket.socket()
    sock.bind(('', port))
    sock.listen(1)
    assert utils.is_port_in_use(port)
    sock.close()
    assert not utils.is_port_in_use(port)
Beispiel #3
0
def test_udp_socket_read():

    address = '127.0.0.1'
    port = get_available_port()
    term = b'\r\n'

    t = threading.Thread(target=echo_server_udp, args=(address, port, term))
    t.start()

    time.sleep(0.1)  # allow some time for the echo server to start

    record = EquipmentRecord(connection=ConnectionRecord(
        address='UDP::{}::{}'.format(address, port),
        backend=Backend.MSL,
        properties=dict(
            termination=term,  # sets both read_termination and write_termination
            timeout=30),
    ))

    dev = record.connect()

    assert dev.read_termination == term
    assert dev.write_termination == term

    dev.write('hello')
    assert dev.read() == 'hello'

    n = dev.write('hello')
    assert dev.read(n) == 'hello' + term.decode(
    )  # specified `size` so `term` is not removed

    n = dev.write(b'021.3' + term + b',054.2')
    assert dev.read(n) == '021.3' + term.decode() + ',054.2' + term.decode(
    )  # `term` is not removed

    dev.write(b'021.3' + term + b',054.2')
    assert dev.read(3) == '021'
    assert dev.read(5) == '.3' + term.decode() + ','
    assert dev.read(
    ) == '054.2'  # read the rest -- removes the `term` at the end

    dev.write(b'021.3' + term + b',054.2')
    assert dev.read() == '021.3'  # read until first `term`
    assert dev.read() == ',054.2'  # read until second `term`

    n = dev.write('12345')
    assert n == 7
    with pytest.raises(MSLTimeoutError):
        dev.read(n + 1)  # read more bytes than are available
    assert dev.read(n) == '12345' + term.decode()
    assert len(dev.byte_buffer) == 0

    dev.write('SHUTDOWN')
Beispiel #4
0
def test_tcp_socket_timeout():

    address = '127.0.0.1'
    port = get_available_port()
    write_termination = b'\n'

    t = threading.Thread(target=echo_server_tcp,
                         args=(address, port, write_termination))
    t.start()

    time.sleep(0.1)  # allow some time for the echo server to start

    record = EquipmentRecord(connection=ConnectionRecord(
        address='TCPIP::{}::{}::SOCKET'.format(
            address, port),  # use PyVISA's address convention
        backend=Backend.MSL,
        properties=dict(write_termination=write_termination, timeout=7),
    ))

    dev = record.connect()
    assert dev.timeout == 7
    assert dev.socket.gettimeout() == 7

    dev.timeout = None
    assert dev.timeout is None
    assert dev.socket.gettimeout() is None

    dev.timeout = 0.1
    assert dev.timeout == 0.1
    assert dev.socket.gettimeout() == 0.1

    dev.timeout = 0
    assert dev.timeout is None
    assert dev.socket.gettimeout() is None

    dev.timeout = -1
    assert dev.timeout is None
    assert dev.socket.gettimeout() is None

    dev.timeout = -12345
    assert dev.timeout is None
    assert dev.socket.gettimeout() is None

    dev.timeout = 10
    assert dev.timeout == 10
    assert dev.socket.gettimeout() == 10

    dev.write('SHUTDOWN')
Beispiel #5
0
def test_timeout():
    with pytest.raises(utils.ConnectionTimeoutError):
        utils.wait_for_server('localhost', utils.get_available_port(), 2)
Beispiel #6
0
def test_port_functions():
    assert not utils.port_in_use(utils.get_available_port(), 20)
Beispiel #7
0
    def __init__(self,
                 module32,
                 host='127.0.0.1',
                 port=None,
                 timeout=10.0,
                 quiet=True,
                 append_sys_path=None,
                 append_environ_path=None,
                 **kwargs):
        """Base class for communicating with a 32-bit library from 64-bit Python.

        Starts a 32-bit server, :class:`~.server32.Server32`, to host a Python module
        that is a wrapper around a 32-bit library. The *client64* module runs within
        a 64-bit Python interpreter and it sends a request to the server which calls
        the 32-bit library to execute the request. The server then provides a
        response back to the client.

        Parameters
        ----------
        module32 : :class:`str`
            The name of the Python module that is to be imported by the 32-bit server.
        host : :class:`str`, optional
            The IP address of the 32-bit server. Default is ``'127.0.0.1'``.
        port : :class:`int`, optional
            The port to open on the 32-bit server. Default is :obj:`None`, which means
            to automatically find a port that is available.
        timeout : :class:`float`, optional
            The maximum number of seconds to wait to establish a connection to the
            32-bit server. Default is 10 seconds.
        quiet : :class:`bool`, optional
            Whether to hide :obj:`sys.stdout` messages from the 32-bit server.
            Default is :obj:`True`.
        append_sys_path : :class:`str` or :class:`list` of :class:`str`, optional
            Append path(s) to the 32-bit server's :obj:`sys.path` variable. The value of
            :obj:`sys.path` from the 64-bit process is automatically included,
            i.e., ``sys.path(32bit) = sys.path(64bit) + append_sys_path``
            Default is :obj:`None`.
        append_environ_path : :class:`str` or :class:`list` of :class:`str`, optional
            Append path(s) to the 32-bit server's :obj:`os.environ['PATH'] <os.environ>`
            variable. This can be useful if the library that is being loaded requires
            additional libraries that must be available on ``PATH``. Default is :obj:`None`.
        **kwargs
            Keyword arguments that will be passed to the :class:`~.server32.Server32`
            subclass. The data type of each value is not preserved. It will be a string
            at the constructor of the :class:`~.server32.Server32` subclass.

        Note
        ----
        If `module32` is not located in the current working directory then you
        must either specify the full path to `module32` **or** you can
        specify the folder where `module32` is located by passing a value to the
        `append_sys_path` parameter. Using the `append_sys_path` option also allows
        for any other modules that `module32` may depend on to also be included
        in :obj:`sys.path` so that those modules can be imported when `module32`
        is imported.

        Raises
        ------
        IOError
            If the frozen executable cannot be found.
        TypeError
            If the data type of `append_sys_path` or `append_environ_path` is invalid.
        :exc:`~msl.loadlib.utils.ConnectionTimeoutError`
            If the connection to the 32-bit server cannot be established.
        """

        self._is_active = False

        if port is None:
            port = utils.get_available_port()

        # the temporary file to use to save the pickle'd data
        self._pickle_temp_file = os.path.join(tempfile.gettempdir(),
                                              str(uuid.uuid4()))

        # select the highest-level pickle protocol to use based on the version of python
        major, minor = sys.version_info.major, sys.version_info.minor
        if (major <= 1) or (major == 2 and minor < 3):
            self._pickle_protocol = 1
        elif major == 2:
            self._pickle_protocol = 2
        elif (major == 3) and (minor < 4):
            self._pickle_protocol = 3
        else:
            self._pickle_protocol = pickle.HIGHEST_PROTOCOL

        # make sure that the server32 executable exists
        server_exe = os.path.join(os.path.dirname(__file__), SERVER_FILENAME)
        if not os.path.isfile(server_exe):
            raise IOError('Cannot find ' + server_exe)

        cmd = [
            server_exe, '--module', module32, '--host', host, '--port',
            str(port)
        ]

        # include paths to the 32-bit server's sys.path
        _append_sys_path = sys.path
        if append_sys_path is not None:
            if isinstance(append_sys_path, str):
                _append_sys_path.append(append_sys_path.strip())
            elif isinstance(append_sys_path, (list, tuple)):
                _append_sys_path.extend(append_sys_path)
            else:
                raise TypeError('append_sys_path must be a str, list or tuple')
        cmd.extend(['--append-sys-path', ';'.join(_append_sys_path).strip()])

        # include paths to the 32-bit server's os.environ['PATH']
        if append_environ_path is not None:
            if isinstance(append_environ_path, str):
                env_str = append_environ_path.strip()
            elif isinstance(append_environ_path, (list, tuple)):
                env_str = ';'.join(append_environ_path).strip()
            else:
                raise TypeError(
                    'append_environ_path must be a str, list or tuple')
            if env_str:
                cmd.extend(['--append-environ-path', env_str])

        # include any keyword arguments
        if kwargs:
            kw_str = ';'.join('{}={}'.format(key, value)
                              for key, value in kwargs.items())
            cmd.extend(['--kwargs', kw_str])

        if quiet:
            cmd.append('--quiet')

        # start the server, cannot use subprocess.call() because it blocks
        subprocess.Popen(cmd, stderr=sys.stderr, stdout=sys.stderr)
        utils.wait_for_server(host, port, timeout)

        # start the connection
        HTTPConnection.__init__(self, host, port)
        self._is_active = True

        self._lib32_path = self.request32('LIB32_PATH')
Beispiel #8
0
    def __init__(self, path, libtype='cdll'):
        """Load a shared library.

        For example, a C/C++, FORTRAN, CLR, Java, Delphi, LabVIEW, ... library.

        Based on the value of `libtype` or the file extension this class will load the
        shared library as a:

            * :class:`~ctypes.CDLL` if `libtype` is ``'cdll'``,
            * :class:`~ctypes.WinDLL` if `libtype` is ``'windll'``,
            * :class:`~ctypes.OleDLL` if `libtype` is ``'oledll'``,
            * `System.Reflection.Assembly <Assembly_>`_ if `libtype` is ``'net'``, or a
            * :class:`~.py4j.java_gateway.JavaGateway` if `libtype` is ``'java'``.

        .. _Assembly: https://msdn.microsoft.com/en-us/library/system.reflection.assembly(v=vs.110).aspx

        Parameters
        ----------
        path : :class:`str`
            The path to the shared library.

            The search order for finding the shared library is:

                1. assume that a full or a relative (to the current working directory)
                   path is specified,
                2. use :obj:`ctypes.util.find_library` to find the shared library file,
                3. search :obj:`sys.path`, then
                4. search :obj:`os.environ['PATH'] <os.environ>` to find the shared library.

        libtype : :class:`str`, optional
            The library type. The following values are currently supported:

            * ``'cdll'`` -- for a library that uses the __cdecl calling convention
            * ``'windll'`` or ``'oledll'`` -- for a __stdcall calling convention
            * ``'net'`` -- for Microsoft's .NET Framework (Common Language Runtime)
            * ``'java'`` -- for a Java archive, ``.jar``, or Java byte code, ``.class``, file

            Default is ``'cdll'``.

            .. note::
               Since the ``.jar`` or ``.class`` extension uniquely defines a Java library,
               the `libtype` will automatically be set to ``'java'`` if `path` ends with
               ``.jar`` or ``.class``.

        Raises
        ------
        IOError
            If the shared library cannot be loaded.
        TypeError
            If `libtype` is not a supported library type.
        """

        # a reference to the shared library
        self._lib = None

        # a reference to the .NET Runtime Assembly
        self._assembly = None

        # a reference to the Py4J JavaGateway
        self._gateway = None

        # fixes Issue #8, if `path` is a <class 'pathlib.Path'> object
        path = str(path)

        # create a new reference to `path` just in case the
        # DEFAULT_EXTENSION is appended below so that the
        # ctypes.util.find_library function call will use the
        # unmodified value of `path`
        _path = path

        # assume a default extension if no extension was provided
        ext = os.path.splitext(_path)[1]
        if not ext:
            _path += DEFAULT_EXTENSION

        # the .jar or .class extension uniquely defines a Java library
        if ext in ('.jar', '.class'):
            libtype = 'java'

        self._path = os.path.abspath(_path)
        if not os.path.isfile(self._path):
            # for find_library use the original 'path' value since it may be a library name
            # without any prefix like 'lib', suffix like '.so', '.dylib' or version number
            self._path = ctypes.util.find_library(path)
            if self._path is None:  # then search sys.path and os.environ['PATH']
                success = False
                search_dirs = sys.path + os.environ['PATH'].split(os.pathsep)
                for directory in search_dirs:
                    p = os.path.join(directory, _path)
                    if os.path.isfile(p):
                        self._path = p
                        success = True
                        break
                if not success:
                    raise IOError(
                        'Cannot find the shared library "{}"'.format(path))

        libtype = libtype.lower()
        if libtype == 'cdll':
            self._lib = ctypes.CDLL(self._path)
        elif libtype == 'windll':
            self._lib = ctypes.WinDLL(self._path)
        elif libtype == 'oledll':
            self._lib = ctypes.OleDLL(self._path)
        elif libtype == 'java':
            if not utils.is_py4j_installed():
                raise IOError(
                    'Cannot load a Java file because Py4J is not installed.\n'
                    'To install Py4J run: pip install py4j')

            from py4j.version import __version__
            from py4j.java_gateway import JavaGateway, GatewayParameters

            # the address and port to use to host the py4j.GatewayServer
            address = '127.0.0.1'
            port = utils.get_available_port()

            # find the py4j JAR (needed to import py4j.GatewayServer on the Java side)
            root = os.path.dirname(sys.executable)
            filename = 'py4j' + __version__ + '.jar'
            py4j_jar = os.path.join(root, 'share', 'py4j', filename)
            if not os.path.isfile(py4j_jar):
                root = os.path.dirname(
                    root)  # then check one folder up (for unix or venv)
                py4j_jar = os.path.join(root, 'share', 'py4j', filename)
                if not os.path.isfile(py4j_jar):
                    py4j_jar = os.environ.get(
                        'PY4J_JAR', '')  # then check the environment variable
                    if not os.path.isfile(py4j_jar):
                        raise IOError(
                            'Cannot find {0}\nCreate a PY4J_JAR environment '
                            'variable to be equal to the full path to {0}'.
                            format(filename))

            # build the java command
            wrapper = os.path.join(os.path.dirname(__file__),
                                   'py4j-wrapper.jar')
            cmd = [
                'java', '-cp', py4j_jar + os.pathsep + wrapper, 'Py4JWrapper',
                str(port)
            ]

            # from the URLClassLoader documentation:
            #   Any URL that ends with a '/' is assumed to refer to a directory. Otherwise, the URL
            #   is assumed to refer to a JAR file which will be downloaded and opened as needed.
            if ext == '.jar':
                cmd.append(self._path)
            else:  # it is a .class file
                cmd.append(os.path.dirname(self._path) + '/')

            try:
                # start the py4j.GatewayServer, cannot use subprocess.call() because it blocks
                subprocess.Popen(cmd, stderr=sys.stderr, stdout=sys.stdout)
                err = None
            except IOError:
                err = 'You must have a Java Runtime Environment installed and available on PATH'

            if err:
                raise IOError(err)

            utils.wait_for_server(address, port, 5.0)

            self._gateway = JavaGateway(gateway_parameters=GatewayParameters(
                address=address, port=port))

            self._lib = self._gateway.jvm

        elif libtype == 'net':
            if not utils.is_pythonnet_installed():
                raise IOError(
                    'Cannot load a .NET Assembly because pythonnet is not installed.\n'
                    'To install pythonnet run: pip install pythonnet')

            import clr
            try:
                # By default, pythonnet can only load libraries that are for .NET 4.0+.
                #
                # In order to allow pythonnet to load a library from .NET <4.0 the
                # useLegacyV2RuntimeActivationPolicy property needs to be enabled
                # in a <python-executable>.config file. If the following statement
                # raises a FileLoadException then attempt to create the configuration
                # file that has the property enabled and then notify the user why
                # loading the library failed and ask them to re-run their Python
                # script to load the .NET library.
                self._assembly = clr.System.Reflection.Assembly.LoadFile(
                    self._path)

            except clr.System.IO.FileLoadException as err:
                # Example error message that can occur if the library is for .NET <4.0,
                # and the useLegacyV2RuntimeActivationPolicy is not enabled:
                #
                # " Mixed mode assembly is built against version 'v2.0.50727' of the
                #  runtime and cannot be loaded in the 4.0 runtime without additional
                #  configuration information. "
                if str(err).startswith('Mixed mode assembly'):
                    status, msg = utils.check_dot_net_config(sys.executable)
                    if not status == 0:
                        raise IOError(msg)
                    else:
                        update_msg = 'Checking .NET config returned "{}" '.format(
                            msg)
                        update_msg += 'and still cannot load library.\n'
                        update_msg += str(err)
                        raise IOError(update_msg)
                raise IOError(
                    'The above "System.IO.FileLoadException" is not handled.\n'
                )

            # the shared library must be available in sys.path
            head, tail = os.path.split(self._path)
            sys.path.append(head)

            # don't include the library extension
            clr.AddReference(os.path.splitext(tail)[0])

            # import namespaces, create instances of classes or reference a System.Type[] object
            dotnet = dict()
            for t in self._assembly.GetTypes():
                if t.Namespace is not None:
                    mod = __import__(t.Namespace)
                    if mod.__name__ not in dotnet:
                        dotnet[mod.__name__] = mod
                else:
                    try:
                        dotnet[t.Name] = self._assembly.CreateInstance(
                            t.FullName)
                    except:
                        if t.Name not in dotnet:
                            dotnet[t.Name] = t
            self._lib = DotNet(dotnet, self._path)

        else:
            raise TypeError('Cannot load libtype={}'.format(libtype))
        logger.debug('Loaded ' + self._path)
Beispiel #9
0
def test_omega_ithx_iserver():
    address = '127.0.0.1'
    port = get_available_port()
    term = b'\r'

    t = 20.0
    h = 40.0
    d = 10.0

    records = [
        EquipmentRecord(
            manufacturer='OMEGA',
            model='iTHX-W3',
            connection=ConnectionRecord(
                address='TCP::{}::{}'.format(address, port),
                backend='MSL',
                properties=dict(
                    termination=term,
                    timeout=2
                ),
            )
        ),

        # iTHX-W and iTHX-2 do not support the *SRB and *SRBF commands for
        # fetching both the temperature and humidity with a single write command
        EquipmentRecord(
            manufacturer='OMEGA',
            model='iTHX-2',
            connection=ConnectionRecord(
                address='TCP::{}::{}'.format(address, port),
                backend='MSL',
                properties=dict(
                    termination=term,
                    timeout=2
                ),
            )
        ),
    ]

    for index, record in enumerate(records):
        thread = threading.Thread(target=simulate_omega_iserver, args=(address, port, term))
        thread.daemon = True
        thread.start()

        dev = record.connect(demo=False)
        assert isinstance(dev, iTHX)

        assert dev.temperature() == t
        assert dev.temperature(probe=1) == t
        assert dev.temperature(probe=2) == t
        assert dev.temperature(probe=1, nbytes=6) == t
        assert dev.temperature(probe=2, nbytes=6) == t

        assert dev.humidity() == h
        assert dev.humidity(probe=1) == h
        assert dev.humidity(probe=2) == h
        assert dev.humidity(probe=1, nbytes=6) == h
        assert dev.humidity(probe=2, nbytes=6) == h

        assert dev.dewpoint() == d
        assert dev.dewpoint(probe=1) == d
        assert dev.dewpoint(probe=2) == d
        assert dev.dewpoint(probe=1, nbytes=6) == d
        assert dev.dewpoint(probe=2, nbytes=6) == d

        assert dev.temperature_humidity() == (t, h)
        assert dev.temperature_humidity(probe=1) == (t, h)
        assert dev.temperature_humidity(probe=2) == (t, h)
        assert dev.temperature_humidity(probe=1, nbytes=12) == (t, h)
        assert dev.temperature_humidity(probe=1, nbytes=13) == (t, h)
        assert dev.temperature_humidity(probe=2, nbytes=12) == (t, h)
        assert dev.temperature_humidity(probe=2, nbytes=13) == (t, h)

        assert dev.temperature_humidity_dewpoint() == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=1) == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=2) == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=1, nbytes=18) == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=1, nbytes=19) == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=1, nbytes=20) == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=2, nbytes=18) == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=2, nbytes=19) == (t, h, d)
        assert dev.temperature_humidity_dewpoint(probe=2, nbytes=20) == (t, h, d)

        dev.write('SHUTDOWN')
        dev.disconnect()
        thread.join()