首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >使用PyInstaller检查时遇到的问题;可以得到类的来源,但不能得到函数

使用PyInstaller检查时遇到的问题;可以得到类的来源,但不能得到函数
EN

Stack Overflow用户
提问于 2020-04-23 04:22:05
回答 1查看 921关注 0票数 5

我的第一篇文章,请原谅我.

我正在使用PyInstaller捆绑一个tkinter应用程序,我在使用inspect库时遇到了一个问题。基本上,我可以调用我编写的类的源代码,但不能调用函数。我做了这个人为的例子来证明:

我有这样的文件夹结构:

代码语言:javascript
运行
复制
experiment/
---experiment.py
---init.py
---resources/
    /---resources.py
    /---init.py
---loader/
    /---loader.py
    /---init.py

resources.py定义了一个函数和一个类:

代码语言:javascript
运行
复制
def foo(a):
    print(str(a) + ' is what you entered.')

class Bar:
    def __init__(self, b):
        self.b = b

loader.py导入这个函数和那个类,并定义一个函数来打印它们的源代码:

代码语言:javascript
运行
复制
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.pyloader.py加载该打印函数并调用它:

代码语言:javascript
运行
复制
from loader.loader import testfunc

testfunc()

我可以在Python中运行experiment.py并获得预期的输出( fooBar的源代码)。

然后,我使用PyInstaller从experiment.py创建一个可执行文件(在一个虚拟环境中添加了PyInstaller )。spec文件未被触摸(我可以共享它),但我确实将loaderresources目录复制/粘贴到dist/experiment,以便可执行文件能够找到它们。如果我从命令提示符运行experiment.exe,则得到以下输出:

代码语言:javascript
运行
复制
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是一个格式化情节代码的模块,experimenttkinter应用程序。与本例一样,inspect从IDE中运行良好,但在生成exe后中断。

关于我的设置的更多信息:

  • virtualenv是使用Anaconda提示符创建的;我使用conda create创建了一个新环境,然后pip安装了PyInstaller。
  • Python为3.7.7
  • 环境中的一切,基本上就是它带来的东西:
代码语言:javascript
运行
复制
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
  • Windows 10 64位

我试过的是:

  • 大量切换导入方法(例如按名称显式导入函数、使用import *、只导入模块和使用module.function)
  • 处理.spec文件,比如将模块添加到Analysisdatas参数中。我见过本期,我尝试将我的模块添加到a.pure中,就像htgoebel评论的那样,但是我不确定我是否做得对,这似乎不是根本问题,因为类Bar的代码可以从同一个文件中找到
  • 我使用了最新的PyInstaller版本(3.6)和来自https://github.com/pyinstaller/pyinstaller/archive/develop.zip的当前开发人员版本

总的来说,最奇怪的部分似乎是--类源代码可以找到,但是函数不能,当它们位于同一个文件中时--但也许PyInstaller在某种程度上使它变得更复杂了?如果你有任何建议或者想让我尝试其他的东西,请告诉我。很高兴提供我能提供的任何其他信息/测试。干杯!

EN

回答 1

Stack Overflow用户

发布于 2020-04-24 02:23:30

我已经找到了一个解决办法后,做了更多的戳-感觉不太理想。

对于loader,我添加了语句来打印fooBar的模块和文件( "mymod“是不必要的):

代码语言:javascript
运行
复制
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的输出指向fooBar之间的差异。mymod.Bar的打印语句如下:

代码语言:javascript
运行
复制
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

代码语言:javascript
运行
复制
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似乎可以识别出它们来自同一个模块,而不是实际的文件;有一个指向BarBar文件的绝对路径,而对于foo只有一个相对路径指向.py文件。

因此,我试图更改导入/变得更明确,并开始使用importlib

代码语言:javascript
运行
复制
from importlib import import_module
mymod = import_module('resources.resources')

这个固定的experiment.exe给出了正确的输出:

代码语言:javascript
运行
复制
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上下文中不起作用--我仍然无法获得函数源代码,但类继续工作。最后,我不得不跟着这个例子做:

代码语言:javascript
运行
复制
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。

如果有人想大喊大叫的话,我还是很想知道这是怎么回事。希望这能帮到别人。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/61379330

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档