【问题标题】:File "<string>" in python tracebackpython回溯中的文件“<string>”
【发布时间】:2015-01-27 09:41:36
【问题描述】:

我正在将一个巨大的 py 模块重构为包 - 为了不破坏现有代码,我将其内容移动到 package/__init__.py 模块 (Adding code to __init__.py) 并继续从那里拆分它。我注意到在我的回溯中我得到了:

Traceback (most recent call last):
      File "<string>", line 656, in DoItemMenu
      File "bash\balt.py", line 2109, in PopupMenu
        link.AppendToMenu(menu,parent,*args)
      File "bash\balt.py", line 2225, in AppendToMenu
        for link in self.links: link.AppendToMenu(subMenu,window,data)
    ...

File "&lt;string&gt;" 中的行对应于特定的package/__init__.py 模块。此外,PyCharm 的调试器会显示“框架不可用”行,并且不会进入 __init__.py 中的行。为什么?是否与导入模式有关?

代码由launcher class导入:

class UnicodeImporter(object):
    def find_module(self,fullname,path=None):
        if isinstance(fullname,unicode):
            fullname = fullname.replace(u'.',u'\\')
            exts = (u'.pyc',u'.pyo',u'.py')
        else:
            fullname = fullname.replace('.','\\')
            exts = ('.pyc','.pyo','.py')
        if os.path.exists(fullname) and os.path.isdir(fullname):
            return self
        for ext in exts:
            if os.path.exists(fullname+ext):
                return self

    def load_module(self,fullname):
        if fullname in sys.modules:
            return sys.modules[fullname]
        else:
            sys.modules[fullname] = imp.new_module(fullname)
        if isinstance(fullname,unicode):
            filename = fullname.replace(u'.',u'\\')
            ext = u'.py'
            initfile = u'__init__'
        else:
            filename = fullname.replace('.','\\')
            ext = '.py'
            initfile = '__init__'
        if os.path.exists(filename+ext):
            try:
                with open(filename+ext,'U') as fp:
                    mod = imp.load_source(fullname,filename+ext,fp)
                    sys.modules[fullname] = mod
                    mod.__loader__ = self
                    return mod
            except:
                print 'fail', filename+ext
                raise
        mod = sys.modules[fullname]
        mod.__loader__ = self
        mod.__file__ = os.path.join(os.getcwd(),filename)
        mod.__path__ = [filename]
        #init file
        initfile = os.path.join(filename,initfile+ext)
        if os.path.exists(initfile):
            with open(initfile,'U') as fp:
                code = fp.read()
            exec code in mod.__dict__
        return mod

【问题讨论】:

  • 这意味着 Python 被告知从字符串编译该模块;这可能是 PyCharm 的错,而不是 Python。
  • 您目前运行这段代码的具体情况如何?
  • @MartijnPieters: here 是启动器 - 我几乎可以肯定,在我将文件复制粘贴到 basher/__init__.py 之前,它没有加载字符串......
  • 不,我的意思是 PyCharm 指示 Python 解释器从字符串运行该文件,而不是要求 Python 将其作为模块导入。我没有说模块本身做了什么。
  • @MartijnPieters:当我直接运行启动器时也会发生这种情况 - 我提到了 Pycharm,因为在我看来,这与它无法单步执行 __init__.py中的代码的原因相同

标签: python python-2.7 python-import traceback


【解决方案1】:

代码不是以传统方式导入的;而launcher code 使用exec statement 来加载__init__.py 文件。

在启动器 load_module() 函数中为一个包缩减流程(因此不是模块的路径),你会得到:

# the fullname module isn't yet loaded
sys.modules[fullname] = imp.new_module(fullname)
initfile = '__init__'  # or u'__init__' if a unicode path was used

# if no .py file was found, so not a module
mod = sys.modules[fullname]
mod.__loader__ = self
mod.__file__ = os.path.join(os.getcwd(),filename)
mod.__path__ = [filename]
#init file
initfile = os.path.join(filename,initfile+ext)
if os.path.exists(initfile):
    with open(initfile,'U') as fp:
        code = fp.read()
    exec code in mod.__dict__
return mod

这会创建一个空的模块对象,手动加载源代码并将其作为字符串执行,将模块命名空间作为已执行代码的全局变量传入。生成的代码对象总是会在回溯中列出 &lt;string&gt;

>>> import imp
>>> mod = imp.new_module('foo.bar')
>>> mod.__file__ = r'C:\some\location\foo\bar'
>>> mod.__path__ = [r'foo\bar']
>>> exec 'raise ValueError("oops")' in mod.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<string>", line 1, in <module>
ValueError: oops

由于没有与代码关联的文件名,PyCharm 也无法找到原始源。

解决方法是使用compile() function 创建一个代码对象首先,然后附加一个文件名:

>>> exec compile('raise ValueError("oops")', r'C:\some\location\foo\bar\__init__.py', 'exec') in mod.__dict__
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "C:\some\location\foo\bar\__init__.py", line 1, in <module>
ValueError: oops

请注意,我在文件名中包含了__init__.py;将其翻译回您要使用的启动器:

if os.path.exists(initfile):
    with open(initfile,'U') as fp:
        code = fp.read()
    exec compile(code, initfile, 'exec') in mod.__dict__

【讨论】:

  • 是的,启动器代码超出了我的范围 - 将通过并消化所有这些并回复您 - 谢谢 :)
  • 只是为了确保我明白了 - 使用 compile() 函数只会在我的场景中发挥作用(记住我正在重构,所以这些文件很大)?顺便说一句,我注意到__init__.py 没有 pyc 文件,只是在问题中没有提到这一点 - 现在我知道为什么了。
  • exec 无论如何都必须编译,但是通过使用显式的compile() 调用,您可以为生成的代码对象指定一个文件名。这对启动器启动的所有代码都有帮助。
猜你喜欢
  • 2019-05-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-12
相关资源
最近更新 更多