【问题标题】:Import a shadowed module from a package从包中导入阴影模块
【发布时间】:2022-01-10 04:51:39
【问题描述】:

这是我的包结构:

.
├── src
│   ├── damastes
│   │   ├── __init__.py
│   │   ├── main.py
│   │   └── run.py

__init__.py:

from .run import *

run.py:

...
_ARGS
...
def _path_compare(path_x: Path, path_y: Path) -> Ord:
    return (
        ...
        if _ARGS.sort_lex
        else ...
    )
...
def run()
...

我可以run 模块导入任何我想要的东西,但不能导入run 模块本身。直到我不得不提供猴子补丁的模块,这并没有给我带来不便:

test.py:

from src.damastes.run import _path_compare
...
def test_path_compare(monkeypatch):
    args = RestrictedDotDict(copy.deepcopy(CLEAN_CONTEXT_PARAMS))
    args.sort_lex = True
    monkeypatch.setattr(src.damastes.run, "_ARGS", args)
    assert _path_compare("alfa", "alfa") == 0

我的麻烦是monkeypatch.setattr() 需要 module 作为第一个参数,但我无法提供它。 src.damastes.run 实际上是一个函数。应该如此。 “短路”是故意的。

错误:

AttributeError: <function run at 0x7f3392b9f790> has no attribute '_ARGS'

在实验中,我提供src.damastes

AttributeError: <module 'src.damastes' from '/src/damastes/__init__.py'> has no attribute '_ARGS'

当然没有。有没有办法将run 模块提供给monkeypatch.setattr() 而无需重构现有的包/导入解决方案?

顺便说一句,from src.damastes.run import _path_compare 部分可以正常工作。

【问题讨论】:

  • 不确定这是否是您问题的根源,但__init__.py 中的from .run import * 不会导入以下划线开头的名称,例如_ARGS
  • 如果您需要对 _ARGS 进行猴子补丁以便正确测试,那么也许它们不应该被硬编码和“标记为私有”(通过使用前导下划线)。考虑依赖倒置的原则。
  • 确实没有。我暂时不确定这也是我问题的根源
  • "from src.damastes.run import _path_compare 部分工作,顺便说一句。"是的,因为现在.run 是“来自”路径的一部分,它可以被解析为引用run.py 定义的模块,而不是src.damastes 中定义的run 名称(通过@987654347 @自动加载)。
  • @KarlKnechtel 就我而言,有出路吗?

标签: python import pytest monkeypatching


【解决方案1】:

我不得不重命名模块:run.py -> shoot.py:

init.py:

from .shoot import *

test.py:

import src.damastes.shoot as shoot
...
class TestNonPureHelpers:
    def new_args(self):
        return RestrictedDotDict(copy.deepcopy(CLEAN_CONTEXT_PARAMS))

    def test_path_compare(self, monkeypatch):
        args = self.new_args()
        monkeypatch.setattr(shoot, "_ARGS", args)
        args.sort_lex = True
        assert shoot._path_compare("10alfa", "2bravo") == -1
        args.sort_lex = False  # Sort naturally.
        assert shoot._path_compare("10alfa", "2bravo") == 1

解决方案有点明显,但有点不舒服。对于像 Python 这样的 ad hoc 语言来说并非不可想象,但这与范围的想法背道而驰。模块内的名称不应与模块名称冲突。

重命名解决方案是唯一可能的吗?

【讨论】:

    猜你喜欢
    • 2015-10-06
    • 2020-08-27
    • 2016-07-30
    • 1970-01-01
    • 2019-05-26
    • 2020-01-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多