Beispiel #1
0
def test_is_pythonnet_installed():
    # importing pythonnet under these conditions causes a fatal crash
    if IS_MAC and sys.version_info[:2] <= (3, 5):
        assert not utils.is_pythonnet_installed()
    else:
        assert utils.is_pythonnet_installed()
Beispiel #2
0
def test_is_pythonnet_installed():
    assert utils.is_pythonnet_installed()
Beispiel #3
0
def test_pythonnet_py4j_comtypes_installed():
    assert utils.is_pythonnet_installed()
    assert utils.is_py4j_installed()
    if IS_WINDOWS:
        assert utils.is_comtypes_installed()
Beispiel #4
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)