写点什么

将整 python 项目的代码中的 py 文件转为 so

作者:关耳
  • 2023-08-20
    湖北
  • 本文字数:4688 字

    阅读完需:约 15 分钟

将整python项目的代码中的py文件转为so

[重要提醒]

  1. 执行打包的动作,不要在源文件上进行,把代码复制到打包环境后再去打包分发,否则会因为清理代码的动作,造成代码丢失

  2. 代码清理会清理'.c', '.py', '.pyc', '.o'文件,所以如果项目中本身就有引用这些文件的话,则需要进行过滤忽略


这里选用一个 django 项目:将 django 整个项目的代码全部转为 so 文件后,再发布

1. 新建 Django 项目

新建一个 django 项目:代码目录如下


.├── account    # 新建的一个app│   ├── __init__.py│   ├── admin.py│   ├── apps.py│   ├── migrations│   │   └── __init__.py│   ├── models.py│   ├── tests.py│   └── views.py├── jiami│   ├── __init__.py│   ├── __pycache__│   │   ├── __init__.cpython-310.pyc│   │   └── settings.cpython-310.pyc│   ├── asgi.py│   ├── settings.py│   ├── urls.py│   └── wsgi.py└── manage.py
复制代码


现在把这个项目的所有 py 文件全部转成 so,然后启动执行。


先回忆下单个文件的做法:新建一个setup.pyguaner.py的同级目录下


from distutils.core import setupfrom Cython.Build import cythonizesetup(ext_modules=cythonize(["guaner.py"]))
复制代码


示例:创建完成后目录结构如下


.├── __init__.py├── guaner.py└── setup.py0 directories, 3 files
复制代码


然后执行以下命令进行转 so


python setup.py build_ext
复制代码


生成的 so 文件如下


jiami├── build│   ├── lib.macosx-10.9-x86_64-3.10│   │   └── guaner.cpython-310-darwin.so│   └── temp.macosx-10.9-x86_64-3.10│       └── guaner.o├── guaner.c├── guaner.py└── setup.py
复制代码

2. 梳理要实现的效果

同理我们要实现整个项目的 py 文件的so化后然后可以直接部署运行,需要做到以下几个要求:


  1. 不能改变文件的目录结构

  2. py文件不能存在,否则不能起到加密的用途

  3. 不能产生非so的文件存在,如新生成的.c文件需要删除

  4. 特殊文件不能转so,比如djangomanage.py文件就不能转so,否则python manage.py xx命令无法执行

3. 实现

3.1 先实现转 so 的动作

setup(ext_modules=cythonize(["guaner.py"]))cythonize需要提供所有的 py 文件,那么要做的第一步就是获取所有的 py 文件

3.1.1 获取项目中的所有 py 文件

def get_all_py_files(dir_root):    """获取所有的py文件,要编译成so的文件
:params dir_root: 项目根目录地址 """ res_py_file_list = [] for root, dirs, files in os.walk(dir_root): for file in files: if os.path.splitext(file)[1] != '.py': continue if 'manage.py' in file: # manage.py文件需要排除在外,不能转so continue _path = f'{root}/{file}' res_py_file_list.append(_path) return res_py_file_list
复制代码

3.1.2 转 so

将生成的 so 文件全部生成在其 py 文件原来的目录下



def py_to_so(): project_dir = './' # 项目相对路径 res_py_file_list = get_all_py_files(project_dir) build_dir = './' build_tmp_dir = './' # 为了让so文件直接在源目录结构下生成,所以指定buid和dist都是当前目录 setup(ext_modules=cythonize(res_py_file_list), script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir])
复制代码


执行转 so,看看情况**完整的文件内容如下:jiami.py文件



import os
from distutils.core import setupfrom Cython.Build import cythonize

def get_all_py_files(dir_root): """获取所有的py文件,要编译成so的文件
:params dir_root: 项目根目录地址 """ res_py_file_list = [] for root, dirs, files in os.walk(dir_root): for file in files: if os.path.splitext(file)[1] != '.py': continue if 'manage.py' in file: # manage.py文件需要排除在外,不能转so continue _path = f'{root}/{file}' res_py_file_list.append(_path) return res_py_file_list

def py_to_so(): project_dir = './' res_py_file_list = get_all_py_files(project_dir) build_dir = './' build_tmp_dir = './'
setup(ext_modules=cythonize(res_py_file_list), script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir])


if __name__ == '__main__': py_to_so()
复制代码


执行python jiami.py开始转so,执行结束后,结果如下:


.├── account│   ├── __init__.c│   ├── __init__.cpython-310-darwin.so│   ├── __init__.o│   ├── __init__.py│   ├── admin.c│   ├── admin.cpython-310-darwin.so│   ├── admin.o│   ├── admin.py│   ├── apps.c│   ├── apps.cpython-310-darwin.so│   ├── apps.o│   ├── apps.py│   ├── migrations│   │   ├── __init__.c│   │   ├── __init__.cpython-310-darwin.so│   │   ├── __init__.o│   │   └── __init__.py│   ├── models.c│   ├── models.cpython-310-darwin.so│   ├── models.o│   ├── models.py│   ├── tests.c│   ├── tests.cpython-310-darwin.so│   ├── tests.o│   ├── tests.py│   ├── views.c│   ├── views.cpython-310-darwin.so│   ├── views.o│   └── views.py├── jiami│   ├── __init__.c│   ├── __init__.cpython-310-darwin.so│   ├── __init__.o│   ├── __init__.py│   ├── __pycache__│   │   ├── __init__.cpython-310.pyc│   │   └── settings.cpython-310.pyc│   ├── asgi.c│   ├── asgi.cpython-310-darwin.so│   ├── asgi.o│   ├── asgi.py│   ├── settings.c│   ├── settings.cpython-310-darwin.so│   ├── settings.o│   ├── settings.py│   ├── urls.c│   ├── urls.cpython-310-darwin.so│   ├── urls.o│   ├── urls.py│   ├── wsgi.c│   ├── wsgi.cpython-310-darwin.so│   ├── wsgi.o│   └── wsgi.py├── jiami.c├── jiami.cpython-310-darwin.so├── jiami.o├── jiami.py└── manage.py
复制代码


可以看到Cython生成了一些.c.o文件,以及我们想要的.so文件,当前我们满足了第一个要求**生成的so文件目录结构没有变化,下一步我们需要清理中间的.c.o.py文件。清理办法:遍历目录删除.c.o.py文件,剩下的都是想要的,只留下了so文件,以及可能存在的一些项目配置文件

3.1.3 清理文件目录

注意清理文件的时候需要注意,不能把没有转 so 的 py 文件也给清理了,否则可能会影响业务运行



def clean_c_file(dir_root): for root, dirs, files in os.walk(dir_root): for file in files: # 这是执行的jiami.py文件,不需要转so,也不需要删除 if 'jiami.py' in file: continue # 这是执行的django的命令入口文件,不需要转so,也不需要删除,否则会影响业务使用 if 'manage.py' in file: continue if os.path.splitext(file)[1] in ['.c', '.py', '.pyc', '.o']: _path = f'{root}/{file}' os.remove(_path)
复制代码


然后我们再执行一次看看,先看完整的jiami.py文件内容


这里有个危险的动作,清除掉多余的文件是直接删除了,所以执行前不要在源项目代码执行,而是在打包环境秩序,否则有可能会导致文件丢失


import os
from distutils.core import setupfrom Cython.Build import cythonize

def get_all_py_files(dir_root): """获取所有的py文件,要编译成so的文件
:params dir_root: 项目根目录地址 """ res_py_file_list = [] for root, dirs, files in os.walk(dir_root): for file in files: if os.path.splitext(file)[1] != '.py': continue if 'manage.py' in file: # manage.py文件需要排除在外,不能转so continue _path = f'{root}/{file}' res_py_file_list.append(_path) return res_py_file_list

def clean_c_file(dir_root): for root, dirs, files in os.walk(dir_root): for file in files: # 这是执行的jiami.py文件,不需要转so,也不需要删除 if 'jiami.py' in file: continue # 这是执行的django的命令入口文件,不需要转so,也不需要删除,否则会影响业务使用 if 'manage.py' in file: continue if os.path.splitext(file)[1] in ['.c', '.py', '.pyc', '.o']: _path = f'{root}/{file}' os.remove(_path)

def py_to_so(): project_dir = './' res_py_file_list = get_all_py_files(project_dir) build_dir = './' build_tmp_dir = './'
setup(ext_modules=cythonize(res_py_file_list), script_args=["build_ext", "-b", build_dir, "-t", build_tmp_dir]) # 清除文件 clean_c_file(project_dir)


if __name__ == '__main__': py_to_so()
复制代码


执行python jiami.py开始转so,执行结束后,结果如下:


.├── account│   ├── __init__.cpython-310-darwin.so│   ├── admin.cpython-310-darwin.so│   ├── apps.cpython-310-darwin.so│   ├── migrations│   │   └── __init__.cpython-310-darwin.so│   ├── models.cpython-310-darwin.so│   ├── tests.cpython-310-darwin.so│   └── views.cpython-310-darwin.so├── jiami│   ├── __init__.cpython-310-darwin.so│   ├── __pycache__│   ├── asgi.cpython-310-darwin.so│   ├── settings.cpython-310-darwin.so│   ├── urls.cpython-310-darwin.so│   └── wsgi.cpython-310-darwin.so├── jiami.cpython-310-darwin.so├── jiami.py└── manage.py
复制代码


不错,不错,是我想要的,除了 so 文件和我要留的外,项目代码很干净了,启动下看看,OK


Watching for file changes with StatReloaderPerforming system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.Run 'python manage.py migrate' to apply them.August 19, 2023 - 10:16:20Django version 4.2.1, using settings 'jiami.settings'Starting development server at http://127.0.0.1:8000/Quit the server with CONTROL-C.
复制代码

4. 重点提醒

[重要提醒]

  1. 执行打包的动作,不要在源文件上进行,把代码复制到打包环境后再去打包分发,否则会因为清理代码的动作,造成代码丢失

  2. 代码清理会清理'.c', '.py', '.pyc', '.o'文件,所以如果项目中本身就有引用这些文件的话,则需要进行过滤忽略

发布于: 2023-08-20阅读数: 8
用户头像

关耳

关注

还未添加个人签名 2019-06-18 加入

还未添加个人简介

评论

发布
暂无评论
将整python项目的代码中的py文件转为so_Python_关耳_InfoQ写作社区