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

Python 使用PyInstaller检查问题;可以获取类的源,但不能获取函数,python,pyinstaller,inspect,Python,Pyinstaller,Inspect,我在这里的第一篇帖子,请耐心等待 我正在使用PyInstaller捆绑tkinter应用程序,在使用inspect库时遇到了一个问题。基本上,我可以调用我编写的类的源代码,但不能调用函数。我制作了这个人为的案例来证明: 我的文件夹结构如下: experiment/ ---experiment.py ---init.py ---resources/ /---resources.py /---init.py ---loader/ /---loader.py /---i

我在这里的第一篇帖子,请耐心等待

我正在使用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)    
然后
experience.py
loader.py
加载打印函数并调用它:

from loader.loader import testfunc

testfunc()
我可以在Python控制台中运行
experience.py
,并获得预期的输出(源代码为
foo
Bar

然后,我使用PyInstaller从
experience.py
(仅添加PyInstaller的虚拟环境)创建一个可执行文件。
spec
文件未被触及(我可以共享它),但我确实将
loader
resources
目录复制/粘贴到
dist/experience
,以便可执行文件可以找到它们。如果从命令提示符下运行
experience.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
  • Windows 10 64位
我所尝试的:

  • 大量切换导入方法(例如显式导入 函数名,使用导入*,仅导入模块,使用 模块(功能)
  • 乱搞
    .spec
    文件,比如将我的模块添加到
    分析的
    参数中。
    正如htgoebel所评论的,我已经看到了,并且我已经尝试将我的模块添加到
    a.pure
    ,但是我不确定我是否做对了,而且这似乎不是根本问题,因为类
    Bar
    的代码可以从同一个文件中找到
  • 我使用了最新的PyInstaller版本(3.6)和
    https://github.com/pyinstaller/pyinstaller/archive/develop.zip

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

在做了更多的探索之后,我找到了一个解决方案——尽管感觉不太理想

loader
中,我添加了语句来打印
foo
Bar
的模块和文件(不需要“
mymod
”):

再次创建程序时,
experience.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
因此,Python似乎可以识别它们来自同一个模块,但不是实际的文件;对于
Bar
,有一个指向
.pyc
文件的绝对路径,而对于
foo
,只有指向
.py
文件的相对路径

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

from importlib import import_module
mymod = import_module('resources.resources')
此修复了
experience.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.')
这在应用程序中起作用-看起来过于冗长和混乱,但最终它不会明显影响应用程序的功能,无论是作为
.exe
还是作为Python脚本

如果有人想大喊一声,我还是很想知道这里发生了什么。如果没有,希望这能帮助别人。
experiment\resources\resources.py
<module 'resources.resources' from 'C:\\Users\...\dist\\experiment\\resources\\resources.pyc'>
Traceback (most recent call last):
... #same error as before
from importlib import import_module
mymod = import_module('resources.resources')
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.')
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