#python #import #pytest #monkeypatching
#питон #импортировать #пытест #охота на обезьян
Вопрос:
Это моя структура пакета:
. ├── 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) -gt; 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()
в качестве первого параметра требуется модуль, но я не могу его предоставить. src.damastes.run
на самом деле это функция. Как и должно быть. «Короткое замыкание» намеренно.
Ошибка:
AttributeError: lt;function run at 0x7f3392b9f790gt; has no attribute '_ARGS'
В порядке эксперимента я предоставляю src.damastes
:
AttributeError: lt;module 'src.damastes' from '/src/damastes/__init__.py'gt; has no attribute '_ARGS'
Конечно, это не так. Есть ли способ предоставить run
модуль monkeypatch.setattr()
без реструктуризации существующего решения для упаковки/импорта?
from src.damastes.run import _path_compare
кстати, часть каким-то образом работает.
Комментарии:
1. Не уверен, что это корень вашего вопроса, но
from .run import *
in__init__.py
не импортирует имена, начинающиеся с подчеркивания, например_ARGS
.2. Если вам нужно
_ARGS
, чтобы они были сопоставлены для правильного тестирования, то, возможно, они не должны быть жестко закодированы и «помечены как частные» (с использованием символа подчеркивания). Рассмотрим принцип инверсии зависимостей.3. Действительно, это не так. Я не уверен, что это тоже корень моего вопроса, на данный момент
4. »
from src.damastes.run import _path_compare
кстати, часть каким-то образом работает». Да, потому что теперь, когда это.run
часть пути «от», он может быть разрешен как ссылка на модуль, определенныйrun.py
, а не наrun
имя, определенное вsrc.damastes
(с помощью__init__
автоматической загрузки).5. @KarlKnechtel Есть ли выход в моем случае?
Ответ №1:
Мне пришлось переименовать модуль: run.py
-gt; gt; 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, но это противоречит идее области применения. Имена внутри модуля не должны конфликтовать с именем модуля.
Является ли решение о переименовании единственно возможным?