使用 Cython 加密 Python 项目
最近公司需要将 python 代码部署到端上查了各种加密方法说到底 python 其实是不建议加密部署的,像什么生成.pyc其实都是很容易反编译直接运行的,因为它是解释型语言。不像 C 或者 java 可以编译后生成机器码直接部署。还有看到把项目打包成.exe文件,在 windows 上运行,由于我们使用 Linux 平台没有尝试,最后选择了使用Cython这个库来加密(编译成二进制)。 Cython其实就是把py 代码编译成 C或者 C++代码来执行,在Linux 上会生成.so二进制文件,Windows下为.pyd,所以还有一个作用是加速代码的执行效率。但还有一些限制如项目中不能删除__init__.py否者包导入会失败。详细可参考官方文档,Cython 还在持续开发中支持 Python3,下面也用Python3演示。 先来做一些准备工作定义编译后的文件夹build和一些部署不需要的文件和文件夹,将待编译的.py文件加入ext_modules列表 cur_dir = os.path.abspath(os.path.dirname(__file__)) setup_file = os.path.split(__file__)[1] build_dir = os.path.join(cur_dir, 'build') build_tmp_dir = os.path.join(build_dir, "temp") # define exclude dirs, these dirs will be deleted exclude_dirs = ['.git', '__pycache__', 'test', 'logs', 'venv', 'tests'] # defile exclude files, these files will be deleted exclude_files = ['*.md', '.gitignore', '.python-version', 'requirements.txt', '*.pyc', '*.c'] # these `.py` files will be retained and don't compile to `.so` ignore_py_files = ['config.py'] ext_modules = [] # get all build files for path, dirs, files in os.walk(cur_dir, topdown=True): dirs[:] = [d for d in dirs if d not in exclude_dirs] # touch a new file when __init__.py not exists for _dir in dirs: init_file = os.path.join(path, _dir, '__init__.py') if not os.path.isfile(init_file): print('WARNING: create new empty [{}] file.'.format(init_file)) with open(init_file, 'a') as f: pass # create target folder if not os.path.isdir(build_dir): os.mkdir(build_dir) # make empty dirs for dir_name in dirs: dir = os.path.join(path, dir_name) target_dir = dir.replace(cur_dir, build_dir) os.mkdir(target_dir) for file_name in files: file = os.path.join(path, file_name) if os.path.splitext(file)[1] == '.py': if file_name in ignore_py_files: # don't compile to .so if file_name not in exclude_files: shutil.copy(file, path.replace(cur_dir, build_dir)) elif file_name in exclude_files: # remove it pass else: # add to compile if file_name == '__init__.py': # copy __init__.py resolve package cannot be imported shutil.copy(file, path.replace(cur_dir, build_dir)) if file_name != setup_file: ext_modules.append(file) else: _exclude = False for pattern in exclude_files: if fnmatch.fnmatch(file_name, pattern): _exclude = True if not _exclude: shutil.copy(file, path.replace(cur_dir, build_dir)) 我们需要把原来的每个文件夹下__init__.py拷贝一份,不然项目中相对导入这些会失效。然后把ext_modules列表传给cythonize生成distutils Extension objects再传给setup函数。 ...