2

I have a few Python files, and I want to pack them into an exe and distribute commercially, but also make sure that no one is able to see the source code of the files... I have heard about pyarmor module, but it does not provide full obfuscation.. Any help regarding this?

I have heard stuff about Cython and Pyinstaller, but never got it to work. I have seen many posts on how to first convert the code to C source code, and then compile it to exe but none of those worked for me.. Anyone here willing to tell me how can I achieve the same?

Hunter
  • 188
  • 2
  • 11
  • Hey @mkrieger1, I answered my own question. I didn't mean to ask a question. Did I do anything wrong? – Hunter Jun 07 '21 at 08:47
  • 1
    Not really wrong, but the question you've asked already exists and has answers. If you want to contribute something new, either post an answer to the already existing question, or make your question more specific to distinguish it from the other question. – mkrieger1 Jun 07 '21 at 08:48
  • Oh ok. I'mma add stuff like Cython and other stuff to the question so that it is distinguished – Hunter Jun 07 '21 at 08:49
  • @mkrieger1 sadly the dupe target was closed as "seeking recommendations" (wrongly in my opinion), so no further answer can be added (feel free to vote to reopen:)). Hunter, in the dupe target there are already solution with cython, if you think your answer adds value, move it there once it is reopened. – ead Jun 07 '21 at 10:12
  • Oh @ead, how can I do that? Sorry am new to Stack. Just wanted to help people cz I personally had too much problem with doing this ¯\_(ツ)_/¯ – Hunter Jun 07 '21 at 11:03

1 Answers1

8

So we will be using a few modules to -->

--> Convert our Python code to C source code and PYD files (PYD is the Python equivalent of DLL files)

--> Pack them into an exe


The modules we will be needing are -->

--> Cython

--> Pyinstaller


We will be using my project files (as an example), to demonstrate how to convert all the files to C source code and PYD files

The files in my project are -->

--> chat_screen.py

--> constants.py

--> main_app.py

--> rooms_list_screen.py


  1. We will make another folder, and name it Distributable executable files (You can name it whatever u want)

  2. We will be adding all the Python files in the folder but change the extension of all the files from py to pyx

  3. Then make another file, known as setup.py and add the following code to it (Note the extension of that file should be py and not pyx)


The setup.py file:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Distutils import build_ext
from Cython.Build import cythonize

ext_modules = [
    Extension("chat_screen", ["chat_screen.pyx"]),
    Extension("constants", ["constants.pyx"]),
    Extension("rooms_list_screen", ["rooms_list_screen.pyx"]),
    Extension("main_app", ["main_app.pyx"])
]
setup(name='My Cython App',
      cmdclass={'build_ext': build_ext},
      ext_modules=cythonize(ext_modules),
      compiler_directives={'language_level': 3},
      zip_safe=False
      )

So what we are doing here is, using the Extension class to make all the files, an extension of the app. So the format for the same would be Extension("The name by which u r importing the file (mostly the file name)", ["The full file name with its pyx extension"]

Then we are making a setup function and specifying the list of the extensions we made earlier in the ext_modules kwarg.. But, we have to wrap the list inside of the cythonize function to compile all the files into C source code and pyd files.


  1. Now, open a command prompt window in the same folder and run this command python setup.py build_ext --inplace and wait. This will output some C and PYD files.. These are your Python files, now compiled.

  2. Now, make another file named main.py (Note the extension of that file should be py and not pyx) and add this code to it __import__("main_app") (Replace the file name with your main file), and then run it simply. If your script works without any errors, that means you are ready to compile your app into an exe!!

  3. Now, install Pyinstaller with pip install pyinstaller and in the same folder, open a command prompt and run pyinstaller main.py and wait.

  4. You will see a spec file in the same directory and a build and dist folder. We can ignore both of those folders for now.

  5. Then, open the spec file and you will see something like this in it


block_cipher = None


a = Analysis(['main.py'],
             pathex=['Omitted due to privacy concerns'],
             binaries=[],
             datas=[],
             hiddenimports=[],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='main',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='main')

In the datas kwarg of the Analysis function, add the following code

[
                 ("chat_screen.c", "."),
                 ("chat_screen.cp39-win_amd64.pyd", "."),
                 ("constants.c", "."),
                 ("constants.cp39-win_amd64.pyd", "."),
                 ("main_app.c", "."),
                 ("main_app.cp39-win_amd64.pyd", "."),
                 ("rooms_list_screen.c", "."),
                 ("rooms_list_screen.cp39-win_amd64.pyd", ".")
             ]

So what we are doing here is, adding every C and pyd file that was generated in the spec file, to make sure they make into the compiled exe. The . represents that the files should be kept in the root of the exe directory.

Also, add the list of imports you are doing in your script in the hiddenimports kwarg of the Analysis function, which in my case is ['requests', 'kivy'].. So my spec file looks something like this

# -*- mode: python ; coding: utf-8 -*-
block_cipher = None


a = Analysis(['main.py'],
             pathex=['Omitted due to privacy concerns'],
             binaries=[],
             datas=[
                 ("chat_screen.c", "."),
                 ("chat_screen.cp39-win_amd64.pyd", "."),
                 ("constants.c", "."),
                 ("constants.cp39-win_amd64.pyd", "."),
                 ("main_app.c", "."),
                 ("main_app.cp39-win_amd64.pyd", "."),
                 ("rooms_list_screen.c", "."),
                 ("rooms_list_screen.cp39-win_amd64.pyd", ".")
             ],
             hiddenimports=['requests', 'kivy'],
             hookspath=[],
             runtime_hooks=[],
             excludes=[],
             win_no_prefer_redirects=False,
             win_private_assemblies=False,
             cipher=block_cipher,
             noarchive=False)
pyz = PYZ(a.pure, a.zipped_data,
             cipher=block_cipher)
exe = EXE(pyz,
          a.scripts,
          [],
          exclude_binaries=True,
          name='main',
          debug=False,
          bootloader_ignore_signals=False,
          strip=False,
          upx=True,
          console=True )
coll = COLLECT(exe,
               a.binaries,
               a.zipfiles,
               a.datas,
               strip=False,
               upx=True,
               upx_exclude=[],
               name='main')


  1. Now, in a command prompt window in the same folder, type in pyinstaller main.spec and press Enter.. Now your files are being converted to an exe!!! Just wait for some time now, and when the process is finished, just go to the following directory in the same folder dist\<name>\ and you will find all the files of your app here. If you want to launch the app, look for a main.exe file and run it.. There, your app is now ready!!!

If you want to make it into a single file, then instead of just doing pyinstaller main.spec, do pyinstaller main.spec --onefile and it would make a single executable file in the same directory!!!

Don't use UPX with Pyinstaller in this case, as it resulted in some random DLL errors in my case, which got fixed after I repacked without using UPX

Hunter
  • 188
  • 2
  • 11
  • 1
    `pyinstaller main.spec --onefile` is invalid. A spec file is either a onedir spec file or a onefile spec file - adding flags won't change that. Older versions of PyInstaller would accept that command but ignore the `--onefile` flag and produce a onedir build anyway. Now PyInstaller raises an error. – pullmyteeth Dec 25 '22 at 17:18