转载请注明出处!!!

准备

项目需要,是在windows7上操作。python3.7,针对python项目,而非单个的python程序。
思路:先将py代码转成c代码,然后编译成pyd(window上是pyd,linux上是so)文件
安装cython:pip install cython

编译

直接上源码

import sys, os, shutil, time
from distutils.core import setup
from Cython.Build import cythonize

start_time = time.time()
curr_dir = os.path.abspath('.')
parent_path = sys.argv[1] if len(sys.argv) > 1 else ""
setup_file = __file__.replace('/', '\\')
build_dir = "build"
build_tmp_dir = build_dir + "/temp"

s = "# cython: language_level=3"


def get_py(base_path=os.path.abspath('.'), parent_path='', excepts=()):
    """
    获取py文件的路径
    :param base_path: 根路径
    :param parent_path: 父路径
    :param excepts: 排除文件
    :return: py文件的迭代器
    """
    full_path = os.path.join(base_path, parent_path, name)
    for filename in os.listdir(full_path):
        full_filename = os.path.join(full_path, filename)
        if os.path.isdir(full_filename) and filename != build_dir and not filename.startswith('.'):
            for f in get_py(base_path, os.path.join(parent_path, name), filename, excepts, copyOther, delC):
                yield f
        elif os.path.isfile(full_filename):
            ext = os.path.splitext(filename)[1]
            if ext == ".c":
                if delC and os.stat(full_filename).st_mtime > start_time:
                    os.remove(full_filename)
            elif full_filename not in excepts and os.path.splitext(filename)[1] not in ('.pyc', '.pyx'):
                if os.path.splitext(filename)[1] in ('.py', '.pyx') and not filename.startswith('__'):
                    path = os.path.join(parent_path, name, filename)
                    yield path
        else:
            pass


def pack_pyd():
    # 获取py列表
    module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,)))
    try:
        setup(
            ext_modules=cythonize(module_list),
            script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir],
        )
    except Exception as ex:
        print("error! ", str(ex))
    else:
        module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,), copyOther=True))

    module_list = list(get_py(base_path=curr_dir, parent_path=parent_path, excepts=(setup_file,), delC=True))
    if os.path.exists(build_tmp_dir):
        shutil.rmtree(build_tmp_dir)

    print("complate! time:", time.time() - start_time, 's')

def delete_c(path='.', excepts=(setup_file,)):
	'''
    删除编译过程中生成的.c文件
    :param path:
    :param excepts:
    :return:
    '''
    dirs = os.listdir(path)
    for dir in dirs:
        new_dir = os.path.join(path, dir)
        if os.path.isfile(new_dir):
            ext = os.path.splitext(new_dir)[1]
            if ext == '.c':
                os.remove(new_dir)
        elif os.path.isdir(new_dir):
            delete_c(new_dir)


if __name__ == '__main__':
    try:
        pack_pyd()
    except Exception as e:
        print(str(e))
    finally:
        delete_c()

运行

在要打包的目录的同级目录下,创建setup.py将上述代码复制到setup.py, 直接运行setup.py即可。
执行成功后,会出现一个build文件夹,其中包含有和要打包的项目结构一样的项目,和原项目不同的是,其中的py文件(除__init__外)均被打包成pyd文件,如此源码就被保护起来了。
在这里插入图片描述

注意

windows上使用cython打包有很多坑

  1. 出现“Unable to find vcvarsall.bat”错误
    方法:
    python3只能通过安装对应版本的vc++来解决
  2. FutureWarning: Cython directive ‘language_level’ not set, using 2 for now (Py2)
    方法:
    在要打包的每个py文件中的第一行,添加如下语句,# cython: language_level=3
  3. 编译后执行需要相同的python版本和编码

参考:
https://blog.csdn.net/qq_20154743/article/details/77891572
http://yshblog.com/blog/117
https://zhuanlan.zhihu.com/p/65906685

Logo

更多推荐