Repository: pyinstaller/pyinstaller

Issue:
Error "ImportError: ERROR: recursion is detected during loading of "cv2" binary extensions. Check OpenCV installation." with Pyinstaller
Environment:

Mac M1 - macOS Monterey

PyInstaller: 5.4.1

Python: 3.8.10

opencv-contrib-python==4.6.0.66
opencv-python==4.6.0.66


Script
`pyinstaller -n "App" --windowed App.py`

Problem:

I am trying to compile my Python code with PyInstaller. The exe is created and when I execute it, I get this error message:
```
Traceback (most recent call last):
  File "App.py", line 6, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 499, in exec_module
  File "main.py", line 28, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 499, in exec_module
  File "image_manager.py", line 6, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 499, in exec_module
  File "workers/image_worker.py", line 6, in <module>
  File "PyInstaller/loader/pyimod02_importers.py", line 499, in exec_module
  File "modules/apply_effects.py", line 3, in <module>
  File "/Users/Josh/Desktop/Work-Stuff/App-Offline/dist/App.app/Contents/MacOS/cv2/__init__.py", line 181, in <module>
    bootstrap()
  File "/Users/Josh/Desktop/Work-Stuff/App-Offline/dist/App.app/Contents/MacOS/cv2/__init__.py", line 153, in bootstrap
    native_module = importlib.import_module("cv2")
  File "importlib/__init__.py", line 127, in import_module
    applySysPathWorkaround = True
  File "/Users/Josh/Desktop/Work-Stuff/App-Offline/dist/App.app/Contents/MacOS/cv2/__init__.py", line 181, in <module>
    bootstrap()
  File "/Users/Josh/Desktop/Work-Stuff/App-Offline/dist/App.app/Contents/MacOS/cv2/__init__.py", line 76, in bootstrap
    raise ImportError('ERROR: recursion is detected during loading of "cv2" binary extensions. Check OpenCV installation.')
ImportError: ERROR: recursion is detected during loading of "cv2" binary extensions. Check OpenCV installation.
```

I fixed this by replacing the cv2 folder in Contents/Resources with the cv2 folder in site-packages.

Notes:
I ran the `diff -r .venv/lib/python3.8/site-packages/cv2 dist/App.app/Contents/Resources/cv2 >> cv2-diff.txt` to see the difference between the two directories, and this was the relevant output.
```
Only in .venv/lib/python3.8/site-packages/cv2: .dylibs
Only in .venv/lib/python3.8/site-packages/cv2: cv2.abi3.so
```

Update:
I don't need to copy the whole cv2 folder from site-packages, I only need to copy the .dylibs directory and the cv2.abi3.so file from site-packages/cv2





Discussion:
What version of `pyinstaller-hooks-contrib` do you have installed?
Nevermind, I've reproduced the problem, which is applicable only to .app bundles.

What happens is that we move the collected `cv2` .py files from `Contents/MacOS/cv2` to `Contents/Resources/cv2` and symlink them back. But we don't do the same with the extension, and therefore the `cv2` loader code fails to set the correct path to the extension (because it resolves the actual paths to .py files). Adding `cv2` as an exemption similar to what we already do for PyQt/PySide directories ([here](https://github.com/pyinstaller/pyinstaller/blob/039a4c08be47e42a6a562ea14c53694112e727b9/PyInstaller/building/osx.py#L195)) should also fix the problem - although god knows what notarization has to say about it...
Hey @rokm, I did as you said and on my local version of PyInstaller edited the osx.py and added cv2 to the set of exceptions as it was done ([here](https://github.com/pyinstaller/pyinstaller/blob/039a4c08be47e42a6a562ea14c53694112e727b9/PyInstaller/building/osx.py#L195)), and it worked. Should I create a PR?

I think instead of exempting `cv2` from relocation, we will need to exempt all `.py` files, because this will be a general problem with packages where we collect sources (perhaps moreso packages where we collect *only* sources, although that's only `cv2` at the moment). I'll create a PR once I do some testing.

Resolving patch:
diff --git a/PyInstaller/building/osx.py b/PyInstaller/building/osx.py
--- a/PyInstaller/building/osx.py
+++ b/PyInstaller/building/osx.py
@@ -192,7 +192,12 @@ def assemble(self):
                     strict_arch_validation=(typ == 'EXTENSION'),
                 )
             # Add most data files to a list for symlinking later.
-            if typ == 'DATA' and base_path not in _QT_BASE_PATH:
+            # Exempt python source files from this relocation, because their real path might need to resolve
+            # to the directory that also contains the extension module.
+            relocate_file = type == 'DATA' and base_path not in _QT_BASE_PATH
+            if relocate_file and os.path.splitext(inm)[1].lower() in {'.py', '.pyc'}:
+                relocate_file = False
+            if relocate_file:
                 links.append((inm, fnm))
             else:
                 tofnm = os.path.join(self.name, "Contents", "MacOS", inm)


Test patch: