SpaceLife-Updater/venv/lib64/python3.12/site-packages/_pyinstaller_hooks_contrib/tests/test_libraries.py

1898 lines
48 KiB
Python

# ------------------------------------------------------------------
# Copyright (c) 2020 PyInstaller Development Team.
#
# This file is distributed under the terms of the GNU General Public
# License (version 2.0 or later).
#
# The full license is available in LICENSE.GPL.txt, distributed with
# this software.
#
# SPDX-License-Identifier: GPL-2.0-or-later
# ------------------------------------------------------------------
import os
from pathlib import Path
import pytest
from PyInstaller.compat import is_darwin, is_linux, is_py39, is_win
from PyInstaller.utils.hooks import is_module_satisfies, can_import_module, get_module_attribute
from PyInstaller.utils.tests import importorskip, requires, xfail
@importorskip('fiona')
def test_fiona(pyi_builder):
pyi_builder.test_source(
'''
import fiona
'''
)
@importorskip('fiona')
def test_fiona_transform(pyi_builder):
# Test that fiona in frozen application has access to its projections database. If projection data is unavailable,
# the transform becomes an identity transform.
pyi_builder.test_source(
"""
from fiona.transform import transform_geom
from fiona.crs import from_epsg
eiffel_tower = {
'type': 'Point',
'coordinates': (2.294694, 48.858093),
}
crs_source = from_epsg(4326) # WGS84
crs_target = from_epsg(25831) # ETRS89 / UTM zone 31N
transformed = transform_geom(crs_source, crs_target, eiffel_tower)
print(f"Transformed point: {transformed}")
# Expected coordinates: obtained by manually running this program unfrozen
EXPECTED_COORDINATES = (448265.9146792292, 5411920.651338793)
EPS = 1e-6
delta = [abs(value - expected) for value, expected in zip(transformed["coordinates"], EXPECTED_COORDINATES)]
print(f"Delta: {delta}")
assert all([value < EPS for value in delta]), f"Delta {delta} exceeds threshold!"
"""
)
@importorskip('jinxed')
def test_jinxed(pyi_builder):
pyi_builder.test_source(
'''
import jinxed
jinxed.setupterm('xterm')
assert jinxed._terminal.TERM.terminfo is jinxed.terminfo.xterm
'''
)
@importorskip("geopandas")
def test_geopandas(pyi_builder):
pyi_builder.test_source(
'''
import geopandas
'''
)
@importorskip('trimesh')
def test_trimesh(pyi_builder):
pyi_builder.test_source(
"""
import trimesh
"""
)
@importorskip('apscheduler')
def test_apscheduler(pyi_builder):
pyi_builder.test_source(
"""
import apscheduler
import pytz
import asyncio
import random
import datetime as dt
from apscheduler.schedulers.asyncio import AsyncIOScheduler
from apscheduler.triggers.interval import IntervalTrigger
loop = asyncio.get_event_loop()
async def test_function(data=0):
print(dt.datetime.now(), random.randint(0, 100))
test_scheduler = AsyncIOScheduler()
test_scheduler.add_job(
test_function,
id="TestJob",
trigger=IntervalTrigger(
seconds=1,
start_date=dt.datetime.now(tz=pytz.UTC)
)
)
test_scheduler.start()
loop.run_until_complete(asyncio.sleep(5))
"""
)
@importorskip('boto')
@xfail(reason='boto does not fully support Python 3')
def test_boto(pyi_builder):
pyi_builder.test_script('pyi_lib_boto.py')
@xfail(reason='Issue #1844.')
@importorskip('boto3')
def test_boto3(pyi_builder):
pyi_builder.test_source(
"""
import boto3
session = boto3.Session(region_name='us-west-2')
# verify all clients
for service in session.get_available_services():
session.client(service)
# verify all resources
for resource in session.get_available_resources():
session.resource(resource)
""")
@xfail(reason='Issue #1844.')
@importorskip('botocore')
def test_botocore(pyi_builder):
pyi_builder.test_source(
"""
import botocore
from botocore.session import Session
session = Session()
# verify all services
for service in session.get_available_services():
session.create_client(service, region_name='us-west-2')
""")
@xfail(is_darwin, reason='Issue #1895.')
@importorskip('enchant')
def test_enchant(pyi_builder):
pyi_builder.test_script('pyi_lib_enchant.py')
@importorskip('zmq')
def test_zmq(pyi_builder):
pyi_builder.test_source(
"""
import zmq
print(zmq.__version__)
print(zmq.zmq_version())
# This is a problematic module and might cause some issues.
import zmq.utils.strtypes
""")
@importorskip('pylint')
def test_pylint(pyi_builder):
pyi_builder.test_source(
"""
# The following more obvious test doesn't work::
#
# import pylint
# pylint.run_pylint()
#
# because pylint will exit with 32, since a valid command
# line wasn't given. Instead, provide a valid command line below.
from pylint.lint import Run
Run(['-h'])
""")
@importorskip('markdown')
def test_markdown(pyi_builder):
# Markdown uses __import__ed extensions. Make sure these work by
# trying to use the 'toc' extension, using both short and long format.
pyi_builder.test_source(
"""
import markdown
print(markdown.markdown('testing',
extensions=['toc']))
print(markdown.markdown('testing',
extensions=['markdown.extensions.toc']))
""")
@importorskip('pylsl')
def test_pylsl(pyi_builder):
pyi_builder.test_source(
"""
import pylsl
print(pylsl.version.__version__)
""")
@importorskip('lxml')
def test_lxml_isoschematron(pyi_builder):
pyi_builder.test_source(
"""
# The import of this module triggers the loading of some
# required XML files.
from lxml import isoschematron
""")
@importorskip('openpyxl')
def test_openpyxl(pyi_builder):
pyi_builder.test_source(
"""
# Test the hook to openpyxl
from openpyxl import __version__
""")
@importorskip('pyodbc')
def test_pyodbc(pyi_builder):
pyi_builder.test_source(
"""
# pyodbc is a binary Python module. On Windows when installed with easy_install
# it is installed as zipped Python egg. This binary module is extracted
# to PYTHON_EGG_CACHE directory. PyInstaller should find the binary there and
# include it with frozen executable.
import pyodbc
""")
@importorskip('pyttsx')
def test_pyttsx(pyi_builder):
pyi_builder.test_source(
"""
# Basic code example from pyttsx tutorial.
# http://packages.python.org/pyttsx/engine.html#examples
import pyttsx
engine = pyttsx.init()
engine.say('Sally sells seashells by the seashore.')
engine.say('The quick brown fox jumped over the lazy dog.')
engine.runAndWait()
""")
@importorskip('pyttsx3')
def test_pyttsx3(pyi_builder):
pyi_builder.test_source("""
import pyttsx3
engine = pyttsx3.init()
""")
@importorskip('pycparser')
def test_pycparser(pyi_builder):
pyi_builder.test_script('pyi_lib_pycparser.py')
@importorskip('Crypto')
def test_pycrypto(pyi_builder):
pyi_builder.test_source(
"""
import binascii
from Crypto.Cipher import AES
BLOCK_SIZE = 16
print('AES null encryption, block size', BLOCK_SIZE)
# Just for testing functionality after all
print('HEX', binascii.hexlify(
AES.new(b"\\0" * BLOCK_SIZE, AES.MODE_ECB).encrypt(b"\\0" * BLOCK_SIZE)))
from Crypto.PublicKey import ECC
""")
@importorskip('Cryptodome')
def test_cryptodome(pyi_builder):
pyi_builder.test_source(
"""
from Cryptodome import Cipher
from Cryptodome.PublicKey import ECC
print('Cryptodome Cipher Module:', Cipher)
""")
@importorskip('h5py')
def test_h5py(pyi_builder):
pyi_builder.test_source("""
import h5py
""")
@importorskip('unidecode')
def test_unidecode(pyi_builder):
pyi_builder.test_source("""
from unidecode import unidecode
# Unidecode should not skip non-ASCII chars if mappings for them exist.
assert unidecode(u"kožušček") == "kozuscek"
""")
@importorskip('pinyin')
def test_pinyin(pyi_builder):
pyi_builder.test_source("""
import pinyin
""")
@importorskip('uvloop')
@pytest.mark.darwin
@pytest.mark.linux
def test_uvloop(pyi_builder):
pyi_builder.test_source("import uvloop")
@importorskip('web3')
def test_web3(pyi_builder):
pyi_builder.test_source("import web3")
@importorskip('phonenumbers')
def test_phonenumbers(pyi_builder):
pyi_builder.test_source("""
import phonenumbers
number = '+17034820623'
parsed_number = phonenumbers.parse(number)
assert(parsed_number.country_code == 1)
assert(parsed_number.national_number == 7034820623)
""")
@importorskip('pendulum')
def test_pendulum(pyi_builder):
pyi_builder.test_source("""
import pendulum
print(pendulum.now().isoformat())
""")
@importorskip('humanize')
def test_humanize(pyi_builder):
pyi_builder.test_source("""
import humanize
from datetime import timedelta
print(humanize.naturaldelta(timedelta(seconds=125)))
""")
@importorskip('argon2')
def test_argon2(pyi_builder):
pyi_builder.test_source("""
from argon2 import PasswordHasher
ph = PasswordHasher()
hash = ph.hash("s3kr3tp4ssw0rd")
ph.verify(hash, "s3kr3tp4ssw0rd")
""")
@importorskip('pytest')
def test_pytest_runner(pyi_builder):
"""
Check if pytest runner builds correctly.
"""
pyi_builder.test_source(
"""
import pytest
import sys
sys.exit(pytest.main(['--help']))
""")
@importorskip('eel')
def test_eel(pyi_builder):
pyi_builder.test_source("import eel")
@importorskip('sentry_sdk')
def test_sentry(pyi_builder):
pyi_builder.test_source(
"""
import sentry_sdk
sentry_sdk.init()
""")
@importorskip('iminuit')
def test_iminuit(pyi_builder):
pyi_builder.test_source("""
from iminuit import Minuit
""")
@importorskip('av')
def test_av(pyi_builder):
pyi_builder.test_source("""
import av
""")
@importorskip('passlib')
@xfail(is_linux and is_py39 and not is_module_satisfies('passlib > 1.7.4'),
reason='Passlib does not account for crypt() behavior change that '
'was introduced in 3.9.x (python #39289).')
def test_passlib(pyi_builder):
pyi_builder.test_source("""
import passlib.apache
""")
@importorskip('publicsuffix2')
def test_publicsuffix2(pyi_builder):
pyi_builder.test_source("""
import publicsuffix2
publicsuffix2.PublicSuffixList()
""")
@importorskip('pydivert')
def test_pydivert(pyi_builder):
pyi_builder.test_source("""
import pydivert
pydivert.WinDivert.check_filter("inbound")
""")
@importorskip('statsmodels')
@pytest.mark.skipif(not is_module_satisfies('statsmodels >= 0.12'),
reason='This has only been tested with statsmodels >= 0.12.')
def test_statsmodels(pyi_builder):
pyi_builder.test_source("""
import statsmodels.api as sm
""")
@importorskip('win32ctypes')
@pytest.mark.skipif(not is_win, reason='pywin32-ctypes is supported only on Windows')
@pytest.mark.parametrize('submodule', ['win32api', 'win32cred', 'pywintypes'])
def test_pywin32ctypes(pyi_builder, submodule):
pyi_builder.test_source("""
from win32ctypes.pywin32 import {0}
""".format(submodule))
@importorskip('pyproj')
@pytest.mark.skipif(not is_module_satisfies('pyproj >= 2.1.3'),
reason='The test supports only pyproj >= 2.1.3.')
def test_pyproj(pyi_builder):
pyi_builder.test_source("""
import pyproj
tf = pyproj.Transformer.from_crs(
7789,
8401
)
result = tf.transform(
xx=3496737.2679,
yy=743254.4507,
zz=5264462.9620,
tt=2019.0
)
print(result)
""")
@importorskip('pydantic')
def test_pydantic(pyi_builder):
pyi_builder.test_source("""
import datetime
import pprint
import pydantic
class User(pydantic.BaseModel):
id: int
name: str = 'John Doe'
signup_ts: datetime.datetime
external_data = {'id': 'not an int', }
try:
User(**external_data)
except pydantic.ValidationError as e:
pprint.pprint(e.errors())
""")
@requires('google-api-python-client >= 2.0.0')
def test_googleapiclient(pyi_builder):
pyi_builder.test_source("""
from googleapiclient import discovery, discovery_cache
API_NAME = "youtube"
API_VERSION = "v3"
for file in os.listdir(discovery_cache.DISCOVERY_DOC_DIR): # Always up to date
if file.startswith("youtube.v") and file.endswith(".json"):
API_NAME, API_VERSION = file.split(".")[:2]
break
# developerKey can be any non-empty string
yt = discovery.build(API_NAME, API_VERSION, developerKey=":)", static_discovery=True)
""")
@importorskip('eth_typing')
def test_eth_typing(pyi_builder):
pyi_builder.test_source("""
import eth_typing
""")
@importorskip("eth_utils")
def test_eth_utils_network(pyi_builder):
pyi_builder.test_source("""
import eth_utils.network
eth_utils.network.name_from_chain_id(1)
""")
@importorskip('plotly')
@importorskip('pandas')
def test_plotly(pyi_builder):
pyi_builder.test_source("""
import pandas as pd
import plotly.express as px
data = [(1, 1), (2, 1), (3, 5), (4, -3)]
df = pd.DataFrame.from_records(data, columns=['col_1', 'col_2'])
fig = px.scatter(df, x='col_1', y='col_2')
""")
@pytest.mark.timeout(600)
@importorskip('dash')
def test_dash(pyi_builder):
pyi_builder.test_source("""
import dash
import dash_core_components as dcc
import dash_html_components as html
from dash.dependencies import Input, Output
app = dash.Dash(__name__)
app.layout = html.Div(
[
dcc.Input(id='input_text', type='text', placeholder='input type text'),
html.Div(id='out-all-types'),
]
)
@app.callback(
Output('out-all-types', 'children'),
[Input('input_text', 'value')],
)
def cb_render(val):
return val
""")
@importorskip('dash_table')
def test_dash_table(pyi_builder):
pyi_builder.test_source("""
import dash
import dash_table
app = dash.Dash(__name__)
app.layout = dash_table.DataTable(
id='table',
columns=[{'name': 'a', 'id': 'a'}, {'name': 'b', 'id': 'b'}],
data=[{'a': 1, 'b': 2}, {'a': 3, 'b': 4}],
)
""")
@importorskip('dash_bootstrap_components')
def test_dash_bootstrap_components(pyi_builder):
pyi_builder.test_source("""
import dash
import dash_bootstrap_components as dbc
import dash_html_components as html
app = dash.Dash(external_stylesheets=[dbc.themes.BOOTSTRAP])
alert = dbc.Alert([html.H4('Well done!', className='alert-heading')])
""")
@importorskip('blspy')
def test_blspy(pyi_builder):
pyi_builder.test_source("""
import blspy
""")
@importorskip('flirpy')
def test_flirpy(pyi_builder):
pyi_builder.test_source("""
from flirpy.camera.lepton import Lepton
print(Lepton.find_video_device())
""")
@importorskip('office365')
def test_office365(pyi_builder):
pyi_builder.test_source("""
from office365.runtime.auth.providers.saml_token_provider import SamlTokenProvider
SamlTokenProvider._prepare_request_from_template('FederatedSAML.xml', {})
SamlTokenProvider._prepare_request_from_template('RST2.xml', {})
SamlTokenProvider._prepare_request_from_template('SAML.xml', {})
""")
@importorskip('thinc')
def test_thinc(pyi_builder):
pyi_builder.test_source("""
from thinc.backends import numpy_ops
""")
@importorskip('srsly')
def test_srsly(pyi_builder):
pyi_builder.test_source("""
import srsly
""")
@importorskip('spacy')
def test_spacy(pyi_builder):
pyi_builder.test_source("""
import spacy
""")
@importorskip('shotgun_api3')
def test_shotgun_api3(pyi_builder):
pyi_builder.test_source("""
import shotgun_api3
""")
@importorskip('msoffcrypto')
def test_msoffcrypto(pyi_builder):
pyi_builder.test_source("""
import msoffcrypto
""")
@importorskip('mariadb')
def test_mariadb(pyi_builder):
pyi_builder.test_source("""
import mariadb
""")
@importorskip('dash_uploader')
def test_dash_uploader(pyi_builder):
pyi_builder.test_source("""
import dash_uploader
""")
@importorskip('cloudscraper')
def test_cloudscraper(pyi_builder):
pyi_builder.test_source("""
import cloudscraper
scraper = cloudscraper.create_scraper()
""")
@importorskip('mnemonic')
def test_mnemonic(pyi_builder):
pyi_builder.test_source("""
import mnemonic
mnemonic.Mnemonic("english")
""")
@importorskip('pynput')
def test_pynput(pyi_builder):
pyi_builder.test_source("""
import pynput
""")
@importorskip('pystray')
def test_pystray(pyi_builder):
pyi_builder.test_source("""
import pystray
""")
@importorskip('rtree')
def test_rtree(pyi_builder):
pyi_builder.test_source("""
import rtree
""")
@importorskip('pingouin')
def test_pingouin(pyi_builder):
pyi_builder.test_source("""
import pingouin
""")
@importorskip('timezonefinder')
def test_timezonefinder(pyi_builder):
pyi_builder.test_source("""
from timezonefinder import TimezoneFinder
TimezoneFinder()
""")
@importorskip('uvicorn')
def test_uvicorn(pyi_builder):
pyi_builder.test_source("""
from uvicorn import lifespan, loops
""")
@importorskip("langdetect")
def test_langdetect(pyi_builder):
pyi_builder.test_source("""
import langdetect
print(langdetect.detect("this is a test"))
""")
@importorskip("swagger_spec_validator")
def test_swagger_spec_validator(pyi_builder):
pyi_builder.test_source("""
from swagger_spec_validator.common import read_resource_file
read_resource_file("schemas/v1.2/resourceListing.json")
read_resource_file("schemas/v2.0/schema.json")
""")
@requires('pythonnet < 3.dev')
@pytest.mark.skipif(not is_win, reason='pythonnet 2 does not support .Net Core, so its only supported by Windows')
def test_pythonnet2(pyi_builder):
pyi_builder.test_source("""
import clr
""")
@requires('pythonnet >= 3.dev')
def test_pythonnet3(pyi_builder):
pyi_builder.test_source("""
from clr_loader import get_coreclr
from pythonnet import set_runtime
set_runtime(get_coreclr()) # Pick up and use any installed .NET runtime.
import clr
""")
if is_win:
# This is a hack to prevent monkeypatch from interfering with PyQt5's additional PATH entries. See:
# https://github.com/pyinstaller/pyinstaller/commit/b66c9021129e9e875ddd138a298ce542483dd6c9
try:
import PyQt5 # noqa: F401
except ImportError:
pass
@importorskip("qtmodern")
@importorskip("PyQt5")
def test_qtmodern(pyi_builder):
pyi_builder.test_source("""
import sys
from PyQt5 import QtWidgets
import qtmodern.styles
import qtmodern.windows
app = QtWidgets.QApplication(sys.argv)
window = QtWidgets.QWidget()
qtmodern.styles.dark(app)
modern_window = qtmodern.windows.ModernWindow(window)
modern_window.show()
""")
@importorskip("platformdirs")
def test_platformdirs(pyi_builder):
pyi_builder.test_source("""
import platformdirs
platformdirs.user_data_dir("FooApp", "Mr Foo")
""")
@importorskip("websockets")
def test_websockets(pyi_builder):
pyi_builder.test_source("import websockets")
@importorskip("tableauhyperapi")
def test_tableauhyperapi(pyi_builder):
pyi_builder.test_source("""
import tableauhyperapi
""")
@importorskip("pymssql")
def test_pymssql(pyi_builder):
pyi_builder.test_source("""
import pymssql
""")
@importorskip("branca")
def test_branca(pyi_builder):
pyi_builder.test_source("""
import branca
""")
@importorskip("folium")
def test_folium(pyi_builder):
pyi_builder.test_source("""
import folium
m = folium.Map(location=[0, 0], zoom_start=5)
""")
@importorskip("metpy")
def test_metpy(pyi_builder):
# Import metpy.plots, which triggers search for colortables data.
pyi_builder.test_source("""
import metpy.plots
""")
@importorskip("pyvjoy")
def test_pyvjoy(pyi_builder):
pyi_builder.test_source("""
import pyvjoy
""")
@importorskip("adbutils")
def test_adbutils(pyi_builder):
# adbutils 0.15.0 renamed adbutils._utils.get_adb_exe() to adb_path()
if is_module_satisfies("adbutils >= 0.15.0"):
pyi_builder.test_source("""
from adbutils._utils import adb_path; adb_path()
""")
else:
pyi_builder.test_source("""
from adbutils._utils import get_adb_exe; get_adb_exe()
""")
@importorskip("pymediainfo")
def test_pymediainfo(pyi_builder):
pyi_builder.test_source("""
from pymediainfo import MediaInfo
MediaInfo._get_library() # Trigger search for shared library.
""")
@importorskip("sacremoses")
def test_sacremoses(pyi_builder):
pyi_builder.test_source("""
import sacremoses
""")
@importorskip("pypeteer")
def test_pypeteer(pyi_builder):
pyi_builder.test_source("""
import pypeteer
print(pypeteer.version)
""")
@importorskip("tzdata")
@pytest.mark.skipif(not is_py39 and not can_import_module('importlib_resources'),
reason='importlib_resources is required on python < 3.9.')
def test_tzdata(pyi_builder):
pyi_builder.test_source("""
import tzdata.zoneinfo # hiddenimport
try:
import importlib.resources as importlib_resources
except ImportError:
import importlib_resources
# This emulates time-zone data retrieval from tzdata, as peformed by
# zoneinfo / backports.zoneinfo
zone_name = "Europe/Ljubljana"
components = zone_name.split("/")
package_name = ".".join(["tzdata.zoneinfo"] + components[:-1])
resource_name = components[-1]
with importlib_resources.open_binary(package_name, resource_name) as fp:
data = fp.read()
print(data)
""")
@importorskip("backports.zoneinfo")
@pytest.mark.skipif(is_win and not can_import_module('tzdata'),
reason='On Windows, backports.zoneinfo requires tzdata.')
def test_backports_zoneinfo(pyi_builder):
pyi_builder.test_source("""
from backports import zoneinfo
tz = zoneinfo.ZoneInfo("Europe/Ljubljana")
print(tz)
""")
@importorskip("zoneinfo")
@pytest.mark.skipif(is_win and not can_import_module('tzdata'),
reason='On Windows, zoneinfo requires tzdata.')
def test_zoneinfo(pyi_builder):
pyi_builder.test_source("""
import zoneinfo
tz = zoneinfo.ZoneInfo("Europe/Ljubljana")
print(tz)
""")
@importorskip("panel")
def test_panel(pyi_builder):
pyi_builder.test_source("""
import panel
# Load the Ace extension to trigger lazy-loading of model
panel.extension("ace")
""")
@importorskip("pyviz_comms")
def test_pyviz_comms(pyi_builder):
pyi_builder.test_source("""
import pyviz_comms
""")
@importorskip("pyphen")
def test_pyphen(pyi_builder):
pyi_builder.test_source("""
import pyphen
""")
@importorskip("pandas")
@importorskip("plotly")
@importorskip("kaleido")
def test_kaleido(pyi_builder):
pyi_builder.test_source("""
import plotly.express as px
fig = px.scatter(px.data.iris(), x="sepal_length", y="sepal_width", color="species")
fig.write_image("figure.png", engine="kaleido")
""")
@pytest.mark.skipif(is_win,
reason='On Windows, Cairo dependencies cannot be installed using Chocolatey.')
@importorskip("cairocffi")
def test_cairocffi(pyi_builder):
pyi_builder.test_source("""
import cairocffi
""")
@pytest.mark.skipif(is_win,
reason='On Windows, Cairo dependencies cannot be installed using Chocolatey.')
@importorskip("cairosvg")
def test_cairosvg(pyi_builder):
pyi_builder.test_source("""
import cairosvg
""")
@importorskip("ffpyplayer")
def test_ffpyplayer(pyi_builder):
pyi_builder.test_source("""
import ffpyplayer.player
""")
@importorskip("cv2")
def test_cv2(pyi_builder):
pyi_builder.test_source("""
import cv2
""")
# Requires OpenCV with enabled HighGUI
@importorskip("cv2")
def test_cv2_highgui(pyi_builder):
from PyInstaller import isolated
@isolated.decorate
def _get_cv2_highgui_backend():
import re
import cv2
# Find `GUI: <type>` line in OpenCV build information dump. This is available only in recent OpenCV versions;
# in earlier versions, we would need to parse all subsequent backend entries, which is out of our scope here.
pattern = re.compile(r'$\s*GUI\s*:\s*(?P<gui>\S+)\s*^', re.MULTILINE)
info = cv2.getBuildInformation()
m = pattern.search(info)
if not m:
return None
return m.group('gui')
has_gui = True
backend = _get_cv2_highgui_backend()
if backend is None:
# We could not determine the backend from OpenCV information; fall back to the dist name
if is_module_satisfies('opencv-python-headless'):
has_gui = False
elif backend == "NONE":
has_gui = False
if not has_gui:
pytest.skip("OpenCV has no GUI support.")
pyi_builder.test_source("""
import cv2
import numpy as np
img = np.zeros((64, 64), dtype='uint8')
cv2.imshow("Test", img)
cv2.waitKey(1000) # Wait a second
""")
@importorskip("twisted")
def test_twisted_default_reactor(pyi_builder):
pyi_builder.test_source("""
from twisted.internet import reactor
assert callable(reactor.listenTCP)
""")
@importorskip("twisted")
def test_twisted_custom_reactor(pyi_builder):
pyi_builder.test_source("""
import sys
if sys.platform.startswith("win") and sys.version_info >= (3,7):
import asyncio
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
from twisted.internet import asyncioreactor
asyncioreactor.install()
from twisted.internet import reactor
assert callable(reactor.listenTCP)
""")
@importorskip("pygraphviz")
def test_pygraphviz_bundled_programs(pyi_builder):
# Test that the frozen application is using collected graphviz executables instead of system-installed ones.
pyi_builder.test_source("""
import sys
import os
import pygraphviz
bundle_dir = os.path.normpath(sys._MEIPASS)
dot_path = os.path.normpath(pygraphviz.AGraph()._get_prog('dot'))
assert os.path.commonprefix([dot_path, bundle_dir]) == bundle_dir, \
f"Invalid program path: {dot_path}!"
""")
@importorskip("pypsexec")
def test_pypsexec(pyi_builder):
pyi_builder.test_source("""
from pypsexec.paexec import paexec_out_stream
next(paexec_out_stream())
""")
@importorskip("mimesis")
def test_mimesis(pyi_builder):
pyi_builder.test_source("""
from mimesis import Address
Address().address()
""")
@importorskip('orjson')
def test_orjson(pyi_builder):
pyi_builder.test_source("""
import orjson
""")
@importorskip('altair')
def test_altair(pyi_builder):
pyi_builder.test_source("""
import altair
""")
@importorskip('fabric')
def test_fabric(pyi_builder):
pyi_builder.test_source("""
import fabric
""")
@importorskip('cassandra')
def test_cassandra(pyi_builder):
pyi_builder.test_source("""
import cassandra
""")
@importorskip('gitlab')
def test_gitlab(pyi_builder):
pyi_builder.test_source("""
import gitlab
""")
@importorskip('graphql_query')
def test_graphql_query(pyi_builder):
pyi_builder.test_source("""
from graphql_query import Operation, Query
hero = Query(name="hero", fields=["name"])
operation = Operation(type="query", queries=[hero])
print(operation.render())
""")
@importorskip('shapely')
def test_shapely(pyi_builder):
pyi_builder.test_source("""
from shapely.geometry import Point
patch = Point(0.0, 0.0).buffer(10.0)
print(patch.area)
""")
@importorskip('lark')
def test_lark(pyi_builder):
pyi_builder.test_source("""
import lark
parser = lark.Lark('''
value: "true"
%import common.SIGNED_NUMBER''',
start='value')
""")
@importorskip('stdnum')
def test_stdnum_iban(pyi_builder):
pyi_builder.test_source("""
import stdnum.iban
""")
@importorskip('numcodecs')
def test_numcodecs(pyi_builder):
pyi_builder.test_source("""
# numcodecs uses multiprocessing
import multiprocessing
multiprocessing.freeze_support()
from numcodecs import Blosc
""")
@importorskip('pypemicro')
def test_pypemicro(pyi_builder):
pyi_builder.test_source("""
from pypemicro import PyPemicro
assert PyPemicro.get_pemicro_lib()
""")
@importorskip('sounddevice')
def test_sounddevice(pyi_builder):
pyi_builder.test_source("""
import sounddevice
""")
@importorskip('soundfile')
def test_soundfile(pyi_builder):
pyi_builder.test_source("""
import soundfile
""")
@importorskip('limits')
def test_limits(pyi_builder):
pyi_builder.test_source("""
import limits
""")
@pytest.mark.skipif(is_win,
reason='On Windows, Weasyprint dependencies cannot be installed using Chocolatey.')
@importorskip("weasyprint")
def test_weasyprint(pyi_builder):
pyi_builder.test_source("""
import weasyprint
""")
@importorskip("great_expectations")
def test_great_expectations(pyi_builder):
# Reproduce the error from pyinstaller/pyinstaller-hooks-contrib#445
pyi_builder.test_source("""
from great_expectations.render.view import view
v = view.DefaultJinjaView()
""")
@importorskip('pyshark')
def test_pyshark(pyi_builder):
pyi_builder.test_source(
"""
import pyshark
#capture = pyshark.FileCapture('/tmp/networkpackages.cap')
#data = [print x for x in capture]
#print(data)
"""
)
@importorskip('pyqtgraph')
@importorskip('PyQt5')
def test_pyqtgraph(pyi_builder):
pyi_builder.test_source(
"""
import pyqtgraph.graphicsItems.PlotItem
import pyqtgraph.graphicsItems.ViewBox.ViewBoxMenu
import pyqtgraph.imageview.ImageView
""",
pyi_args=['--exclude', 'PySide2', '--exclude', 'PySide6', '--exclude', 'PyQt6']
)
@importorskip('pyqtgraph')
def test_pyqtgraph_colormap(pyi_builder):
pyi_builder.test_source(
"""
import pyqtgraph.colormap
assert pyqtgraph.colormap.listMaps()
"""
)
@importorskip('pyqtgraph')
@importorskip('PyQt5')
def test_pyqtgraph_remote_graphics_view(pyi_builder):
pyi_builder.test_source(
"""
import sys
import os
import signal
from PyQt5 import QtCore, QtWidgets
import pyqtgraph
# Multiprocessing is used internally by pyqtgraph.multiprocess
import multiprocessing
multiprocessing.freeze_support()
# pyqtgraph.multiprocess also uses a subprocess.Popen() to spawn its
# sub-process, so we need to restore _MEIPASS2 to prevent the executable
# to unpacking itself again in the subprocess.
os.environ['_MEIPASS2'] = sys._MEIPASS
# Create a window with remote graphics view
app = QtWidgets.QApplication(sys.argv)
signal.signal(signal.SIGINT, signal.SIG_DFL)
window = QtWidgets.QWidget()
layout = QtWidgets.QVBoxLayout(window)
remote_view = pyqtgraph.widgets.RemoteGraphicsView.RemoteGraphicsView()
layout.addWidget(remote_view)
window.show()
# Quit after a second
QtCore.QTimer.singleShot(1000, app.exit)
sys.exit(app.exec_())
""",
pyi_args=['--exclude', 'PySide2', '--exclude', 'PySide6', '--exclude', 'PyQt6']
)
# Remove xfail once facebookresearch/hydra#2531 is merged.
@importorskip('hydra')
@xfail(
is_module_satisfies('PyInstaller >= 5.8'),
reason="uses deprecated PEP-302 functionality that was removed from PyInstaller's FrozenImporter.")
def test_hydra(pyi_builder, tmpdir):
config_file = str((Path(__file__) / '../data/test_hydra/config.yaml').resolve(strict=True).as_posix())
pyi_builder.test_source(
"""
import os
import hydra
from omegaconf import DictConfig, OmegaConf
config_path = os.path.join(os.path.dirname(__file__), 'conf')
@hydra.main(config_path=config_path, config_name="config")
def my_app(cfg):
assert cfg.test_group.secret_string == 'secret'
assert cfg.test_group.secret_number == 123
if __name__ == "__main__":
my_app()
""",
pyi_args=['--add-data', os.pathsep.join((config_file, 'conf'))]
)
@importorskip('pywintypes')
def test_pywintypes(pyi_builder):
pyi_builder.test_source("""
import pywintypes
""")
@importorskip('pythoncom')
def test_pythoncom(pyi_builder):
pyi_builder.test_source("""
import pythoncom
""")
@importorskip('spiceypy')
def test_spiceypy(pyi_builder):
pyi_builder.test_source("""
import spiceypy
""")
@importorskip('discid')
def test_discid(pyi_builder):
pyi_builder.test_source(
"""
# Basic import check
import discid
# Check that shared library is in fact collected into application bundle.
# We expect the hook to collect it to top-level directory (sys._MEIPASS).
import discid.libdiscid
lib_name = discid.libdiscid._LIB_NAME
lib_file = os.path.join(sys._MEIPASS, lib_name)
assert os.path.isfile(lib_file), f"Shared library {lib_name} not collected to _MEIPASS!"
"""
)
@importorskip('exchangelib')
def test_exchangelib(pyi_builder):
pyi_builder.test_source("""
import exchangelib
""")
@importorskip('cftime')
def test_cftime(pyi_builder):
pyi_builder.test_source("""
import cftime
""")
@importorskip('netCDF4')
def test_netcdf4(pyi_builder):
pyi_builder.test_source("""
import netCDF4
""")
@importorskip('charset_normalizer')
def test_charset_normalizer(pyi_builder):
pyi_builder.test_source("""
import base64
import charset_normalizer
message = base64.b64decode(b"yUCEmYWBlIWEQIFAhJmFgZRAloZAgUCUlpmFQKKFlaKJgpOFQJeBg5KBh4U=")
print(charset_normalizer.from_bytes(message).best())
""")
@importorskip('cf_units')
def test_cf_units(pyi_builder):
pyi_builder.test_source("""
import cf_units
""")
@importorskip('compliance_checker')
def test_compliance_checker(pyi_builder):
# The test file - taken from the package's own test data/examples. Use an .nc file instead of .cdl one, because
# loading the latter requires ncgen utility to be available on the system.
pkg_path = get_module_attribute('compliance_checker', '__path__')[0]
input_file = Path(pkg_path) / 'tests/data/bad-trajectory.nc'
assert input_file.is_file(), f"Selected test file, {input_file!s} does not exist! Fix the test!"
pyi_builder.test_source("""
import os
import json
import compliance_checker
import compliance_checker.runner
input_file = sys.argv[1]
# Load all available checker classes
check_suite = compliance_checker.runner.CheckSuite()
check_suite.load_all_available_checkers()
# Run cf and adcc checks
return_value, errors = compliance_checker.runner.ComplianceChecker.run_checker(
input_file,
checker_names=['cf', 'acdd'],
verbose=False,
criteria='normal',
output_filename='-',
output_format='json')
# We do not really care about validation results, just that validation finished without raising any exceptions.
print("Return value:", return_value)
print("Errors occurred:", errors)
""", app_args=[str(input_file)])
@importorskip('nbt')
def test_nbt(pyi_builder):
pyi_builder.test_source("""
import nbt
""")
@importorskip('minecraft_launcher_lib')
def test_minecraft_launcher_lib(pyi_builder):
pyi_builder.test_source(
'''
import minecraft_launcher_lib
assert isinstance(minecraft_launcher_lib.utils.get_library_version(), str)
'''
)
@importorskip('moviepy')
def test_moviepy_editor(pyi_builder):
# `moviepy.editor` tries to access the `moviepy.video.fx` and `moviepy.audio.fx` plugins/modules via the
# `moviepy.video.fx.all` and `moviepy.video.fx.all` modules, which in turn programmatically import and
# forward all corresponding submodules.
pyi_builder.test_source("""
import moviepy.editor
""")
@importorskip('customtkinter')
def test_customtkinter(pyi_builder):
pyi_builder.test_source("""
import customtkinter
""")
@importorskip('pylibmagic')
def test_pylibmagic(pyi_builder):
pyi_builder.test_source("""
import pylibmagic
import os
import sys
bundle_dir = os.path.normpath(sys._MEIPASS)
pylibmagic_data_path = f"{bundle_dir}/pylibmagic"
files_to_assert = ["magic.mgc"]
if sys.platform == 'darwin':
files_to_assert.append("libmagic.1.dylib")
elif sys.platform.startswith('linux'):
files_to_assert.append("libmagic.so.1")
for file in files_to_assert:
assert os.path.isfile(f"{pylibmagic_data_path}/{file}"), \
f"The {file} was not collected to _MEIPASS!"
""")
@importorskip('fastparquet')
def test_fastparquet(pyi_builder):
pyi_builder.test_source("""
import fastparquet
""")
@importorskip('librosa')
def test_librosa(pyi_builder):
pyi_builder.test_source("""
import librosa
# Requires intervals.msgpack data file
import librosa.core.intervals
# Requires example files on import
import librosa.util.files
""")
@importorskip('librosa')
def test_librosa_util_function(pyi_builder):
# Test that functions from `librosa.util` that use `numba` vectorization can be run in frozen application.
pyi_builder.test_source("""
import librosa.util
import numpy as np
x = np.array([1, 0, 1, 2, -1, 0, -2, 1])
result = librosa.util.localmin(x)
expected = np.array([False, True, False, False, True, False, True, False])
assert (result == expected).all()
""")
@importorskip('sympy')
def test_sympy(pyi_builder):
pyi_builder.test_source("""
import sympy
""")
@importorskip('bokeh')
def test_bokeh(pyi_builder):
pyi_builder.test_source("""
import bokeh
""")
@importorskip('xyzservices')
def test_xyzservices(pyi_builder):
pyi_builder.test_source("""
import xyzservices.providers
print(xyzservices.providers.CartoDB)
""")
@importorskip('mistune')
def test_mistune(pyi_builder):
pyi_builder.test_source("""
import mistune
""")
@importorskip('jsonschema')
def test_jsonschema(pyi_builder):
pyi_builder.test_source("""
import jsonschema
# Sample schema
schema = {
"type" : "object",
"properties" : {
"price" : {"type" : "number"},
"name" : {"type" : "string"},
},
}
jsonschema.validate(instance={"name" : "Eggs", "price" : 3.38}, schema=schema)
try:
jsonschema.validate(instance={"name" : "Eggs", "price" : "Invalid"}, schema=schema)
except jsonschema.ValidationError as e:
print(f"Validation error: {e}")
""")
@importorskip('psutil')
def test_psutil(pyi_builder):
pyi_builder.test_source("""
import psutil
""")
@importorskip('litestar')
def test_litestar(pyi_builder):
pyi_builder.test_source("""
from litestar import Litestar, get
from litestar.testing import TestClient
from typing import Dict, Any
@get("/sync", sync_to_thread=False)
def sync_hello_world() -> Dict[str, Any]:
return {"hello": "world"}
app = Litestar(route_handlers=[sync_hello_world])
client = TestClient(app)
response = client.get("/sync")
assert response.status_code == 200
assert response.json() == {"hello": "world"}
""")
@importorskip('lingua')
def test_lingua_language_detector(pyi_builder):
pyi_builder.test_source("""
from lingua import Language, LanguageDetectorBuilder
languages = [Language.ENGLISH, Language.FRENCH, Language.GERMAN, Language.SPANISH]
detector = LanguageDetectorBuilder.from_languages(*languages).build()
assert detector.detect_language_of("languages are awesome") == Language.ENGLISH
""")
@importorskip('opencc')
def test_opencc(pyi_builder):
pyi_builder.test_source("""
import opencc
cc = opencc.OpenCC('s2t')
assert cc.convert('开放中文转换') == '開放中文轉換'
""")
@importorskip('jieba')
def test_jieba(pyi_builder):
pyi_builder.test_source("""
import jieba
assert jieba.lcut('我来到北京清华大学') == ['', '来到', '北京', '清华大学']
""")
@importorskip('simplemma')
def test_simplemma(pyi_builder):
pyi_builder.test_source("""
import simplemma
assert simplemma.lemmatize('tests', lang='en') == 'test'
""")
@importorskip('wordcloud')
def test_wordcloud(pyi_builder):
pyi_builder.test_source("""
import wordcloud
wordcloud.WordCloud().generate('test')
""")
@importorskip('eng_to_ipa')
def test_eng_to_ipa(pyi_builder):
pyi_builder.test_source("""
import eng_to_ipa
""")
@importorskip('mecab')
def test_mecab(pyi_builder):
pyi_builder.test_source("""
import mecab
mecab.MeCab()
""")
@importorskip('khmernltk')
def test_khmernltk(pyi_builder):
pyi_builder.test_source("""
import khmernltk
""")
@importorskip('pycrfsuite')
def test_pycrfsuite(pyi_builder):
pyi_builder.test_source("""
import pycrfsuite
""")
@importorskip('pymorphy3')
def test_pymorphy3(pyi_builder):
# Language availability depends on installed packages.
available_languages = []
if can_import_module('pymorphy3_dicts_ru'):
available_languages.append('ru')
if can_import_module('pymorphy3_dicts_uk'):
available_languages.append('uk')
pyi_builder.test_source("""
import sys
import pymorphy3
languages = sys.argv[1:]
print(f"Languages to test: {languages}")
for language in languages:
pymorphy3.MorphAnalyzer(lang=language)
""", app_args=available_languages)
@importorskip('sudachipy')
@importorskip('sudachidict_small')
@importorskip('sudachidict_core')
@importorskip('sudachidict_full')
def test_sudachipy(pyi_builder):
pyi_builder.test_source("""
from sudachipy import Dictionary
Dictionary(dict='small').create()
Dictionary(dict='core').create()
Dictionary(dict='full').create()
""")
@importorskip('laonlp')
def test_laonlp(pyi_builder):
pyi_builder.test_source("""
import laonlp
""")
@importorskip('pythainlp')
def test_pythainlp(pyi_builder):
pyi_builder.test_source("""
import pythainlp
""")
@importorskip('gmsh')
def test_gmsh(pyi_builder):
pyi_builder.test_source("""
import gmsh
""")
@importorskip('sspilib')
def test_sspilib(pyi_builder):
pyi_builder.test_source("""
import sspilib
cred = sspilib.UserCredential(
"username@DOMAIN.COM",
"password",
)
ctx = sspilib.ClientSecurityContext(
"host/server.domain.com",
credential=cred,
)
print(ctx)
""")
@importorskip('rlp')
def test_rlp(pyi_builder):
pyi_builder.test_source("""
import rlp
""")
@importorskip('eth_rlp')
def test_eth_rlp(pyi_builder):
pyi_builder.test_source("""
import eth_rlp
""")
@importorskip('z3c.rml')
def test_z3c_rml_rml2pdf(pyi_builder):
pyi_builder.test_source("""
from z3c.rml import rml2pdf
rml = '''
<!DOCTYPE document SYSTEM "rml.dtd" >
<document filename="test.pdf">
<template showBoundary="1">
<!--Debugging is now turned on, frame outlines -->
<!--will appear on the page -->
<pageTemplate id="main">
<!-- two frames are defined here: -->
<frame id="first" x1="100" y1="400" width="150" height="200" />
<frame id="second" x1="300" y1="400" width="150" height="200" />
</pageTemplate>
</template>
<stylesheet><!-- still empty...--></stylesheet>
<story>
<para>Welcome to RML.</para>
</story>
</document>
'''
pdf_bytes = rml2pdf.parseString(rml)
""")
@importorskip('freetype')
def test_pyi_freetype(pyi_builder):
pyi_builder.test_source("""
import sys
import pathlib
import freetype
# Ensure that the freetype shared library is bundled with the frozen application; otherwise, freetype might be
# using system-wide library.
# First, check that freetype.FT_Library_filename is an absolute path; otherwise, it is likely using
# basename-only ctypes fallback.
ft_library_file = pathlib.Path(freetype.FT_Library_filename)
print(f"FT library file (original): {ft_library_file}", file=sys.stderr)
assert ft_library_file.is_absolute(), "FT library file is not an absolute path!"
# Check that fully-resolved freetype.FT_Library_filename is anchored in fully-resolved frozen application
# directory.
app_dir = pathlib.Path(__file__).resolve().parent
print(f"Application directory: {app_dir}", file=sys.stderr)
ft_library_path = pathlib.Path(ft_library_file).resolve()
print(f"FT library file (resolved): {ft_library_path}", file=sys.stderr)
assert app_dir in ft_library_path.parents, "FT library is not bundled with frozen application!"
""")
@importorskip('vaderSentiment')
def test_vadersentiment(pyi_builder):
pyi_builder.test_source("""
import vaderSentiment.vaderSentiment
vaderSentiment.vaderSentiment.SentimentIntensityAnalyzer()
""")
@importorskip('langchain')
def test_langchain_llm_summarization_checker(pyi_builder):
pyi_builder.test_source("""
import langchain.chains.llm_summarization_checker.base
""")
@importorskip('seedir')
def test_seedir(pyi_builder):
pyi_builder.test_source("""
import seedir
""")
@importorskip('PyTaskbar')
@pytest.mark.skipif(not is_win, reason='PyTaskbar is supported only on Windows')
def test_PyTaskbar(pyi_builder):
pyi_builder.test_source("""
import PyTaskbar
""")
@importorskip('celpy')
def test_celpy(pyi_builder):
pyi_builder.test_source("""
import celpy
""")
@importorskip('pygwalker')
def test_pygwalker(pyi_builder):
pyi_builder.test_source("""
import pygwalker
""")
@importorskip('pypylon')
def test_pypylon(pyi_builder):
pyi_builder.test_source("""
from pypylon import pylon
""")