У меня есть проект Python, для которого я оборачиваю некоторый код C / C++ (используя отличную библиотеку PyBind). У меня есть набор модульных тестов C и Python, и я настроил CI Gitlab для их запуска при каждом нажатии. В тестах C используется минималистичный фреймворк модульного тестирования под названием minunit, а я использую набор Python unittest.
Перед запуском тестов C весь код C компилируется, а затем тестируется. Я также хотел бы скомпилировать оболочку C / C++ для Python перед запуском тестов Python, но у меня нет времени на это.
Есть ли стандартный / хороший способ заставить Gitlab-CI создать расширение Python с использованием setuptools перед запуском модульных тестов?
Для локальной компиляции оболочки C / C++ я использую setuptools с файлом setup.py, включая команду build_ext.
Я локально компилирую все с помощью python setup.py build_ext --inplace (последний аргумент --inplace просто скопирует скомпилированный файл в текущий каталог).
Насколько я знаю, это вполне стандартно.
Что я пытался сделать в Gitlab, так это иметь скрипт Python (код ниже), который будет запускать несколько команд с помощью команды os.system (что кажется плохой практикой ...).
Первая команда - запустить сборку сценария и запустить все тесты C. Это работает, но я рад принять рекомендации (следует ли мне настроить Gitlab CI для запуска тестов C отдельно?).
Теперь проблема возникает, когда я пытаюсь создать оболочку C / C++ с помощью os.system("cd python/ \npython setup.py build_ext --inplace"). Это вызывает ошибку
File "setup.py", line 1, in <module>
from setuptools import setup, Extension
ImportError: No module named setuptools
Поэтому я попытался изменить файл конфигурации CI моего gitlab, чтобы установить python-dev. Мой .gitlab-ci.yml выглядит так
test:
script:
- apt-get install -y python-dev
- python run_tests.py
Но, не будучи sudo на сервере gitlab, я получаю следующую ошибку E: Could not open lock file /var/lib/dpkg/lock - open (13: Permission denied).
Кто-нибудь знает способ обойти это или лучший способ решить эту проблему?
Любая помощь будет более чем приветствоваться!
import unittest
import os
from shutil import copyfile
import glob
class AllTests(unittest.TestCase):
def test_all(self):
# this automatically loads all tests in current dir
testsuite = unittest.TestLoader().discover('tests/Python_tests')
# run tests
result = unittest.TextTestRunner(verbosity=2).run(testsuite)
# send/print results
self.assertEqual(result.failures, [], 'Failure')
if __name__ == "__main__":
# run C tests
print(' ------------------------------------------------------ C TESTS')
os.system("cd tests/C_tests/ \nbash run_all.sh")
# now python tests
print(' ------------------------------------------------- PYTHON TESTS')
# first build and copy shared library compiled from C++ in the python test directory
# build lib
os.system("cd python/ \npython setup.py build_ext --inplace")
# copy lib it to right place
dest_dir = 'tests/Python_tests/'
for file in glob.glob(r'python/*.so'):
print('Copying file to test dir : ', file)
copyfile(file, dest_dir+file.replace('python/', ''))
# run Python tests
unittest.main(verbosity=0)






Я предлагаю перенести всю логику выполнения теста в сценарий установки.
testПрежде всего, setuptools содержит команду test, поэтому вы можете запускать тесты через python setup.py test. Более того, test вызывает команду build_ext под капотом и размещает встроенные расширения так, чтобы они были доступны в тестах, поэтому вам не нужно явно вызывать python setup.py build_ext:
$ python setup.py test
running test
running egg_info
creating so.egg-info
writing so.egg-info/PKG-INFO
writing dependency_links to so.egg-info/dependency_links.txt
writing top-level names to so.egg-info/top_level.txt
writing manifest file 'so.egg-info/SOURCES.txt'
reading manifest file 'so.egg-info/SOURCES.txt'
writing manifest file 'so.egg-info/SOURCES.txt'
running build_ext
building 'wrap_fib' extension
creating build
creating build/temp.linux-aarch64-3.6
aarch64-unknown-linux-gnu-gcc -pthread -fPIC -I/data/gentoo64/usr/include/python3.6m -c wrap_fib.c -o build/temp.linux-aarch64-3.6/wrap_fib.o
aarch64-unknown-linux-gnu-gcc -pthread -fPIC -I/data/gentoo64/usr/include/python3.6m -c cfib.c -o build/temp.linux-aarch64-3.6/cfib.o
creating build/lib.linux-aarch64-3.6
aarch64-unknown-linux-gnu-gcc -pthread -shared -Wl,-O1 -Wl,--as-needed -L. build/temp.linux-aarch64-3.6/wrap_fib.o build/temp.linux-aarch64-3.6/cfib.o -L/data/gentoo64/usr/lib64 -lpython3.6m -o build/lib.linux-aarch64-3.6/wrap_fib.cpython-36m-aarch64-linux-gnu.so
copying build/lib.linux-aarch64-3.6/wrap_fib.cpython-36m-aarch64-linux-gnu.so ->
test_fib_0 (test_fib.FibonacciTests) ... ok
test_fib_1 (test_fib.FibonacciTests) ... ok
test_fib_10 (test_fib.FibonacciTests) ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK
(Для игры я использовал код из Репозиторий примеров Cython Book, но результат должен быть очень похож на то, что производит PyBind).
Еще одна функция, которая может оказаться полезной, - это дополнительные ключевые слова, которые добавляет setuptools: test_suite, tests_require, test_loader (документы). Вот пример встраивания пользовательского набора тестов, как в run_tests.py:
# setup.py
import unittest
from Cython.Build import cythonize
from setuptools import setup, Extension
exts = cythonize([Extension("wrap_fib", sources=["cfib.c", "wrap_fib.pyx"])])
def pysuite():
return unittest.TestLoader().discover('tests/python_tests')
if __name__ == '__main__':
setup(
name='so',
version='0.1',
ext_modules=exts,
test_suite='setup.pysuite'
)
testПоследнее требование - запуск C-тестов. Мы можем встроить их, переопределив команду test и вызвав оттуда некоторый собственный код. Преимущество этого заключается в том, что distutils предлагает командный API с множеством полезных функций, таких как копирование файлов или выполнение внешних команд:
# setup.py
import os
import unittest
from Cython.Build import cythonize
from setuptools import setup, Extension
from setuptools.command.test import test as test_orig
exts = cythonize([Extension("wrap_fib", sources=["cfib.c", "wrap_fib.pyx"])])
class test(test_orig):
def run(self):
# run python tests
super().run()
# run c tests
self.announce('Running C tests ...')
pwd = os.getcwd()
os.chdir('tests/C_tests')
self.spawn(['bash', 'run_all.sh'])
os.chdir(pwd)
def pysuite():
return unittest.TestLoader().discover('tests/python_tests')
if __name__ == '__main__':
setup(
name='so',
version='0.1',
ext_modules=exts,
test_suite='setup.pysuite',
cmdclass = {'test': test}
)
Я расширил исходную команду test, запустив некоторые дополнительные вещи после завершения модульных тестов Python (обратите внимание на вызов внешней команды через self.spawn). Все, что осталось, - это заменить команду test по умолчанию на пользовательскую, передав cmdclass в функции настройки.
Теперь у вас есть все, что собрано в сценарии установки, и python setup.py test сделает всю грязную работу.
But, not being sudo on the gitlab's server, I get the following error
У меня нет опыта работы с Gitlab CI, но я не могу представить, что нет возможности устанавливать пакеты на сервер сборки. Может этот вопрос будет полезен: Как использовать sudo в скрипте сборки для gitlab ci?
Если другого варианта действительно нет, можно Загрузите локальную копию setuptools с ez_setup.py. Однако обратите внимание, что хотя этот метод все еще работает, недавно он устарел.
Кроме того, если вы используете последнюю версию Python (3.4 и новее), тогда у вас должен быть pip в комплекте с дистрибутивом Python, поэтому должна быть возможность установить setuptools без прав root с помощью
$ python -m pip install --user setuptools
Вы можете настроить обнаружение теста в скрипте установки, в ответе есть пример этого. Если вы имеете в виду тесты C, я обновил ответ, спустившись в каталог тестов (через os.chdir).
Что касается вопроса с разрешением, не могли бы вы поделиться подробностями? Это система, в которой вы не являетесь пользователем root и не имеете никаких шансов получить разрешения sudo от вашего администратора? Можете проверить, работает ли sudo apt install случайно? Кроме того, какая версия Python предустановлена / предназначена (python -V)? Есть ли у вас pip для целевого дистрибутива Python (pip -V)? По-прежнему доступны некоторые другие варианты, такие как создание локальной установки Python из исходного кода и установка любых пакетов, которые вам нужны локально, или запуск нового контейнера докеров, в котором у вас будет root-доступ.
Спасибо, да, настройка обнаружения тестов кажется естественным вариантом, но все же проблема в том, что для этого требуется setuptools. Да, я попробовал с sudo apt install и получил ту же ошибку. К сожалению, нет, у меня нет доступа к sudo. Я не знаю, какая версия работает на сервере, это должен быть Python 3.5.4, но я дважды проверю! У меня есть pip, да.
Да, что касается докера, я никогда его не использовал, но я посмотрел его, и это похоже на хорошее решение моей проблемы. Вы когда-нибудь запускали тесты с докером на gitlab?
Если у вас есть pip и даже последняя версия Python, использование докера только для установки setuptools является излишним - используйте pip install --user setuptools или python -m pip install --user setuptools, и все готово. Сначала проверьте, соответствуют ли pip и python целевым версиям, замените конкретными pip3.5 / python3.5 / и т. д.
Большое спасибо, добавление python -m pip install --user setuptools помогло! Вы можете изменить свой комментарий как ответ, и я отмечу его как правильный ответ. Спасибо большое
Хорошо, рад, что смог помочь! Я упомянул этот метод в последнем абзаце ответа.
Большое спасибо за ответ! Однако мои тесты не находятся в том же каталоге, что и оболочка, и команда
python setup.py testне находит их, что, как я полагаю, можно решить довольно легко. Проблема в том, что это решение по-прежнему предполагает возможность загрузкиsetuptools, что не относится к моей проблеме ... Спасибо за две другие ссылки, я проверю их и добавлю ответ, если найду ту, которая решает мою проблему. проблема.