【问题标题】:Getting Attempted relative import in non-package error in spite of having __init__.py尽管有 __init__.py,但在非包错误中尝试相对导入
【发布时间】:2013-09-19 07:03:49
【问题描述】:

我有一个包裹cclogger。该目录有一个__init__.py 文件,其中包含一些用于加载配置的代码。当我尝试使用以下命令在该目录中运行文件api_main.py 时...

python -m cclogger.api_main

我得到以下错误:-

config loaded
Instantiating DB with: cclogger/test123@localhost:x
Instantiated ParseCentral
Register parser called by : CitiIndia
Registered parser for email:  CitiAlert.India@citicorp.com
Instantiated SmsParseCentral
Register parser called by : Citi Bank
Registered sms parser for address:  lm-citibk
Register parser called by : HDFC Bank
Registered sms parser for address:  am-hdfcbk
Traceback (most recent call last):
  File "/Users/applegrew/Dropbox/Credit Expense/cclogger/cclogger/api_main.py", line 4, in <module>
    from .bottle import run, default_app, debug, get
ValueError: Attempted relative import in non-package

错误上方显示的消息来自__init__.py导入的同一包中的模块。

api_main.py 中的代码是:-

import re
import os

from .bottle import run, default_app, debug, get
from .common_util import date_str_to_datetime, UTCOffset, date_filter

#app = Bottle()

default_app().router.add_filter('date', date_filter)

from . import api, dev

@get('/index')
def index():
    return "CCLogger API main live and kicking."

if dev:
    debug(True)
    run(reloader=True, port=9000)
else:
    os.chdir(os.path.dirname(__file__))
    application = default_app()

我有 python 2.7.1。

我做错了什么?您可以在https://github.com/applegrew/cclogger/tree/master/cclogger查看完整代码

【问题讨论】:

    标签: python python-2.7 osx-lion


    【解决方案1】:

    您不能直接将 python 模块作为脚本运行(我真的不知道原因)。

    编辑:原因在PEP338 中解释,这是"-m" 选项的规范。

    2.5b1 的发布显示出令人惊讶的(虽然在 回顾)此 PEP 和 PEP 328 之间的交互 ​​- 显式 相对导入在主模块中不起作用。这是由于 事实上,相对导入依赖__name__ 来确定当前 模块在包层次结构中的位置。在主模块中, __name__ 的值始终为 __main__ ,因此显式相对导入 总是会失败(因为它们只适用于包内的模块)。

    对于 2.5 版本,建议在任何打算用作主模块的模块中始终使用绝对导入

    要测试您的应用程序,请将 api_main 封装在一个函数中并创建一个将运行主循环的顶级 main.py 文件:

    cclogger/api_main.py:

    import re
    import os
    
    
    from .bottle import run, default_app, debug, get
    from .common_util import date_str_to_datetime, UTCOffset, date_filter
    
    #app = Bottle()
    
    
    def main():
        default_app().router.add_filter('date', date_filter)
    
        from . import api, dev
    
        @get('/index')
        def index():
            return "CCLogger API main live and kicking."
    
        if dev:
            debug(True)
            run(reloader=True, port=9000)
        else:
            os.chdir(os.path.dirname(__file__))
            application = default_app()
    

    还有 /main.py

    from cclogger import api_main
    
    
    if __name__ == '__main__':
        api_main.main()
    

    您可以通过键入 python main.pypython -m mainpython -c "import cclogger.api_main; api_main.main()" 来运行您的应用程序。

    PS:感谢您链接完整的源代码,它总是比问题提供的存根更有帮助。

    【讨论】:

    • 它对我不起作用。 (同理:非包中的相对导入)
    • 这个接受的答案是不正确的(或者说已经过时了——对于 Python 2.5 可能是正确的,但 OP 声明了 Python 2.7)。请参考我的回答。
    • @Manganeez :这对于 2.7 仍然有效。 2.xx 规范具有向前兼容性(或者如果您更喜欢 2.xx 实现,则向后兼容)。
    • @georgesl 对不起,我不明白。向后兼容意味着以前可以工作的东西继续工作——这并不意味着以前不能工作的东西现在一定不能工作。例如,2.7 清楚地引入了许多新语法(例如 set literals 和 dict comprehensions)。您断言因为“-m”标志与 2.5 中的相对导入不兼容,所以在 2.7 中它必须仍然如此,但这显然是错误的。 PEP-366 明确指出这种不兼容性已在 2.6 中得到纠正。你看我的回答了吗?如果我误解了你的意思,我深表歉意。
    【解决方案2】:

    从 Python 2.6 开始,相对导入在使用 python -m 执行的模块中运行良好。正如http://www.python.org/dev/peps/pep-0366 中所解释的,__package__ 属性会在以这种方式执行时自动在模块上设置,以便相对导入起作用。这里导致失败的部分是重新加载器,它显然无法设置__package__

    编辑:看一眼代码,我发现这是一个瓶子 API。我没有检查瓶子,但它可能与 Flask 的重新加载器存在相同的问题,后者通过信任 sys.argv 中的内容来尝试重新启动。问题是处理python -m(位于runpy 模块中)的代码修改了sys.argv,删除了“-m”并将模块名称转换为文件名(这非常令人沮丧,但我当然他们有充分的理由)。因此,重新加载器实际执行的内容没有“-m”,并且设置__package__ 的位永远不会触发,导致不支持相对导入。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-06-13
      • 2017-05-17
      • 2017-07-25
      • 1970-01-01
      • 2012-07-17
      • 1970-01-01
      相关资源
      最近更新 更多