Repository: marcelotduarte/cx_Freeze (python)

Issue:
cx-Freeze - No module named 'scipy._lib.array_api_compat._aliases'
**Prerequisite**
This was previously reported in the closed issue #2544, where no action was taken. I include a minimal script that produces the problem for me.

**Describe the bug**
When running the compiled executable, i get the following error:
```
PS C:\dat\projects\gazeMapper\cxFreeze\build\exe.win-amd64-3.10> .\test.exe
Traceback (most recent call last):
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\Lib\site-packages\cx_Freeze\initscripts\__startup__.py", line 141, in run
    module_init.run(f"__main__{name}")
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\Lib\site-packages\cx_Freeze\initscripts\console.py", line 25, in run
    exec(code, main_globals)
  File "C:\dat\projects\gazeMapper\cxFreeze\test.py", line 1, in <module>
    from scipy.spatial.transform import Rotation
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\spatial\__init__.py", line 110, in <module>
    from ._kdtree import *
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\spatial\_kdtree.py", line 4, in <module>
    from ._ckdtree import cKDTree, cKDTreeNode
  File "_ckdtree.pyx", line 11, in init scipy.spatial._ckdtree
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\sparse\__init__.py", line 293, in <module>
    from ._base import *
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\sparse\_base.py", line 5, in <module>
    from ._sputils import (asmatrix, check_reshape_kwargs, check_shape,
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\sparse\_sputils.py", line 10, in <module>
    from scipy._lib._util import np_long, np_ulong
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\_lib\_util.py", line 18, in <module>
    from scipy._lib._array_api import array_namespace, is_numpy, size as xp_size
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\_lib\_array_api.py", line 21, in <module>
    from scipy._lib.array_api_compat import (
  File "C:\dat\projects\gazeMapper\cxFreeze\.venv\lib\site-packages\scipy\_lib\array_api_compat\numpy\__init__.py", line 16, in <module>
    __import__(__package__ + '.linalg')
ModuleNotFoundError: No module named 'scipy._lib.array_api_compat._aliases'
```

**To Reproduce**
Two files:
test.py
```python
from scipy.spatial.transform import Rotation
print(Rotation.from_euler('XYZ', [10, 10, 10], degrees=True).as_matrix())
```
setup.py:
```python
import cx_Freeze
import pathlib
import sys
import site

path = pathlib.Path(__file__).absolute().parent

def get_include_files():
    # don't know if this is a bad idea, it certainly didn't help
    files = []

    # scipy dlls
    for d in site.getsitepackages():
        d=pathlib.Path(d)/'scipy'/'.libs'
        if d.is_dir():
            for f in d.iterdir():
                if f.is_file() and f.suffix=='' or f.suffix in ['.dll']:
                    files.append((f,pathlib.Path('lib')/f.name))
    return files

build_options = {
    "build_exe": {
        "optimize": 1,
        "packages": [
            'numpy','scipy'
        ],
        "excludes":["tkinter"],
        "zip_include_packages": "*",
        "zip_exclude_packages": [],
        "silent_level": 1,
        "include_msvcr": True
    }
}
if sys.platform.startswith("win"):
    build_options["build_exe"]["include_files"] = get_include_files()

cx_Freeze.setup(
    name="test",
    version="0.0.1",
    description="test",
    executables=[
        cx_Freeze.Executable(
            script=path / "test.py",
            target_name="test"
        )
    ],
    options=build_options,
    py_modules=[]
)
```

**Expected behavior**
exe runs

**Desktop (please complete the following information):**
 - Windows 11 Enterprise
 - amd64
 - cx_Freeze version 7.2.2
 - Python version 3.10
 - Numpy 2.1.1
 - Scipy 1.14.1

**Additional context**
at `\.venv\Lib\site-packages\scipy\_lib\array_api_compat` there is no `_aliases.py`, only `__init__.py` with the following content:
```python
__version__ = '1.5.1'
from .common import *  # noqa: F401, F403
```
`_aliases.py` does exist at `\.venv\Lib\site-packages\scipy\_lib\array_api_compat\common`

Both files are packed into library.zip (whole scipy tree is)


Discussion:
Changing the config to `"zip_exclude_packages": ['scipy']`, things work. I assume it should work just fine/the same from the zip file. This will be my workaround for now

Patch:
diff --git a/cx_Freeze/finder.py b/cx_Freeze/finder.py
--- a/cx_Freeze/finder.py
+++ b/cx_Freeze/finder.py
@@ -537,7 +537,10 @@ def _replace_package_in_code(module: Module) -> CodeType:
             # Insert a bytecode to set __package__ as module.parent.name
             codes = [LOAD_CONST, pkg_const_index, STORE_NAME, pkg_name_index]
             codestring = bytes(codes) + code.co_code
-            consts.append(module.parent.name)
+            if module.file.stem == "__init__":
+                consts.append(module.name)
+            else:
+                consts.append(module.parent.name)
             code = code_object_replace(
                 code, co_code=codestring, co_consts=consts
             )
diff --git a/cx_Freeze/hooks/scipy.py b/cx_Freeze/hooks/scipy.py
--- a/cx_Freeze/hooks/scipy.py
+++ b/cx_Freeze/hooks/scipy.py
@@ -18,12 +18,18 @@
 def load_scipy(finder: ModuleFinder, module: Module) -> None:
     """The scipy package.
 
-    Supported pypi and conda-forge versions (lasted tested version is 1.11.2).
+    Supported pypi and conda-forge versions (lasted tested version is 1.14.1).
     """
     source_dir = module.file.parent.parent / f"{module.name}.libs"
     if source_dir.exists():  # scipy >= 1.9.2 (windows)
-        finder.include_files(source_dir, f"lib/{source_dir.name}")
-        replace_delvewheel_patch(module)
+        if IS_WINDOWS:
+            finder.include_files(source_dir, f"lib/{source_dir.name}")
+            replace_delvewheel_patch(module)
+        else:
+            target_dir = f"lib/{source_dir.name}"
+            for source in source_dir.iterdir():
+                finder.lib_files[source] = f"{target_dir}/{source.name}"
+
     finder.include_package("scipy.integrate")
     finder.include_package("scipy._lib")
     finder.include_package("scipy.misc")


Test patch:
diff --git a/samples/scipy/test_scipy.py b/samples/scipy/test_scipy.py
--- a/samples/scipy/test_scipy.py
+++ b/samples/scipy/test_scipy.py
@@ -1,8 +1,6 @@
 """A simple script to demonstrate scipy."""
 
-from scipy.stats import norm
+from scipy.spatial.transform import Rotation
 
 if __name__ == "__main__":
-    print(
-        "bounds of distribution lower: {}, upper: {}".format(*norm.support())
-    )
+    print(Rotation.from_euler("XYZ", [10, 10, 10], degrees=True).as_matrix())