This commit is contained in:
2025-09-07 22:09:54 +02:00
parent e1b817252c
commit 2fc0d000b6
7796 changed files with 2159515 additions and 933 deletions

View File

@ -0,0 +1,36 @@
"""This hook should collect all binary files and any hidden modules that numpy
needs.
Our (some-what inadequate) docs for writing PyInstaller hooks are kept here:
https://pyinstaller.readthedocs.io/en/stable/hooks.html
"""
from PyInstaller.compat import is_pure_conda
from PyInstaller.utils.hooks import collect_dynamic_libs
# Collect all DLLs inside numpy's installation folder, dump them into built
# app's root.
binaries = collect_dynamic_libs("numpy", ".")
# If using Conda without any non-conda virtual environment manager:
if is_pure_conda:
# Assume running the NumPy from Conda-forge and collect it's DLLs from the
# communal Conda bin directory. DLLs from NumPy's dependencies must also be
# collected to capture MKL, OpenBlas, OpenMP, etc.
from PyInstaller.utils.hooks import conda_support
datas = conda_support.collect_dynamic_libs("numpy", dependencies=True)
# Submodules PyInstaller cannot detect. `_dtype_ctypes` is only imported
# from C and `_multiarray_tests` is used in tests (which are not packed).
hiddenimports = ['numpy._core._dtype_ctypes', 'numpy._core._multiarray_tests']
# Remove testing and building code and packages that are referenced throughout
# NumPy but are not really dependencies.
excludedimports = [
"scipy",
"pytest",
"f2py",
"setuptools",
"distutils",
"numpy.distutils",
]

View File

@ -0,0 +1,13 @@
from typing import Final
# from `PyInstaller.compat`
is_conda: Final[bool]
is_pure_conda: Final[bool]
# from `PyInstaller.utils.hooks`
def is_module_satisfies(requirements: str, version: None = None, version_attr: None = None) -> bool: ...
binaries: Final[list[tuple[str, str]]]
hiddenimports: Final[list[str]]
excludedimports: Final[list[str]]

View File

@ -0,0 +1,16 @@
import pytest
from numpy.testing import IS_EDITABLE, IS_WASM
if IS_WASM:
pytest.skip(
"WASM/Pyodide does not use or support Fortran",
allow_module_level=True
)
if IS_EDITABLE:
pytest.skip(
"Editable install doesn't support tests with a compile step",
allow_module_level=True
)

View File

@ -0,0 +1,32 @@
"""A crude *bit of everything* smoke test to verify PyInstaller compatibility.
PyInstaller typically goes wrong by forgetting to package modules, extension
modules or shared libraries. This script should aim to touch as many of those
as possible in an attempt to trip a ModuleNotFoundError or a DLL load failure
due to an uncollected resource. Missing resources are unlikely to lead to
arithmetic errors so there's generally no need to verify any calculation's
output - merely that it made it to the end OK. This script should not
explicitly import any of numpy's submodules as that gives PyInstaller undue
hints that those submodules exist and should be collected (accessing implicitly
loaded submodules is OK).
"""
import numpy as np
a = np.arange(1., 10.).reshape((3, 3)) % 5
np.linalg.det(a)
a @ a
a @ a.T
np.linalg.inv(a)
np.sin(np.exp(a))
np.linalg.svd(a)
np.linalg.eigh(a)
np.unique(np.random.randint(0, 10, 100))
np.sort(np.random.uniform(0, 10, 100))
np.fft.fft(np.exp(2j * np.pi * np.arange(8) / 8))
np.ma.masked_array(np.arange(10), np.random.rand(10) < .5).sum()
np.polynomial.Legendre([7, 8, 9]).roots()
print("I made it!")

View File

@ -0,0 +1,35 @@
import subprocess
from pathlib import Path
import pytest
# PyInstaller has been very unproactive about replacing 'imp' with 'importlib'.
@pytest.mark.filterwarnings('ignore::DeprecationWarning')
# It also leaks io.BytesIO()s.
@pytest.mark.filterwarnings('ignore::ResourceWarning')
@pytest.mark.parametrize("mode", ["--onedir", "--onefile"])
@pytest.mark.slow
def test_pyinstaller(mode, tmp_path):
"""Compile and run pyinstaller-smoke.py using PyInstaller."""
pyinstaller_cli = pytest.importorskip("PyInstaller.__main__").run
source = Path(__file__).with_name("pyinstaller-smoke.py").resolve()
args = [
# Place all generated files in ``tmp_path``.
'--workpath', str(tmp_path / "build"),
'--distpath', str(tmp_path / "dist"),
'--specpath', str(tmp_path),
mode,
str(source),
]
pyinstaller_cli(args)
if mode == "--onefile":
exe = tmp_path / "dist" / source.stem
else:
exe = tmp_path / "dist" / source.stem / source.stem
p = subprocess.run([str(exe)], check=True, stdout=subprocess.PIPE)
assert p.stdout.strip() == b"I made it!"