Импорт затененного модуля из пакета

#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, но это противоречит идее области применения. Имена внутри модуля не должны конфликтовать с именем модуля.

Является ли решение о переименовании единственно возможным?