使用PyInstaller检查的问题;可以获得类的源码,但不能获得函数。

问题描述 投票:0回答:1

我第一次在这里发帖,请耐心等待......。

我正在用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 并得到预期的输出 (源代码为 fooBar).

然后,我使用PyInstaller创建一个可执行文件,从 experiment.py (在虚拟环境中,只添加了PyInstaller)。 该 spec 文件未被修改(我可以分享它),但我复制粘贴了 loaderresources 目录到 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 是一个用于格式化绘图代码的模块,而 experimenttkinter 申请。 如本案中的 inspect 在IDE中可以正常工作,但在构建exe后就坏了。

更多关于我的设置。

  • virtualenv是用Anaconda Prompt创建的;我使用了 conda create 营造一个新的环境,然后 pip 安装了PyInstaller
  • Python是3.7.7
  • 环境中的一切,基本都是自带的。
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 64bit

我试过的。

  • 我尝试了很多不同的导入方法(例如:通过名称明确导入函数,使用导入*,只导入模块和使用module.function)
  • 瞎搞 .spec 文件,比如将我的模块添加到 datas 的论点 Analysis...我已经看到了 本期我试着将我的模块添加至 a.pure 正如htgoebel所评论的那样,但我不确定我这样做是否正确,而且看起来也许不是根本问题,因为类的代码是由 Bar 可以从同一个文件中找到
  • 我使用了最新的PyInstaller版本(3.6)和当前的开发者版本。https://github.com/pyinstaller/pyinstaller/archive/develop.zip

总的来说,最奇怪的部分似乎是 可以找到类的源代码,但无法找到函数。当它们在同一个文件中时,可能PyInstaller让它变得更复杂,我不明白? 如果你有任何建议或者想让我尝试其他方法,请告诉我。 很高兴提供任何其他信息测试,我可以。 干杯!我是第一次在这里发帖,忍耐一下。

python pyinstaller inspect
1个回答
1
投票

我在做了一些小动作之后,有了一个解决方案--虽然感觉不是很理想。

loader 我添加了打印模块和文件的语句。fooBar (该"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 指的是以下两个方面的差异 fooBar. 打印声明 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脚本。

还是很想知道这到底是怎么回事,如果有人觉得想给个说法。 希望这能帮助到别人,如果没有的话。🤷

© www.soinside.com 2019 - 2024. All rights reserved.