我的第一篇文章,请原谅我.
我正在使用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()
我可以在Python中运行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
的代码,但是foo
的代码无法找到!注意,第9行是检查foo
的地方。
真正的上下文是一个图形化程序,在这里,我希望返回用于绘制一个图的代码,以防用户想要进行调整。所以foo
实际上是很多matplotlib
代码,loader
是一个格式化情节代码的模块,experiment
是tkinter
应用程序。与本例一样,inspect
从IDE中运行良好,但在生成exe后中断。
关于我的设置的更多信息:
conda create
创建了一个新环境,然后pip
安装了PyInstaller。altgraph 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
文件,比如将模块添加到Analysis
的datas
参数中。我见过本期,我尝试将我的模块添加到a.pure
中,就像htgoebel评论的那样,但是我不确定我是否做得对,这似乎不是根本问题,因为类Bar
的代码可以从同一个文件中找到https://github.com/pyinstaller/pyinstaller/archive/develop.zip
的当前开发人员版本总的来说,最奇怪的部分似乎是--类源代码可以找到,但是函数不能,当它们位于同一个文件中时--但也许PyInstaller在某种程度上使它变得更复杂了?如果你有任何建议或者想让我尝试其他的东西,请告诉我。很高兴提供我能提供的任何其他信息/测试。干杯!
发布于 2020-04-24 02:23:30
我已经找到了一个解决办法后,做了更多的戳-感觉不太理想。
对于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似乎可以识别出它们来自同一个模块,而不是实际的文件;有一个指向Bar
的Bar
文件的绝对路径,而对于foo
只有一个相对路径指向.py
文件。
因此,我试图更改导入/变得更明确,并开始使用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。
如果有人想大喊大叫的话,我还是很想知道这是怎么回事。希望这能帮到别人。
https://stackoverflow.com/questions/61379330
复制相似问题