我第一次在这里发帖,请耐心等待......。
我正在用PyInstaller捆绑一个tkinter应用程序,当我使用了 inspect
库。 基本上,我可以调用我写的一个类的源代码,但不能调用一个函数。 我做了这个人为的案例来演示。
我的文件夹结构是这样的:
experiment/
---experiment.py
---init.py
---resources/
/---resources.py
/---init.py
---loader/
/---loader.py
/---init.py
resources.py
定义了一个函数和一个类
def foo(a):
print(str(a) + ' is what you entered.')
class Bar:
def __init__(self, b):
self.b = b
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)
然后... experiment.py
打印功能从 loader.py
并调用它。
from loader.loader import testfunc
testfunc()
我可以运行 experiment.py
并得到预期的输出 (源代码为 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
所以这个代码是 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
我试过的。
.spec
文件,比如将我的模块添加到 datas
的论点 Analysis
...我已经看到了 本期我试着将我的模块添加至 a.pure
正如htgoebel所评论的那样,但我不确定我这样做是否正确,而且看起来也许不是根本问题,因为类的代码是由 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)
再次创建程序,输出的 experiment.exe
指的是以下两个方面的差异 foo
和 Bar
. 打印声明 mymod.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
而对于 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
因此,Python似乎可以识别它们来自同一个模块,但不是实际的文件;有一个绝对路径指向了 .pyc
归档 Bar
的相对路径,而只有一个通往 .py
归档 foo
.
所以我试着把importingbe改得更明确,于是用了 importlib
:
from importlib import import_module
mymod = import_module('resources.resources')
此项修正 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.')
所以即使是这样,我也不明白为什么要改变导入。 此外,这个修复方法在我的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
而这在应用程序中是有效的 -- 看起来过于啰嗦和混乱,但最终它显然不会影响到应用程序的功能,无论是作为一个新的应用程序,还是作为一个新的应用程序。.exe
或作为一个Python脚本。
还是很想知道这到底是怎么回事,如果有人觉得想给个说法。 希望这能帮助到别人,如果没有的话。🤷