Cobertura en toxinas para múltiples versiones de Python

8

Aquí hay un enlace a un proyecto y un resultado que puede usar para reproducir el problema que describo a continuación.

Estoy usando cobertura con tox contra múltiples versiones de python. Mi archivo tox.ini se parece a esto:

[tox]
envlist =
    py27
    py34

[testenv]
deps =
    coverage

commands =
    coverage run --source=modules/ -m pytest
    coverage report -m

Mi problema es que la cobertura se ejecutará usando solo una versión de python (en mi caso, py27), no tanto py27 como py34. Este es un problema cada vez que tengo una ejecución de código que depende de la versión de Python, por ejemplo:

def add(a, b):
    import sys
    if sys.version.startswith('2.7'):
        print('2.7')
    if sys.version.startswith('3'):
        print('3')
    return a + b

La ejecución de la cobertura contra el código anterior informará incorrectamente que la línea 6 ("print ('3')") está "Falta" tanto para py27 como para py34. Solo debería faltar para py34.

Sé por qué sucede esto: la cobertura está instalada en mi sistema operativo base (que usa python2.7). Por lo tanto, cuando se ejecuta tox , se da cuenta de que la cobertura ya está instalada y hereda la cobertura del sistema operativo base en lugar de instalarlo en el virtualenv que crea.

Esto está bien para py27, pero provoca resultados incorrectos en el informe de cobertura de py34. Tengo una solución temporal hacky: Necesito una versión de cobertura ligeramente anterior (en relación con la instalada en mi sistema operativo base) para que tox se vea obligado a instalar una copia separada de la cobertura en el virtualenv. P.ej

[testenv]
deps =
    coverage==4.0.2
    pytest==2.9.0
    py==1.4.30

No me gusta esta solución, pero es la mejor que he encontrado por ahora. ¿Alguna sugerencia sobre una forma de obligar a tox a instalar la versión actual de la cobertura en sus virtualenv, incluso cuando ya la tengo instalada en mi sistema operativo base?

4
  • No puedo reproducir esto: la cobertura me dice que falta la línea 8 para el env py27 y la línea 6 para el env py35. Tengo un coveragecomando global instalado (un script de Python 3.5). La única diferencia es que agregué pytestcomo una dependencia adicional, porque de lo contrario obtengo un InvocationError(no tengo un pytestcomando global instalado).
    user707650
    14 de abril de 2016 a las 1:46
  • Gracias por intentarlo. He subido tanto el código que debería poder reproducir este problema como el resultado de ejecutar tox (y otras herramientas) para ayudar con la depuración. Este contenido se encuentra aquí . Estoy ejecutando esto en OS X 10.11.4. Simplemente ejecute "tox" en el directorio "test_project" y (dedos cruzados) debería ver resultados similares a los míos.
    bfrizb
    14/04/2016 a las 16:48
  • Extraño, Tox virtualenv no debería heredar los paquetes del sitio de forma predeterminada. Marque la sitepackagesopción Tox , tal vez esté configurada en alguna parte. ¿O, tal vez, tiene algunos virtualenv realmente antiguos (las versiones antiguas del IIRC se requieren --no-site-packagesexplícitamente)? 16 de abril de 2016 a las 1:22
  • Intenté agregar sitepackages = False a tox.ini, pero no tuve suerte (tiene sentido, ya que False es el valor predeterminado). Dudo que este problema se deba a un antiguo virtualenv ya que creé este proyecto y su virtualenv en las últimas 2 semanas. En mi (aunque limitada) experiencia, siempre he visto módulos / paquetes de uso de toxinas en las máquinas host, en lugar de instalar una copia separada, si satisfacen los requisitos de virtualenv. ¿Puede vincular a un proyecto de ejemplo donde este no sea el caso?
    bfrizb
    18/04/2016 a las 23:13
9

Hoy me encontré con este problema, pero no pude encontrar una respuesta fácil. Entonces, para referencia futura, aquí está la solución que se me ocurrió.

  1. Cree un envlistque contenga cada versión de Python que se probará y un env personalizado para cov.
  2. Para todas las versiones de Python, configure COVERAGE_FILEla variable de entorno para almacenar el .coveragearchivo {envdir}.
  3. Para el covenv, utilizo dos comandos.
    1. coverage combine que combina los informes y
    2. coverage html para generar el informe y, si es necesario, suspender la prueba.
  4. Cree un .coveragercarchivo que contenga una [paths]sección para enumerar las source=ubicaciones.
    1. La primera línea es donde se encuentra el código fuente real.
    2. Las líneas siguientes son los subtrayectos que serán eliminados por la "combinación de cobertura".

tox.ini:

[tox]
envlist=py27,py36,py35,py34,py33,cov

[testenv]
deps=
    pytest
    pytest-cov
    pytest-xdist
setenv=
    py{27,36,35,34,33}: COVERAGE_FILE={envdir}/.coverage
commands=
    py{27,36,35,34,33}: python -m pytest --cov=my_project  --cov-report=term-missing --no-cov-on-fail
    cov: /usr/bin/env bash -c '{envpython} -m coverage combine {toxworkdir}/py*/.coverage'
    cov: coverage html --fail-under=85

.coveragerc:

[paths]
source=
    src/
    .tox/py*/lib/python*/site-packages/

La parte más peculiar de la configuración es la invocación de coverage combine. Aquí hay un desglose del comando:

  • toxno maneja expansiones de Shell {toxworkdir}/py*/.coverage, por lo que necesitamos invocar un shell ( bash -c) para obtener la expansión necesaria.
    • Si uno estuviera inclinado, podría simplemente escribir todas las rutas individualmente y no saltar a través de todos estos aros, pero eso agregaría mantenimiento y .coveragedependencia de archivos para cada pyNNenv.
  • /usr/bin/env bash -c '...'para asegurarnos de obtener la versión correcta de bash. El uso de la ruta completa para envevitar la necesidad de configurar whitelist_externals.
  • '{envpython} -m coverage ...'asegura que invocamos el correcto pythony coveragepara el covenv.
  • NOTA: El problema desafortunado de esta solución es que el covenv depende de py{27,36,35,34,33}cuya invocación tiene algunos efectos secundarios no tan deseables.
    • Mi sugerencia sería invocar solo a covtravés tox.
    • Nunca invoques tox -ecovporque, tampoco
      • Es probable que falle debido a que falta un .coveragearchivo o
      • Podría dar resultados extraños (combinando diferentes pruebas).
    • Si debe invocarlo como un subconjunto ( tox -epy27,py36,cov), borre el .toxdirectorio primero ( rm -rf .tox) para evitar el .coverageproblema del archivo faltante .
4
  • 1
    Útil, sin embargo, confiar en bash no es útil. En lugar de usarlo COVERAGE_FILE=.coverage.{envname}, puede cambiar coverage combine {toxworkdir}/py*/.coveragea solo coverage combine. 10 de mayo de 2019 a las 4:28
  • @Peilonrayz Probé tu enfoque. coverage combinecuando es llamado por tox siempre fallará sin que se encuentren datos, mientras que "simplemente funciona" cuando lo llamo desde la CLi. 25 feb a las 19:35
  • @ user1129682 Lo siento, ha pasado un tiempo desde que utilicé Tox, y la depuración en los comentarios no es buena. ¿Las pruebas construyen los .coverage.{envname}archivos deseados con Tox? (Suena como un sí) ¿Usted o Tox cambian el PWD para el entorno de la cosechadora? (puede intentar ejecutar pwden el entorno de combinación y comparar lo que obtiene con lo que espera, luego podría intentar echo {toxinidir}comparar). Supongo coverage combine {toxinidir}que funcionaría. Si la respuesta anterior funciona, no arregle algo que no esté roto;) 25 feb a las 21:37
  • @Peilonrayz lo arreglé. Estuviste en el clavo. Pensé que podría proporcionar un directorio coverage combiney escanearía cada archivo y descubriría datos de cobertura. La verdad es que coveragetiene un patrón, BASADO EN EL NOMBRE DEL ARCHIVO DE SALIDA, para los archivos aceptados y simplemente aplica ese patrón al directorio. Arreglé el nombre de mi archivo de salida y todo encajó en su lugar. 26 feb a las 9:43
1

No entiendo por qué toxina no instalaría la cobertura en cada virtualenv correctamente. Debería obtener dos informes de cobertura diferentes, uno para py27 y otro para py35. Una mejor opción podría ser producir un informe combinado. Úselo coverage run -ppara registrar datos separados para cada ejecución y luego coverage combinecombinarlos antes de informar.

2
  • Tengo entendido que tox verifica el archivo de requisitos (-dev) .txt con lo que ya está instalado en la máquina host. Si los módulos ya están instalados en el host, no se molesta en instalarlos en virtualenv ya que eso consumiría más espacio en disco. Eche un vistazo al archivo de registro de toxinas (py27-1.log) en el enlace que publiqué . (continúa más abajo ...)
    bfrizb
    18/04/2016 a las 23:03
  • (continuación) Verá que dice "Requisito ya satisfecho (use --upgrade para actualizar): cobertura en /Library/Python/2.7/site-packages", lo que básicamente significa que tox dijo "Veo que ya tiene la cobertura instalada en su host, por lo que no lo instalaré en el virtualenv; usaré la cobertura de su host en su lugar ". Esta línea en el archivo de registro cambia si cambio el requisito a cobertura == 4.0.2. Luego, tox dice "Hmmm, tienes la cobertura v4.0.3 instalada en la máquina host, y eso no satisface tener instalada la v4.0.2, así que seguiré adelante con una cobertura de instalación v4.0.2 en los virtualenv".
    bfrizb
    18/04/2016 a las 23:03