Tom*_*Tom 6 python pyinstaller inspect
我的第一篇文章在这里,请耐心等待......
我正在使用 PyInstaller 捆绑 tkinter 应用程序,并且在使用该inspect库时遇到了问题。基本上,我可以调用我编写的类的源代码,但不能调用函数。我做了这个人为的案例来证明:
我有一个像这样的文件夹结构:
experiment/
---experiment.py
---init.py
---resources/
/---resources.py
/---init.py
---loader/
/---loader.py
/---init.py
Run Code Online (Sandbox Code Playgroud)
resources.py定义一个函数一个类:
def foo(a):
print(str(a) + ' is what you entered.')
class Bar:
def __init__(self, b):
self.b = b
Run Code Online (Sandbox Code Playgroud)
loader.py导入该函数和该类并定义一个函数来打印它们的源代码:
import inspect
from resources import resources
def testfunc():
source_Bar = inspect.getsource(resources.Bar)
print(source_Bar)
source_foo = inspect.getsource(resources.foo)
print(source_foo)
Run Code Online (Sandbox Code Playgroud)
并experiment.py从中加载打印函数loader.py并调用它:
from loader.loader import testfunc
testfunc()
Run Code Online (Sandbox Code Playgroud)
我可以experiment.py在 Python 控制台中运行并获得预期的输出(foo和的源代码Bar)。
然后,我使用 PyInstaller 创建可执行文件experiment.py(从仅添加 PyInstaller 的虚拟环境)。该spec文件未受影响(我可以共享它),但我确实将loader和resources目录复制/粘贴到dist/experiment,以便可执行文件可以找到它们。experiment.exe如果我从命令提示符运行,我会得到以下输出:
C:\Users\earne\Desktop\experiment\dist\experiment>experiment.exe
class Bar:
def __init__(self, b):
self.b = b
Traceback (most recent call last):
File "experiment\experiment.py", line 3, in <module>
File "experiment\loader\loader.py", line 9, in testfunc
File "inspect.py", line 973, in getsource
File "inspect.py", line 955, in getsourcelines
File "inspect.py", line 786, in findsource
OSError: could not get source code
[14188] Failed to execute script experiment
Run Code Online (Sandbox Code Playgroud)
因此找到并打印了 的代码,但是找不到 的Bar代码foo!请注意,第 9 行是foo检查的地方。
真正的上下文是一个绘图程序,我想返回用于绘制绘图的代码,以防用户想要进行调整。所以foo实际上是很多matplotlib代码,loader是格式化绘图代码的模块,experiment是tkinter应用程序。在本例中,inspect在 IDE 中工作正常,但在构建 exe 后就中断了。
有关我的设置的更多信息:
conda create新建一个环境,然后pip安装PyInstalleraltgraph 0.17
certifi 2020.4.5.1
future 0.18.2
pefile 2019.4.18
pip 20.0.2
pyinstaller 4.0.dev0+03d42a2a25
pywin32-ctypes 0.2.0
setuptools 46.1.3.post20200330
wheel 0.34.2
wincertstore 0.2
Run Code Online (Sandbox Code Playgroud)
我尝试过的:
.spec文件,例如将我的模块添加datas到Analysis. 我已经看到了这个问题,并且我尝试将我的模块添加到a.purehtgoebel 评论中,但我不确定我是否做对了,而且似乎可能不是根本问题,因为该类的代码Bar可以从同一个文件中找到https://github.com/pyinstaller/pyinstaller/archive/develop.zip总的来说,最奇怪的部分似乎是,当类源代码位于同一个文件中时,可以找到类源代码,但无法找到函数- 但也许 PyInstaller 正在以一种我不明白的方式使其变得更加复杂?如果您有任何建议或希望我尝试其他方法,请告诉我。很高兴提供我能提供的任何其他信息/测试。干杯!
经过一番摸索后,我得到了一个解决方案 - 但感觉并不理想。
我loader添加了语句来打印两者的模块和文件foo(Bar“ mymod”是不必要的):
import inspect
from resources import resources as mymod
def testfunc():
print(inspect.getfile(mymod.Bar))
print(inspect.getmodule(mymod.Bar))
source_Bar = inspect.getsource(mymod.Bar)
print(source_Bar)
print(inspect.getfile(mymod.foo))
print(inspect.getmodule(mymod.foo))
source_foo = inspect.getsource(mymod.foo)
print(source_foo)
Run Code Online (Sandbox Code Playgroud)
再次创建程序, 的输出指向和experiment.exe之间的差异。的打印语句是:fooBarmymod.Bar
C:\Users\...\dist\experiment\resources\resources.pyc
<module 'resources.resources' from 'C:\\Users\...\dist\\experiment\\resources\\resources.pyc'>
class Bar:
def __init__(self, b):
self.b = b
Run Code Online (Sandbox Code Playgroud)
和对于mymod.foo:
experiment\resources\resources.py
<module 'resources.resources' from 'C:\\Users\...\dist\\experiment\\resources\\resources.pyc'>
Traceback (most recent call last):
... #same error as before
Run Code Online (Sandbox Code Playgroud)
所以看起来Python可以识别它们来自同一个模块,但不能识别实际的文件;有一个指向.pyc文件的绝对路径Bar,而只有一个指向.py文件的相对路径foo。
所以我尝试更改导入/更加明确,并开始使用importlib:
from importlib import import_module
mymod = import_module('resources.resources')
Run Code Online (Sandbox Code Playgroud)
修复此问题experiment.exe以提供正确的输出:
C:\Users\...\dist\experiment\resources\resources.py
<module 'resources.resources' from 'C:\\Users\...\dist\\experiment\\resources\\resources.py'>
class Bar:
def __init__(self, b):
self.b = b
C:\Users\...\experiment\resources\resources.py
<module 'resources.resources' from 'C:\\Users\...\dist\\experiment\\resources\\resources.py'>
def foo(a):
print(str(a) + ' is what you entered.')
Run Code Online (Sandbox Code Playgroud)
因此,即使如此,我也不明白为什么需要更改导入。此外,此修复在我的 GUI 上下文中不起作用 - 我仍然无法获取函数源代码,但该类继续工作。我最终不得不遵循这个例子并执行以下操作:
import importlib.util
homedir = os.path.dirname(os.path.dirname(__file__)
resources_dir = os.path.join(homedir, 'resources/resources.py')
spec = importlib.util.spec_from_file_location("resources.resources", resources_dir)
mymod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(mymod)
#I do os stuff to get a relative path to resources from loader
Run Code Online (Sandbox Code Playgroud)
这在应用程序中有效 - 看起来过于冗长和混乱,但最终它不会明显影响应用程序的功能,无论是作为还是.exe作为 Python 脚本。
如果有人想大声喊叫,我仍然很想知道这里发生了什么。希望这对某人有帮助(如果没有的话)。