Как управлять sys.path в pytest, в котором выполняются приемочные тесты конфигурации системы

#python #ansible #pytest

#python #ansible #pytest

Вопрос:

У нас есть тестовая платформа, которая немного расширяет pytest и оборачивает его большим количеством Ansible, чтобы запускать приемочные тесты конфигурации системы, чтобы убедиться, что наша инфраструктура установлена и настроена правильно и, похоже, работает нормально. Это делается с помощью Ansible для упаковки тестов, отправки их на удаленный хост, затем распаковки их в virtualenv для запуска тестов. Пока что тесты большинства пользователей включают выполнение команд на удаленном хосте для проверки их работы.

Часть того, что я пытаюсь проверить, установлены ли и работают ли некоторые модули Python, и если они установлены, используйте их в дальнейших тестах. Но я несколько загнан в тупик из-за того, что virtualenv (совершенно справедливо) скрывает все установленные в системе модули Python.

Пока что моя стратегия заключается в запуске subprocess командной оболочки / небольшого скрипта, который отключает все переменные среды, связанные с virtualenv, а затем запускает короткий скрипт Python, используя системный python, который выбирает его sys.path и сбрасывает в стандартный вывод.

Но тогда я не уверен насчет следующего шага. Как правильно обращаться с sys.path внутри теста или приспособления? Восстановит ли pytest его, как только устройство больше не понадобится? Что pytest делает с модулями, которые импортирует тест? Удаляет ли это их из sys.modules после завершения теста?

Ответ №1:

Ну, я не получил ответа, поэтому я придумал это приспособление для установки conftest.py :

 @pytest.fixture(scope="module")
def need_system_libraries():
    pycmd = 'unset VIRTUAL_ENV PYTHONPATH; exec {pyexec} -c '
            '"import sys, pickle; sys.stdout.write(pickle.dumps(sys.path, 0))"'

    pycmd = pycmd.format(pyexec='/usr/bin/python3.7')
    _, stdout, _ = testlib.execute(pycmd)
    system_path = pickle.loads(stdout)
    # Prune any paths from the system_path that already exist in sys.path or     
    # are relative (not absolute) paths.                                         
    newsyspaths = frozenset(system_path) - frozenset(sys.path)
    newsyspaths = frozenset(path for path in newsyspaths
                            if os.path.isabs(path))
    # A set isn't ordered. Put difference back in the same order as the          
    # elements appeared in system_path. (Not needed in Python >= 3.6).           
    newsyspaths = [path for path in system_path if x in newsyspaths]
    # Save a copy of the path so we can restore it.                              
    saved_syspath = sys.path.copy()
    sys.path.extend(newsyspaths)
    # This fixture modifies the environment itself, and so doesn't need to       
    # return a value.                                                            
    yield None
    # The yield will come back when this fixture is no longer needed, so         
    # restore sys.path back to its original value. Do this by copying elements   
    # back into original sys.path in case the identity of sys.path is important  
    # somewhere deep in the bowels of Python.                                    
    sys.path[:] = saved_syspath