hook-numpy.py 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2013-2024, PyInstaller Development Team.
  3. #
  4. # Distributed under the terms of the GNU General Public License (version 2
  5. # or later) with exception for distributing the bootloader. Additional
  6. #
  7. # The full license is in the file COPYING.txt, distributed with this software.
  8. #
  9. # SPDX-License-Identifier: (GPL-2.0-or-later WITH Bootloader-exception)
  10. #-----------------------------------------------------------------------------
  11. # --- Copyright Disclaimer ---
  12. #
  13. # An earlier copy of this hook has been submitted to the NumPy project, where it was integrated in v1.23.0rc1
  14. # (https://github.com/numpy/numpy/pull/20745), under terms and conditions outlined in their repository [1].
  15. #
  16. # A special provision is hereby granted to the NumPy project that allows the NumPy copy of the hook to incorporate the
  17. # changes made to this (PyInstaller's) copy of the hook, subject to their licensing terms as opposed to PyInstaller's
  18. # (stricter) licensing terms.
  19. #
  20. # .. refs:
  21. #
  22. # [1] NumPy's license: https://github.com/numpy/numpy/blob/master/LICENSE.txt
  23. # NOTE: when comparing the contents of this hook and the NumPy version of the hook (for example, to port changes), keep
  24. # in mind that this copy is PyInstaller-centric - it caters to the version of PyInstaller it is bundled with, but needs
  25. # to account for different behavior of different NumPy versions. In contrast, the NumPy copy of the hook caters to the
  26. # version of NumPy it is bundled with, but should account for behavior differences in different PyInstaller versions.
  27. # Override the default hook priority so that our copy of hook is used instead of NumPy's one (which has priority 0,
  28. # the default for upstream hooks).
  29. # $PyInstaller-Hook-Priority: 1
  30. from PyInstaller import compat
  31. from PyInstaller.utils.hooks import (
  32. get_installer,
  33. collect_dynamic_libs,
  34. )
  35. from packaging.version import Version
  36. numpy_version = Version(compat.importlib_metadata.version("numpy")).release
  37. numpy_installer = get_installer('numpy')
  38. hiddenimports = []
  39. datas = []
  40. binaries = []
  41. # Collect shared libraries that are bundled inside the numpy's package directory. With PyInstaller 6.x, the directory
  42. # layout of collected shared libraries should be preserved (to match behavior of the binary dependency analysis). In
  43. # earlier versions of PyInstaller, it was necessary to collect the shared libraries into application's top-level
  44. # directory (because that was also what binary dependency analysis in PyInstaller < 6.0 did).
  45. binaries += collect_dynamic_libs("numpy")
  46. # Check if we are using Anaconda-packaged numpy
  47. if numpy_installer == 'conda':
  48. # Collect DLLs for NumPy and its dependencies (MKL, OpenBlas, OpenMP, etc.) from the communal Conda bin directory.
  49. from PyInstaller.utils.hooks import conda_support
  50. datas += conda_support.collect_dynamic_libs("numpy", dependencies=True)
  51. # NumPy 1.26 started using `delvewheel` for its Windows PyPI wheels. While contemporary PyInstaller versions
  52. # automatically pick up DLLs from external `numpy.libs` directory, this does not work on Anaconda python 3.8 and 3.9
  53. # due to defunct `os.add_dll_directory`, which forces `delvewheel` to use the old load-order file approach. So we need
  54. # to explicitly ensure that load-order file as well as DLLs are collected.
  55. #
  56. # Under contemporary python versions, we might still need to explicitly collect the DLLs from `numpy.libs` directory
  57. # to accommodate the cases when some other package's `.lib` directory (for example, `pandas.libs`) contains a DLL
  58. # with the same name, and binary dependency analysis ends up resolving that one.
  59. #
  60. # The installer check compares against 'conda', because PyPI wheels might be installed by installers other than 'pip'
  61. # (for example, 'uv' - see #9360).
  62. if compat.is_win and numpy_version >= (1, 26) and numpy_installer != 'conda':
  63. from PyInstaller.utils.hooks import collect_delvewheel_libs_directory
  64. datas, binaries = collect_delvewheel_libs_directory("numpy", datas=datas, binaries=binaries)
  65. # Submodules PyInstaller cannot detect (probably because they are only imported by extension modules, which PyInstaller
  66. # cannot read).
  67. if numpy_version >= (2, 0):
  68. # In v2.0.0, `numpy.core` was renamed to `numpy._core`.
  69. # See https://github.com/numpy/numpy/commit/47b70cbffd672849a5d3b9b6fa6e515700460fd0
  70. hiddenimports += ['numpy._core._dtype_ctypes', 'numpy._core._multiarray_tests']
  71. else:
  72. hiddenimports += ['numpy.core._dtype_ctypes']
  73. # See https://github.com/numpy/numpy/commit/99104bd2d0557078d7ea9a590129c87dd63df623
  74. if numpy_version >= (1, 25):
  75. hiddenimports += ['numpy.core._multiarray_tests']
  76. # Starting with v2.3.0, we need to add `numpy._core._exceptions` to hiddenimports; in previous versions, this module
  77. # was picked up due to explicit import in `numpy._core._methods`, which was removed as part of cleanup in
  78. # https://github.com/numpy/numpy/commit/a51a4f5c10aa9b7962ff1e7e9b5f9b7d91c51489
  79. if numpy_version >= (2, 3, 0):
  80. hiddenimports += ['numpy._core._exceptions']
  81. # This hidden import was removed from NumPy hook in v1.25.0 (https://github.com/numpy/numpy/pull/22666). According to
  82. # comment in the linked PR, it should have been unnecessary since v1.19.
  83. if compat.is_conda and numpy_version < (1, 19):
  84. hiddenimports += ["six"]
  85. # Remove testing and building code and packages that are referenced throughout NumPy but are not really dependencies.
  86. excludedimports = [
  87. "scipy",
  88. "pytest",
  89. "nose",
  90. "f2py",
  91. "setuptools",
  92. ]
  93. # As of v1.22.0, numpy.testing (imported for example by some scipy modules) requires numpy.distutils and distutils.
  94. # This was due to numpy.testing adding import of numpy.testing._private.extbuild, which in turn imported numpy.distutils
  95. # and distutils. These imports were moved into functions that require them in v1.22.2 and v.1.23.0.
  96. # See: https://github.com/numpy/numpy/pull/20831 and https://github.com/numpy/numpy/pull/20906
  97. # So we can exclude them for all numpy versions except for v1.22.0 and v1.22.1 - the main motivation is to avoid pulling
  98. # in `setuptools` (which nowadays provides its vendored version of `distutils`).
  99. if numpy_version < (1, 22, 0) or numpy_version > (1, 22, 1):
  100. excludedimports += [
  101. "distutils",
  102. "numpy.distutils",
  103. ]
  104. # In numpy v2.0.0, numpy.f2py submodule has been added to numpy's `__all__` attribute. Therefore, using
  105. # `from numpy import *` leads to an error if `numpy.f2py` is excluded (seen in scipy 1.14). The exclusion in earlier
  106. # releases was not reported to cause any issues, so keep it around. Although it should be noted that it does break an
  107. # explicit import (i.e., `import numpy.f2py`) from user's code as well, because it prevents collection of other
  108. # submodules from `numpy.f2py`.
  109. if numpy_version < (2, 0):
  110. excludedimports += [
  111. "numpy.f2py",
  112. ]