Python 测试工具tox
似乎旨在与 virtualenv 一起使用。它也可以在基于 conda/anaconda 的 Python 安装上工作吗?
5 回答
tox-conda插件现在应该可以弥补这一差距,但需要积极使用 conda 的贡献者来测试和改进它。
从自述文件:
tox-conda
是一个插件,为tox自动化工具提供与conda包和环境管理器的集成。这就像拥有你的蛋糕并吃它一样!默认情况下,
tox
使用 [virtualenv]( https://virtualenv.pypa.io]创建隔离环境并从pip
.相反,在使用
tox-conda
插件时tox
将使用conda
创建环境,并从conda
. 这对于依赖conda
环境管理和包分发但希望利用tox
测试自动化提供的功能的开发人员很有用。
要安装该插件,它需要与 tox 一起安装在同一个虚拟环境中。要创建一个包含tox
并且tox-conda
这应该足够的虚拟环境:
$ python3 -m venv toxbase
$ toxbase/bin/pip install tox tox-conda
[...]
Successfully installed tox-3.13.2 tox-conda-0.2.0
$ toxbase/bin/tox --version
3.13.1 imported from /home/ob/tmp/toxbase/lib/python3.6/site-packages/tox/__init__.py
registered plugins:
tox-conda-0.2.0 at /home/ob/tmp/toxbase/lib/python3.6/site-packages/tox_conda/plugin.py
从那时起,tox 可以用作命令行工具,并通过在toxbase
virtualenv 中升级它来保持最新状态。另一种更自动化的方法是使用pipx
虽然 tox 不能使用 conda,但您可以使用 conda 在 tox 可以找到它们的地方“安装”不同的 Python 版本(就像它会在这些文件夹中找到“正常”的 Python 安装一样)。以下是在 Windows 上测试的:
- 您需要在根 conda 环境中
virtualenv
安装 via 。pip
我怀疑这是 tox 将使用的 virtualenv。(我必须安装 virtualenv usingpip install virtualenv
才能使virtualenv
命令正常工作,即使它conda list
显示为已安装。) - 安装要测试的 Python 版本。这很容易使用
conda create
.tox
将在 Windows 上自动检测 Python 二进制文件C:\python27
,C:\python33
等,因此使用conda create -p C:\python27 python=2.7
等创建环境。
是的,您需要virtualenv
安装的 conda 版本才能正常工作。
尝试执行:
conda install virtualenv
virtualenv 15.1.0 py36_
切换到包含tox.ini
并执行的项目目录:
tox
我通过以下方式使 tox 和 conda 在 Windows 中协同工作:
virtualenv
在我使用的环境中使用 conda安装tox
:conda install virtualenv
从 C:\PythonXY创建“目录连接”符号链接到我的实际环境路径。这绕过了
InterpreterNotFound
-error:mklink /J C:\PythonXY C:\real\path\to\myPythonXYenv
我在 E:\Anaconda3\ 中安装了 Anaconda,在E:\Anaconda3\envs\中安装了我的所有环境,例如E:\Anaconda3\envs\py27\
(请参阅下面的脚本,以使其快速简便。)
第 1 步- 使用 conda 创建环境:
E:\dev> conda create -n py27 python=2.7 --yes
E:\dev> conda create -n py33 python=3.3 --yes
...
E:\dev> conda create -n py36 python=3.6 --yes
第 2 步- 创建所有符号链接:
E:\dev> mklink /J C:\Python27 E:\Anaconda3\envs\py27
E:\dev> mklink /J C:\Python33 E:\Anaconda3\envs\py33
...
E:\dev> mklink /J C:\Python36 E:\Anaconda3\envs\py36
注意:我conda create
从 E 驱动器上的目录调用,因此不需要--prefix
/选项就可以在E:\Anaconda3\envs\中安装新环境。-p
更简单的方法:
无需为每个环境/python版本进行繁琐的设置过程,可以使用ToxEnvMatcher
以这种方式进一步添加的-class:
my_envs = os.path.join('E:\\', 'Anaconda3', 'envs')
tem = ToxEnvMatcher(my_envs)
for version in '27,34,35,36'.split(','):
tem.make(version)
编辑:为了使脚本更易于使用,我在文件中添加了一个新部分,(这里假定为tox_with_conda.py,)因此可以从 cmd.exe 调用它:
C:\dev> python tox_with_conda.py E:\Anaconda3\envs 27 34 35 36 37
编辑 2:也可以使用 pip: 安装pip install tox_with_conda
并用作:
C:\dev> python -m tox_with_conda E:\Anaconda3\envs 27 34 35 36 37
我正在使用 Python 3.6.3 和 tox 2.9.1,但我不知道早期版本何时/是否也可以工作。
防御:我认为对于某些人来说,这似乎是一个过于繁琐的过程(但实际上并非如此),或者对于很多黑客来说。但请记住,能够使用 Anaconda/conda 还可以减少尝试安装库、包、++++ 所花费的时间。
请注意:
- 我将 tox 与 pytest 一起使用,并没有注意到对我的测试有任何影响。
- 我的测试很简单,而且我可能还没有遇到问题。
- 假设,有些事情我没有想到可能与其他人相关。
课程(也可在此处获得):
from subprocess import run
from os.path import join
DEFAULT_BASE = join('C:\\', 'Python')
class ToxEnvMatcher:
"""
Utility to make conda environments work with tox.
Conda envs might be in other locations than where `tox <https://tox.readthedocs.io>`_ expects them to be.
A symbolic link 'Directory Junction' is created from expected location to the actual location.
Intended for Windows to get around the ``InterpreterNotFound``-error.
E.g.: tox expects to find Python 2.7 in ``C:\Python27``,
but may actually be installed in another drive and location.
Examples of use:
.. code-block:: python
my_envs = join('E:\\', 'Anaconda3', 'envs')
tem = ToxEnvMatcher(my_envs)
for version in '27,34,35,36'.split(','):
tem.make(version)
The class is utilized through ``argsparse`` so it can also be used from cmd.exe.
Examples of use of th of using ``ToxEnvMatcher`` from cmd.exe:
.. code-block:: none
E:\dev> tox_with_conda.py E:\Anaconda3\envs 27 34 35 36 37
It's possible to use the ``-b``/``--base`` option to override the default base location (``C:\Python``):
.. code-block:: none
E:\dev> tox_with_conda.py E:\Anaconda3\envs 27 34 35 36 37 --base D:\Python
:param str envs_dir: The path to where new conda environments will be created
:param str default_base: The base of the 'default' location. Usually it's ``C:\Python``
"""
def __init__(self, envs_dir, default_base=DEFAULT_BASE):
self.envs_dir = envs_dir
self.default_base = default_base
def __repr__(self):
return '{}({})'.format(self.__class__.__name__, self.envs_dir)
def make(self, version):
"""
Take version and create conda environment with symlink from 'default tox location'.
E.g.: given version='27' and environment folder ``{self.envs_dir}``:
- ``conda create -p {self.envs_dir}\py27 python=2.7``
- ``mklink /J C:\Python27 {self.envs_dir}\py27``
:param str version: A string on the form 'XY', e.g. '27' or '36'
:return: None
:rtype: NoneType
"""
if len(version) != 2 or not int(version):
raise ValueError("Parameter 'version' must be on the form 'XY', and not '{}'".format(version))
conda_cmd = self._create_cmd_args(version)
symlink_cmd = self._create_symlink_args(version)
run(conda_cmd, shell=True)
run(symlink_cmd, shell=True)
def _get_env_folder(self, version):
return join(self.envs_dir, 'py{}'.format(version))
def _create_cmd_args(self, version):
env_dir = self._get_env_folder(version)
python_version = '.'.join(version)
conda_create = 'conda create -p {} python={} --yes'.format(env_dir, python_version)
return conda_create.split(' ')
def _create_symlink_args(self, version):
env_dir = self._get_env_folder(version)
return 'mklink /J {}{} {}'.format(self.default_base, version, env_dir).split(' ')
使其从 cmd 工作的添加代码是:
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument("env_dir",
help="The folder where conda environments should be installed.")
parser.add_argument("versions", nargs='*',
help="The list of versions, formatted 'XY' where X is major and Y minor. E.g. '27 35 36'")
parser.add_argument("-b", "--base", default=DEFAULT_BASE,
help="Base of the path which tox expects to find Python installed. "
"Default: {}.".format(DEFAULT_BASE))
args = parser.parse_args()
print('env_dir: ', args.env_dir)
print('versions: ', args.versions)
print('--base: ', args.base)
tem = ToxEnvMatcher(args.env_dir, default_base=args.base)
for version in args.versions:
tem.make(version)
我不知道它的发展程度,但你可以看看https://github.com/hayd/ctox。