Pyinstaller打包单个bundle时封装额外的资源文件

Pyinstaller简易使用说明:

安装:

pip install pyinstaller

在待打包目录下

新建一个批处理specgen.bat,内容为

pyi-makespec.exe Name.py -F -w –icon=fav.ico

新建一个批处理exegen.bat,内容为

pyinstaller.exe Name.spec

执行specgen.bat生成spec文件(若有需要,对其进行修改)

执行exegen.bat生成可执行文件,结果位于dist目录下

正文:

在编程时往往会引用一些额外文件,如图片,音乐等。

若是用Pyinstaller打包为文件夹,那么这些文件直接复制过去就行。

而在打包为单文件exe时,通常这些文件是不希望用户直接能够看见的,因此有了这篇文档。

一.添加附件的方法。

事实上,官方指南给出了详细的方法,没有例子且有些生涩。我只做具体操作的转述,机理有心情再附上。

概述:

将附件打包至exe非常容易,但是源程序要做必要的修改,这些文件不再能简单使用相对路径。

详解:

Step1:添加basedir在待打包的主程序头部

if getattr(sys, 'frozen', False): # we are running in a |PyInstaller| bundle basedir = sys._MEIPASS else: # we are running in a normal Python environment basedir = os.path.dirname(__file__)

Step2:待打包附件全部添加至某文件夹,如myimages

程序内所有引用均应当这么写:basedir+’路径’

open(basedir+’/myimages/fav.ico’)

Step3:修改打包参数

  1. 生成一个spec文件
在spec文件中添加

extra_tree = Tree(‘./myimages’, prefix = ‘myimages’)
3. 在a.scripts等a.开头文件所在的部分后添加extra_tree
4. 直接用修改的spec打包即可。

例如:

extra_tree = Tree('./myimages', prefix = 'myimages') exe = EXE(pyz, a.scripts, a.binaries, a.zipfiles, a.datas, extra_tree, name='test.exe', debug=False, strip=None, upx=True, console=False , icon='fav.ico')

Enjoy it!

二.运行机理。

在打包为单文件exe时,先分析所需运行文件,然后把这些文件利用Tree函数形成一个文件目录树,运行时将这些文件 释放至C:UsersUSERNAMEAppDataLocal下的一个临时文件夹XXXXX内。程序自身引用此目录的绝对路径来获得所需的文件。此时,对于程序,他所见的目录仍然是exe所存储的目录。

也就是说,程序里的open(‘sample.txt’)打开的是程序所在目录下的sample.txt,而非临时文件夹下的。

extra_tree = Tree('./myimages', prefix = 'myimages')

对照打包函数,pyinstaller对额外资源的打包,实质也是利用Tree函数生成一个文件目录树,也将这些文件压进exe,并释放到临时目录中。

其中:

第一个参数’.images’是待打包的目录地址,将会将该目录下所有文件添加至文件树。

第二个参数prefix = ‘myimages’则意为:这些文件在运行时将被解压至临时文件夹中名为’myimages’的文件夹下。若没有这个参数,则这些文件会被释放到临时文件夹中。

于是乎,basedir的作用就体现了,他是官方文档中给出的获得基础目录的函数,其作用及原理已经注释的很清楚。

附注:

文件树(官方文档中称为Table of Class):类似于python的列表变量。Tree函数将所有文件均添加为’DATA’型数据

其结构为:[(‘解压后地址’, ‘文件地址’, ‘类型参数’)]

例如

[('README', '/my/project/readme', 'DATA')]

Read more

Coroutine

从一般概念上说, 协程是特殊的函数调用: 被调用的函数可以在可控的位置被中断,然后在下一次调用时,继续从上次中断的位置继续执行。 本文主要通过Python的协程来介绍协程, 这是我唯一熟悉的一种协程实现. Classic Coroutine 下面的python代码很好的说明了协程的核心功能 def co_routine(): recv0 = yield 996 # hangs here after first coro.send assert recv0 == "Second" yield 711 # hangs here after second coro.send return def main(): coro = co_routine() # Create a new coroutine object value = coro.send(None)

By Edimetia3D

GDB with Python

这篇文章的主要应用场景是调试Python的C/C++ Extension 1. 同时使用pdb / gdb 进行调试. 通俗点说, 既可以break在 .py 文件中,也可以break在 .cc 文件中 2. 在gdb中不但可以获得常规的调试信息, 还可以获得python VM 的调试信息, 例如获得python的调用栈, 访问Python局部变量等. 这将会在调试exception时(如Segmentfalut)非常有用, 这种场景下, 定位 Python VM 正运行到哪一行代码往往可以提供一些直观的重要信息. 第一步: 编译源码以获得一些辅助数据. 我们并不真的需要使用从源码编译的Python, 但是一些调试相关的辅助文件需要从源码中获得, 包括 python-gdb.py及debug symbol等. 在 https://www.python.org/ftp/python/ 或 https://github.com/python/cpython

By Edimetia3D

Bazel Notes

这是一篇2019年左右的记录, 内容可能过时, 也不太全面 杂谈 Bazel是Google为Monorepo服务而开发的构建工具. 首先是巨大,当问题的规模变大,事情总是会变得更复杂. 而Google面对的"巨大Monorepo",应该是世间罕有的. 然后是Monorepo,这极大的影响了代码的组织风格.例如,你要写一个操作系统内核ProjectOS,还要写一个游戏ProjectGame.在传统的开发习惯中,这两个项目会组织到两个不同的Repo里,PorjectOS和ProjectGame之间无法直接相互引用,例如,你在ProjectOS里写了一个高级的数据结构,想要在Game里也使用,要么直接复制粘贴,要么是创建一个新的CommonRepo,把可公用的代码都放在Common里,然后两个项目各自引入Common作为依赖. 使用MonoRepo则不存在这个问题,Game可以直接依赖OS内的组件,按照Bazel的语法描述,就是在Game中可以直接使用@ProjectOS//path/to/package:AdvancedStruct.当然,你仍然可以选择重构一

By Edimetia3D

Unix related things

这是一篇2017年左右的记录, 仅用作分享 杂 * 在shell内能干的事,我们都可以比较简单地通过系统调用实现. * `称为反引号,^称为脱字符,常用来表示CTRL * windows的系统调用是不开放的,windows下只能直接使用windows.h里的windows API. * /dev目录下的设备是供用于程序直接使用的,主要由block,char,pipe,socket类型 * 并不是所有设备都能映射为这种形式 * /sys/device/目录称为sysfs,他下面存放了所有设备的信息.(不能直接从/dev获得任何设备信息) * udevadm info --query=all --name="/dev/sda1"可以用于查询/dev下某个设备对应的sysfs路径 权限系统 * 权限系统由两部分组成 * 文件属性:用于标注文件owner,所属组,以及权限的设定(默认只有owner和root可以修改权限设置) *

By Edimetia3D