How do I protect Python code from being read by users?
Python, being a byte-code-compiled interpreted language, is very difficult to lock down. Even if you use a exe-packager like py2exe, the layout of the executable is well-known, and the Python byte-codes are well understood.
Usually in cases like this, you have to make a tradeoff. How important is it really to protect the code? Are there real secrets in there (such as a key for symmetric encryption of bank transfers), or are you just being paranoid? Choose the language that lets you develop the best product quickest, and be realistic about how valuable your novel ideas are.
If you decide you really need to enforce the license check securely, write it as a small C extension so that the license check code can be extra-hard (but not impossible!) to reverse engineer, and leave the bulk of your code in Python.
Hiding python source code to prevent people from seeing it.. The whole and proper way
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
We will make another folder, and name it
Distributable executable files
(You can name
it whatever u want)We will be adding all the Python files in the folder but change the extension of
all the files frompy
topyx
Then make another file, known as
setup.py
and add the following code to it (Note the extension of that file should bepy
and notpyx
)
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.
Now, open a command prompt window in the
same folder
and run this commandpython setup.py build_ext --inplace
and wait. This will output some C and PYD files.. These are your Python files, now compiled.Now, make another file named
main.py
(Note the extension of that file should bepy
and notpyx
) 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!!Now, install Pyinstaller with
pip install pyinstaller
and in the same folder, open a command prompt and runpyinstaller main.py
and wait.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.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')
- 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 folderdist\<name>\
and you will find all the files of your app here. If you want to launch the app, look for amain.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
How can you lock python code for users without license?
I don't know what sort of license you are using, but if it's in something saved in a text file, you can add an if statement to you code that checks where or not the file exists. Additionally you could add hashing to compare a digest of the official license to the file save on the user's computer.
Python does not have the facilities built-in to enforce copy protection.
How to protect Python source code, while making the file available for running?
The criteria you've posted are inconsistent.
Python is an interpreted language. The entity running the language (i.e. Python interpreter) is reading your code and executing it, line by line. If you wrap it up to send to someone, their Python interpreter must have read permissions on the file, whether it's source code or "compiled" Python (which is easily decompiled into equivalent source code).
If we take a wider interpretation of "send to someone", there may be a business solution that serves your needs. You would provide your functionality, rather than the code: deploy it as a service from some available server: your own, or rented space. To do this, you instead provide an interface to your functionality.
If this fulfills your needs, you now have your next research topic.
Related Topics
How to Iterate Over a List in Chunks
Replacements For Switch Statement in Python
What Exactly Do "U" and "R" String Prefixes Do, and What Are Raw String Literals
What Is the Purpose of the Single Underscore "_" Variable in Python
Importing Files from Different Folder
How to Convert String Representation of List to a List
How to Detect Collision in Pygame
"Is" Operator Behaves Unexpectedly With Integers
How to Avoid Having Class Data Shared Among Instances
Strange Result When Removing Item from a List While Iterating Over It
Limiting Floats to Two Decimal Points
Why Is the Output of My Function Printing Out "None"
How to Select a HTML Element No Matter What Frame It Is in in Selenium
What's the Easiest Way to Escape HTML in Python