我有一个Python项目,为此我包装了一些C / C ++代码(使用出色的PyBind库)。我有一组C和Python单元测试,并且已配置Gitlab的CI以在每次推送时运行它们。C测试使用称为minunit的极简单元测试框架,而我使用Python的unittest套件。
在运行C测试之前,将编译所有C代码,然后进行测试。我还想在运行Python测试之前为Python编译C / C ++包装器,但这样做很难。
在运行单元测试之前,是否有标准/好的方法来Gitlab-CI
构建Python扩展setuptools
?
为了在本地编译C / C ++包装器,我使用setuptools
了setup.py
包含build_ext
命令的文件。我在本地编译所有内容python setup.py build_ext --inplace
(最后一个arg--inplace
只会将编译后的文件复制到当前目录中)。据我所知,这是很标准的。
我试图在Gitlab上做的是拥有一个Python脚本(下面的代码),该脚本将使用os.system
command运行一些命令(这似乎是一种不好的做法...)。第一个命令是运行脚本构建并运行所有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
因此,我尝试修改gitlab的CI配置文件以安装python-dev。我的.gitlab-ci.yml
样子
test:
script:
- apt-get install -y python-dev
- python run_tests.py
但是,不是在gitlab的服务器上使用sudo,我得到了以下错误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
callsbuild_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
并将完成所有肮脏的工作。
但是,不是在gitlab的服务器上使用sudo,我收到以下错误
我没有使用Gitlab CI的经验,但是我无法想象不可能在构建服务器上安装软件包。也许这个问题会有所帮助:如何在gitlab ci的构建脚本中使用sudo?
如果真的有没有其他选择,你可以引导的本地副本setuptools
用ez_setup.py
。但是请注意,尽管此方法仍然有效,但最近已弃用。
另外,如果您恰巧使用的是Python的最新版本(3.4及更高版本),则应pip
将其与Python发行版捆绑在一起,因此应该可以在setuptools
没有root权限的情况下进行安装
$ python -m pip install --user setuptools
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句