What I learned from python app distribution
Tags: python, production
Published: 2020-03-07 14:44:37

### Problem 
Recently, we need to distribute our python Flask application in docker container. Initially, I was using [hatch](https://github.com/ofek/hatch) to build/test our application, by default, [hatch build](https://github.com/ofek/hatch/blob/master/COMMANDS.rst#build) will produce a source distribution and a wheel. But we do not want to distribute our source code in our docker container, how can we protect our intellectual property?

### Possible Options and Challenges
Here are a few options that discussed in python wiki - [How do you protect Python source code?](https://wiki.python.org/moin/Asking%20for%20Help/How%20do%20you%20protect%20Python%20source%20code%3F), in summary, you have the following options
- Use compiled bytecode 
  * The generated **.pyc** files are not portable between different versions of Python 
  * You can still decompyle the **pyc** file to reverse engineering.
- Executable creators (or Installers)
  * Option like using [PyInstaller](http://www.pyinstaller.org/)
  * But [PyInstaller](http://www.pyinstaller.org/) currently not support Python 3.8 yet
- Software as a Service 
  * Deploy the way like Google did.
  * deployment only on locked down VMs (client only has API acces, no console access to host).
- Python Source Code Obfuscators 
  * Use the tool like [Pyarmor](https://wiki.python.org/moin/Pyarmor)
  * But I am worried about the performance
- Use of modified docker container that has no shell environment or other mechanisms to prevent access to the files within docker container.
  * no shell in docker looks so challenge since we need bash to execute the program, allocate job tasks, etc.
  * lock file permissions may be possible

### Solution
- Besides the source code, when you build a distribution package, you can use [wheel](https://packaging.python.org/glossary/#term-wheel) or [egg](https://packaging.python.org/glossary/#term-egg) format, here is a good summary of [Wheel vs Egg](https://packaging.python.org/discussions/wheel-vs-egg/)
- When you use `$ python setup.py bdist_egg --exclude-source-files` can help you remove source files in the distributed package, but egg file you need to use [easy_install](https://setuptools.readthedocs.io/en/latest/easy_install.html) to install, which is deprecated in setuptools now, the recommended way is using **pip** which need to install wheel file.
- thanks to stackoverflow answer - [how to exclude source code from beist_wheel python](https://stackoverflow.com/questions/47174664/how-to-exclude-source-code-from-bdist-wheel-python) 
```
python setup.py bdist_egg --exclude-source-files
wheel convert dist/package-*.egg -d dist
```
which will generate `dist/package-*-py38-none-any.whl` if you did not define platform details.
- finally, you can use `pip install package-*-py38-none-any.whl` to install the package which only contains the **pyc** files
- you can use `pip show <package>` to check where your package get installed and also go though all folders to double confirm no source code `*.py` files exist.


### References
- [Easy Install in setuptools is deprecated](https://setuptools.readthedocs.io/en/latest/easy_install.html)
- [wheel convert](https://wheel.readthedocs.io/en/stable/reference/wheel_convert.html)
- [Creating Built Distributions](https://docs.python.org/3/distutils/builtdist.html)
- [Installing Packages](https://packaging.python.org/tutorials/installing-packages/)
- [pip install](https://pip.pypa.io/en/stable/reference/pip_install/)
- [PEAK EasyInstall](http://peak.telecommunity.com/DevCenter/EasyInstall)
- [PEAK PythonEggs](http://peak.telecommunity.com/DevCenter/PythonEggs)