Использование py.test с компилированным библиотечным кодом
У меня есть библиотека python со следующей структурой репозитория:
repobase
|- mylibrary
| |- __init__.py
|- tests
|- test_mylibrary.py
До сих пор запуск тестов можно было просто выполнить, вызвав py.test
в каталоге repobase. import mylibrary
в test_mylibrary.py затем использовал локальный код в repobase/mylibrary.
Теперь я расширил библиотеку, чтобы использовать скомпилированный код. Поэтому исходный код в repobase/mylibrary не работает сам по себе. Я должен сделать setup.py build
. Это создает repobase/build/lib.linux-x86_64-2.7/mylibrary.
Есть ли разумный способ заставить py.test использовать этот каталог для импорта mylibrary? Учитывая эти ограничения:
-
Я не хочу включать никакую магию sys.path
/import в test_mylibrary.py, потому что это может нарушить тесты в других envrionments.
-
Я не хочу отказаться от возможности запускать py.test
из repobase. Поэтому изменение PYTHONPATH не помогает, потому что .
по-прежнему будет первым в sys.path
. И таким образом repobase/mylibrary будет предпочтительнее repobase/build/lib.linux-x86_64-2.7/mylibrary.
Если нет, то какой стандартный способ для тестирования библиотек python, которые нуждаются в создании?
Ответы
Ответ 1
Я думаю, ваша проблема в том, что py.test не копирует встроенный общий объект в корень вашего репозитория.
Я просто попытался запустить UT прямо из Python wiki при тестировании расширений C с помощью py.test следующим образом:
python setup.py build
py.test test/examp_unittest.py
Это не удалось с помощью AssertionError: No module named examp
.
Однако, когда я следую wiki в письме (и запускаю python setup.py test
вместо этого), я отмечаю, что он копирует .so
в корневой каталог (обратите внимание на последнюю строку до начала запуска теста):
running test
running egg_info
writing examp.egg-info/PKG-INFO
writing top-level names to examp.egg-info/top_level.txt
writing dependency_links to examp.egg-info/dependency_links.txt
reading manifest file 'examp.egg-info/SOURCES.txt'
writing manifest file 'examp.egg-info/SOURCES.txt'
running build_ext
copying build/lib.linux-x86_64-2.6/examp.so ->
runTest (test.examp_unittest.DeviceTest) ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK
Запустив это в моей системе, я могу теперь запустить py.test довольно счастливо на той же базе кода, как показано ниже.
============================= test session starts ==============================
platform linux2 -- Python 2.7.3, pytest-2.9.2, py-1.4.31, pluggy-0.3.1
rootdir: /tmp/sotest, inifile:
collected 1 items
test/examp_unittest.py .
=========================== 1 passed in 0.01 seconds ===========================
Таким образом, решение состоит в том, чтобы скопировать общий объект в корень вашего репозитория.
Чтобы убедиться, что я выполнил все это с нуля, просто создайте расширение, скопируйте общий объект и запустите py.test. Все это работает как ожидается.
Ответ 2
Из обсуждения в чате это звучит так, как будто реализация C предоставляет только часть функциональности реализации Python.
Общим решением является разбиение модуля таким образом, что части, которые требуют оптимизированных реализаций, существуют в отдельном модуле.
Рассмотрим более конкретный пример библиотеки, которая нуждается в преобразовании между различными форматами изображений.
Предположим, что ваш макет выглядит так...
repobase
|- image
| |- __init__.py
| |- pyJPEG.py
|- build
| |- lib.linux-x86_64-2.7
| |- cJPEG.so
|- tests
|- test_image.py
... ваш PYTHONPATH
включает /path/to/repobase:/path/to/repobase/build/lib.linux-x86_64-2.7
, ваш cJPEG.so
экспортирует символы jpeg_decompress
и jpeg_compress
, и ваши файлы выглядят так:
изображение /__ __ INIT. Ру
# Load the C implementation if we have it, otherwise fall back to
# a pure Python implementation
try:
from cJPEG import jpeg_decompress, jpeg_compress
except ImportError:
from pyJPEG import jpeg_decompress, jpeg_compress
def load_image(filename):
data = open(filename, 'rb').read()
if filename.endswidth('.jpg'):
return jpeg_decompress(data)
else:
raise NotImplementedError
def save_image(data, filename, filetype='JPEG'):
if filetype == 'JPEG':
data = jpeg_compress(data)
else:
raise NotImplementedError
open(filename, 'wb').write(data)
изображение /pyJPEG.py
def jpeg_decompress(data):
# A pure Python implementation of a JPEG decoder
def jpeg_compress(data):
# A pure Python implementation of a JPEG encoder
При таком макете тестовому набору не важно, построена ли библиотека или нет - вы можете использовать один и тот же набор в обоих случаях, а наличие (или отсутствие) cJPEG.so
будет определять, какая версия протестирована.