Repository: pyinstaller/pyinstaller

Issue:
OSError: Cannot load native module 'Cryptodome.Cipher._raw_ecb'
The stacktrace is

``` bash
Traceback (most recent call last):
  File "runserver.py", line 15, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "pogom/search.py", line 23, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "pogom/pgoapi/__init__.py", line 1, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "pogom/pgoapi/pgoapi.py", line 32, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "pogom/pgoapi/auth_google.py", line 27, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "gpsoauth/__init__.py", line 4, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "gpsoauth/google.py", line 4, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "Cryptodome/PublicKey/RSA.py", line 79, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "Cryptodome/IO/PKCS8.py", line 73, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "Cryptodome/IO/_PBES.py", line 44, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "Cryptodome/Cipher/__init__.py", line 78, in <module>
  File "/Library/Python/2.7/site-packages/PyInstaller-3.2-py2.7.egg/PyInstaller/loader/pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "Cryptodome/Cipher/_mode_ecb.py", line 46, in <module>
  File "Cryptodome/Util/_raw_api.py", line 168, in load_pycryptodome_raw_lib
OSError: Cannot load native module 'Cryptodome.Cipher._raw_ecb'
```

on 

``` bash
$ sw_vers
ProductName:    Mac OS X
ProductVersion: 10.11.6
BuildVersion:   15G31
```



Discussion:
I'm having the same Issue on Windows 10. Coincidentally I am also using the `pgoapi` module.

Still happens. Using Windows 10 here too. Minimal program to test:

from Cryptodome import Cipher
print(Cipher)

Building that gives me no errors, but running the exe it gives me:

```
Traceback (most recent call last):
  File "test.py", line 1, in <module>
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 634, in _load_backward_compatible
  File "c:\python35\lib\site-packages\pyinstaller-3.3.dev0+g1804636-py3.5.egg\Py
Installer\loader\pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "Cryptodome\Cipher\__init__.py", line 78, in <module>
  File "<frozen importlib._bootstrap>", line 969, in _find_and_load
  File "<frozen importlib._bootstrap>", line 958, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 664, in _load_unlocked
  File "<frozen importlib._bootstrap>", line 634, in _load_backward_compatible
  File "c:\python35\lib\site-packages\pyinstaller-3.3.dev0+g1804636-py3.5.egg\Py
Installer\loader\pyimod03_importers.py", line 389, in load_module
    exec(bytecode, module.__dict__)
  File "Cryptodome\Cipher\_mode_ecb.py", line 46, in <module>
  File "Cryptodome\Util\_raw_api.py", line 168, in load_pycryptodome_raw_lib
OSError: Cannot load native module 'Cryptodome.Cipher._raw_ecb'
Failed to execute script test
```

I tried setting hiddenimports=['Cryptodome.Cipher', 'Cryptodome'], but with no effect.

Where abouts in the code could I start looking for ways to fix this myself with the view to creating a pull request?

Cheers.

Sorry, should have said:
Python 3.5.2 (v3.5.2:4def2a2901a5, Jun 25 2016, 22:01:18) [MSC v.1900 32 bit (Intel)] on win32

Please have a look at [Make sure everything is packaged correctly](https://github.com/pyinstaller/pyinstaller/wiki/How-to-Report-Bugs#make-sure-everything-is-packaged-correctly) for tips to find out what is missing.

Hi,

I tried --onedir, --noupx, and manually putting the module into the 
output directory, I even put the whole site-packages directory from my 
virtualenv into the output directory and added it to sys.path in my 
code, all with no dice.

I'll look into pyi-archive-viewer tonight, and see if I can figure it out.

I read somewhere about hooks, do I need to write one of those maybe?

I've been over the manual a bunch of times trying to find stuff relevant 
to the problem, and always come up blank.

Cheers,

On 25/08/2016 08:38, Hartmut Goebel wrote:

> Please have a look at Make sure everything is packaged correctly 
> https://github.com/pyinstaller/pyinstaller/wiki/How-to-Report-Bugs#make-sure-everything-is-packaged-correctly 
> for tips to find out what is missing.
> 
> —
> You are receiving this because you commented.
> Reply to this email directly, view it on GitHub 
> https://github.com/pyinstaller/pyinstaller/issues/2125#issuecomment-242305264, 
> or mute the thread 
> https://github.com/notifications/unsubscribe-auth/AENM6RaGg6IRAfaxA3W0JlD-YqzODZvzks5qjUaRgaJpZM4JaCyx.

I'm currently trying to run a virtual environment inside my code... Even that doesn't seem to be working.

I'd look into the causes, but I honestly don't even know where to start. If someone can point me in the right direction I'll happily take a look.

This is actually becoming quite a big problem for me since the main piece of software I have created relies on Cryptodome, and I can't build it as an exe.


Resolving patch:
diff --git a/PyInstaller/hooks/hook-Cryptodome.py b/PyInstaller/hooks/hook-Cryptodome.py
new file mode 100644
--- /dev/null
+++ b/PyInstaller/hooks/hook-Cryptodome.py
@@ -0,0 +1,40 @@
+#-----------------------------------------------------------------------------
+# Copyright (c) 2005-2016, PyInstaller Development Team.
+#
+# Distributed under the terms of the GNU General Public License with exception
+# for distributing bootloader.
+#
+# The full license is in the file COPYING.txt, distributed with this software.
+#-----------------------------------------------------------------------------
+
+"""
+Hook for Cryptodome module: https://pypi.python.org/pypi/pycryptodomex
+
+Tested with Cryptodomex 3.4.2, Python 2.7 & 3.5, Windows
+"""
+
+import os
+import glob
+
+from PyInstaller.compat import EXTENSION_SUFFIXES
+from PyInstaller.utils.hooks import get_module_file_attribute
+
+# Include the modules as binaries in a subfolder named like the package.
+# Cryptodome's loader expects to find them inside the package directory for
+# the main module. We cannot use hiddenimports because that would add the
+# modules outside the package.
+
+binaries = []
+binary_module_names = [
+    'Cryptodome.Cipher',
+    'Cryptodome.Util',
+    'Cryptodome.Hash',
+    'Cryptodome.Protocol',
+]
+
+for module_name in binary_module_names:
+    m_dir = os.path.dirname(get_module_file_attribute(module_name))
+    for ext in EXTENSION_SUFFIXES:
+        module_bin = glob.glob(os.path.join(m_dir, '_*%s' % ext))
+        for f in module_bin:
+            binaries.append((f, module_name.replace('.', '/')))


Test patch:
diff --git a/tests/functional/test_libraries.py b/tests/functional/test_libraries.py
--- a/tests/functional/test_libraries.py
+++ b/tests/functional/test_libraries.py
@@ -407,6 +407,16 @@ def test_pycrypto(pyi_builder):
             AES.new("\\0" * BLOCK_SIZE).encrypt("\\0" * BLOCK_SIZE)))
         """)
 
+
+@importorskip('Cryptodome')
+def test_cryptodome(pyi_builder):
+    pyi_builder.test_source(
+        """
+        from Cryptodome import Cipher
+        print('Cryptodome Cipher Module:', Cipher)
+        """)
+
+
 @importorskip('requests')
 def test_requests(tmpdir, pyi_builder, data_dir, monkeypatch):
     # Note that including the data_dir fixture copies files needed by this test.
diff --git a/tests/requirements-libraries.txt b/tests/requirements-libraries.txt
--- a/tests/requirements-libraries.txt
+++ b/tests/requirements-libraries.txt
@@ -53,6 +53,7 @@ twisted
 zeep
 pyexcelerate
 Pillow
+pycryptodomex
 future
 pyusb