1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115
|
#-----------------------------------------------------------------------------
# Copyright (c) 2005-2023, PyInstaller Development Team.
#
# Distributed under the terms of the GNU General Public License (version 2
# or later) with exception for distributing the bootloader.
#
# The full license is in the file COPYING.txt, distributed with this software.
#
# SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
#-----------------------------------------------------------------------------
import os
import pytest
import textwrap
from PyInstaller.depend import utils
from PyInstaller import compat
CTYPES_CLASSNAMES = (
'CDLL', 'ctypes.CDLL',
'WinDLL', 'ctypes.WinDLL',
'OleDLL', 'ctypes.OleDLL',
'PyDLL', 'ctypes.PyDLL',
) # yapf: disable
def __scan_code_for_ctypes(code, monkeypatch, extended_args):
# _resolveCtypesImports would filter our some of our names
monkeypatch.setattr(utils, '_resolveCtypesImports', lambda cbinaries: cbinaries)
code = textwrap.dedent(code)
if extended_args:
# Chuck in a load of preceding rubbish to test if the bytecode scanner can correctly
# handle the EXTENDED_ARGS opcode.
from test_bytecode import many_str_constants, many_globals
code = many_str_constants() + many_globals() + code
co = compile(code, 'dummy', 'exec')
#import pdb ; pdb.set_trace()
return utils.scan_code_for_ctypes(co)
@pytest.mark.parametrize('classname', CTYPES_CLASSNAMES)
@pytest.mark.parametrize('extended_args', [False, True])
def test_ctypes_CDLL_call(monkeypatch, classname, extended_args):
code = f"{classname}('somelib.xxx')"
res = __scan_code_for_ctypes(code, monkeypatch, extended_args)
assert res == set(['somelib.xxx'])
@pytest.mark.parametrize('classname', CTYPES_CLASSNAMES)
@pytest.mark.parametrize('extended_args', [False, True])
def test_ctypes_LibraryLoader(monkeypatch, classname, extended_args):
# This type of usage is only valid on Windows and the lib-name will always get `.dll` appended.
code = f"{classname.lower()}.somelib"
res = __scan_code_for_ctypes(code, monkeypatch, extended_args)
assert res == set(['somelib.dll'])
@pytest.mark.parametrize('classname', CTYPES_CLASSNAMES)
@pytest.mark.parametrize('extended_args', [False, True])
def test_ctypes_LibraryLoader_LoadLibrary(monkeypatch, classname, extended_args):
code = f"{classname.lower()}.LoadLibrary('somelib.xxx')"
res = __scan_code_for_ctypes(code, monkeypatch, extended_args)
assert res == set(['somelib.xxx'])
@pytest.mark.parametrize('extended_args', [False, True])
@pytest.mark.skipif(compat.is_musl, reason="find_library() doesn't work on musl")
@pytest.mark.skipif(
compat.is_macos_11 and not (compat.is_macos_11_native and compat.is_py39),
reason="find_library() requires python >= 3.9 built with Big Sur support.",
)
def test_ctypes_util_find_library(monkeypatch, extended_args):
# for lind_library() we need a lib actually existing on the system
if compat.is_win:
libname = "KERNEL32"
else:
libname = "c"
code = f"ctypes.util.find_library('{libname}')"
res = __scan_code_for_ctypes(code, monkeypatch, extended_args)
assert res
def test_ctypes_util_find_library_as_default_argument():
# Test-case for fix:
# commit 55b542f135340c612a861cfcce0f86c4e5a968df
# Author: Hartmut Goebel <h.goebel@crazy-compilers.com>
# Date: Thu Nov 19 14:45:30 2015 +0100
code = """
def locate_library(loader=ctypes.util.find_library):
pass
"""
code = textwrap.dedent(code)
co = compile(code, '<ctypes_util_find_library_as_default_argument>', 'exec')
utils.scan_code_for_ctypes(co)
@pytest.mark.linux
@pytest.mark.skipif(compat.is_termux, reason="Termux does not have libc.so")
def test_ldconfig_cache():
utils.load_ldconfig_cache()
if compat.is_musl:
# load_ldconfig_cache() should be a no-op on musl because musl does not use ldconfig.
assert not utils.LDCONFIG_CACHE
return
libpath = None
for soname in utils.LDCONFIG_CACHE:
if soname.startswith('libc.so.'):
libpath = utils.LDCONFIG_CACHE[soname]
break
assert libpath, 'libc.so not found'
assert os.path.isfile(libpath)
|