2016-05-18 5 views
10

У меня есть библиотека питона со следующей структурой: хранилищеИспользование py.test с кодом компилируются библиотеки

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? С учетом этих ограничений:

  1. Я не хочу, чтобы включить любые sys.path/импорта магии в test_mylibrary.py, потому что это может нарушить тесты в других envrionments.

  2. Я не хочу отказаться от возможности запускать py.test от repobase. Поэтому изменение PYTHONPATH не помогает, потому что . по-прежнему будет первым в sys.path. И таким образом repobase/mylibrary было бы желательно за repobase/build/lib.linux-x86_64-2.7/mylibrary.

Если нет, то это стандартный способ для библиотек тестирования Python, что нужно строить?

+0

Непонятно, что вы подразумеваете под «... я расширил библиотеку для использования скомпилированного кода ...», то есть, скомпилированная версия предоставляет тот же интерфейс, что и версия Python, или же импортирует версию Python 'скомпилированная версия? Если первое, то вы эффективно тестируете две разные вещи, поэтому, возможно, они должны иметь разные имена, даже если набор тестов идентичен, q.v. Python 'pickle' vs' cPickle'. Если последнее, то у них обязательно должны быть разные имена. Общая идиома Python заключается в том, что скомпилированная часть должна быть добавлена ​​с подчеркиванием, q.v. Python 'socket' vs' _socket'. – Aya

+0

(продолжение) В любом случае, имея две разные реализации модулей, использующие одно и то же имя, возникает проблема. Даже если ваше решение работает для всех возможных случаев выполнения на сегодняшний день, вы не можете предвидеть все возможные будущие случаи выполнения, и вы можете в конечном итоге импортировать или протестировать неправильную версию, не осознавая ее. – Aya

+0

@Aya У меня только одна реализация. Скомпилированный код * заменяет * прежний код python. Как я написал «Поэтому исходный код в repobase/mylibrary не работает сам по себе». Мне нужен скомпилированный код, чтобы иметь возможность запускать тесты. –

ответ

5

Я думаю, ваша проблема в том, что py.test не копирует встроенный общий объект в корень вашего репозитория.

Я просто попытался запустить UT прямо из Python wiki на расширениях тестирования С помощью py.test следующим образом:

python setup.py build 
py.test test/examp_unittest.py 

Это не удалось, с AssertionError: No module named examp.

Однако, когда я следую вики к письму (и запустить 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. Все работает так, как ожидалось.

+0

@TimHoffmann Что-то я пропустил в этом ответе? –

+0

Я не могу сказать, потому что у меня еще не было времени попробовать (домой, завтра). Из вашего примера я заключаю, что мне нужно запустить 'python setup.py test'. Но сначала будут запускаться стандартные unittests (что является устаревшим, поскольку оно не будет обнаруживать и/или правильно относиться ко всем моим тестам). Это просто лишнее время. Возможно, тогда я должен интегрировать pytest с setuptools (https://pytest.org/latest/goodpractices.html#integrating-with-setuptools-python-setup-py-test-pytest-runner). –

+0

Все еще кажется, что это неправильно, чтобы каждый раз запускать все тесты. Я часто хочу изменить некоторый код, а затем построить (потому что я должен для скомпилированных материалов), а затем запустить один тест, например. 'py.test tests/test_mylibrary.py :: test_do_something'. Возможно, мне нужно адаптировать 'python setup.py build' или создать собственный' python setup.py build_test', который просто копирует необходимые файлы '.so'. Мне действительно интересно, почему я не могу найти что-либо в этом направлении, потому что я бы предположил, что это стандартная проблема, с которой вы сталкиваетесь, как только вы используете py.test и скомпилированный код. –

3

Из обсуждения в чате звучит так, как если бы реализация 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__.py

# 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 определит, какая версия протестирована.

Смежные вопросы