【问题标题】:Attempted relative import beyond toplevel package尝试相对导入超出顶级包
【发布时间】:2017-02-22 15:35:30
【问题描述】:

这是我的文件夹结构:

Mopy/ # no init.py !
   bash/
     __init__.py
     bash.py # <--- Edit: yep there is such a module too
     bass.py
     bosh/
       __init__.py # contains from .. import bass
       bsa_files.py
     ...
   test_bash\
     __init__.py # code below
     test_bosh\
       __init__.py
       test_bsa_files.py

test_bash\__init__.py 我有:

import sys
from os.path import dirname, abspath, join, sep
mopy = dirname(dirname(abspath(__file__)))
assert mopy.split(sep)[-1].lower() == 'mopy'
sys.path.append(mopy)
print 'Mopy folder appended to path: ', mopy

test_bsa_files.py:

import unittest
from unittest import TestCase

import bosh

class TestBSAHeader(TestCase):
    def test_read_header(self):
        bosh.bsa_files.Header.read_header()

if __name__ == '__main__':
    unittest.main()

现在当我发布时:

python.exe "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py" C:\path\to\Mopy\test_bash\test_bosh\test_bsa_files.py true

我明白了:

Traceback (most recent call last):
  File "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py", line 124, in <module>
    modules = [loadSource(a[0])]
  File "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py", line 43, in loadSource
    module = imp.load_source(moduleName, fileName)
  File "C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\test_bash\test_bosh\test_bsa_files.py", line 4, in <module>
    import bosh
  File "C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\bash\bosh\__init__.py", line 50, in <module>
    from .. import bass
ValueError: Attempted relative import beyond toplevel package

既然“Mopy”在 sys.path 中并且 bosh\__init__.py 已正确解决,为什么它抱怨顶级包上方的相对导入?哪个是顶级包

顺便说一句,这是我向遗留项目添加测试的尝试 - 在 Python test package layout 中提出过要求,但作为 Where do the Python unit tests go? 的副本被关闭。非常感谢对我当前测试包布局的评论!


answer below 在我的情况下不起作用:

模块 bash.py 是应用程序的入口点,其中包含:

if __name__ == '__main__':
    main()

当我使用 import bash.boshfrom bash import bosh 时,我得到:

C:\_\Python27\python.exe "C:\_\JetBrains\PyCharm 2016.2.2\helpers\pycharm\utrunner.py" C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\test_bash\test_bosh\test_bsa_files.py true
Testing started at 3:45 PM ...
usage: utrunner.py [-h] [-o OBLIVIONPATH] [-p PERSONALPATH] [-u USERPATH]
                   [-l LOCALAPPDATAPATH] [-b] [-r] [-f FILENAME] [-q] [-i]
                   [-I] [-g GAMENAME] [-d] [-C] [-P] [--no-uac] [--uac]
                   [--bashmon] [-L LANGUAGE]
utrunner.py: error: unrecognized arguments: C:\Dropbox\eclipse_workspaces\python\wrye-bash\Mopy\test_bash\test_bosh\test_bsa_files.py true

Process finished with exit code 2

此用法消息来自 bash 中的 main()。

【问题讨论】:

  • 您在bash.py 中是否有任何未保护的代码与sys.argvargparse 一起使用? if __name__ == '__main__': 不会在 import bash 或其任何变体上触发。
  • @MisterMiyagi:是的,你明白了 - 这里的这一行解析了 cli:github.com/wrye-bash/wrye-bash/blob/…
  • 你应该把那个移到if __name__ ...,虽然它不能解决问题。我已经更新了答案,赋予包比其模块更高的优先级。

标签: python python-2.7 python-import python-unittest relative-import


【解决方案1】:

TLDR:做

import bash.bosh

from bash import bosh

避免修改sys.path,因为这会重复模块。


当你这样做时

import bosh

它将导入 模块 bosh。这意味着Mopy/bash 在你的sys.path 中,python 在那里找到文件bosh,并导入它。该模块现在以名称bosh 全球闻名。 bosh 本身是模块还是包无关紧要,只是使用bosh.py 还是bosh/__init__.py 会有所不同。

现在,当bosh 尝试做时

from .. import bass

不是文件系统操作(“一个目录向上,文件bass”)而是一个模块名称操作。它的意思是“一个包升级,模块低音”。 bosh 不是从它的包中导入的,而是单独导入的。所以升级一个包是不可能的——你最终得到包'',这是无效的。

让我们看看当你这样做时会发生什么

import bash.bosh

相反。首先, bash 被导入。然后,bosh 被导入为该包的一个模块 - 它在全球范围内被称为bash.bosh,即使您使用了from bash import bosh

bosh 这样做时

from .. import bass

现在可以使用了:从bash.bosh 上一级可以到达bash。从那里,bass 被导入为bash.bass

【讨论】:

  • 我试过了,顺便说一句,我收到了消息,但我没有提到(以及无数其他这样的暗中打击)
  • @Mr_and_Mrs_D 您的文件层次结构中有重复的名称,并且认为这可能不值得一提? oO 让我看看我能不能解决这个问题...
  • @Mr_and_Mrs_D 添加了一块用于更改模块与包代码的偏好。试一试。
  • Nope :( 与append 完全相同的行为 - 我仍然看到 bash.main() 中的 usage() (哈哈哈,我的意思是,想象我们甚至有一个自定义加载器 - 而不是世界上最简单的包结构...)
  • 等一下 - 让我们把它移到 test_bsa_files 里面 - 编辑:是的,成功了!!!我这样做是因为我注意到调试打印('print'Mopy 文件夹附加到路径:',mopy` 在最后一个输出中没有看到 - 带有使用()的那个)。但是为什么在这种情况下不运行test_bash\__init__.py
【解决方案2】:

无需破解或研究同级模块的导入。 只需转到您的项目目录并导入模块。如果项目目录不是包,添加 init.py 使其成为项目目录。

# File name ProjectDir/sibling1/main.py
import ProjectDir.sibling2

if __name__=='__main__':
    md = sibling2.module()

【讨论】:

  • 可以重复模块和子模块的深度。例如。 import ProjectDir.sibling2.submodule.subsubmodule ...
猜你喜欢
  • 2020-11-03
  • 2020-04-06
  • 2016-05-12
  • 1970-01-01
  • 2020-03-07
  • 2021-10-03
  • 2020-12-03
相关资源
最近更新 更多