Xiphias
Xiphias

Reputation: 4716

Installing and testing a python project in travis

I have a file structure like this:

./
    project_name/
        __init__.py
    setup.py
    tests/
    .travis.yml

So I run py.test in my .travis.yml. Depending on how I install the package, it either works or I encounter an error.

If I install the package with pip install -e ., it's all fine, but this has the disadvantage of not being a realistic install scenario.

If I install the package with pip install ., it is installed as it would be on another machine in non-dev mode. However, a problem arises: When I run pytest, my tests use import project_name. Then, python imports locally from the directory instead of using the installed package, with leads to an ImportMismatchError.

What would be the best way to deal with this?

Upvotes: 2

Views: 244

Answers (2)

hoefling
hoefling

Reputation: 66501

If you want to run the tests on installed code, you need an intermediary directory (not package) containing all your source code that will be installed; the common name for that being simply src. Sample layout:

project_root
├── src
│   ├── spam
│   │   ├── __init__.py
│   │   └── eggs.py
│   └── ...
├── tests
│   ├── test_spam.py
│   └── ...
└── setup.py

Adjusting the setup script to honor the src dir:

# setup.py

from setuptools import setup, find_packages

setup(
    name='spam',
    ...
    packages=find_packages('src'),
    package_dir={'': 'src'},
    ...
)

With this layout, although the current dir (project_root) is still added to sys.path, all the packages are hidden from importing. This way, you are forced to install your package to be able to invoke the tests and are always testing the installed code. Usually, you install the package in development mode via pip install --editable . on your local machine while writing code, and install the package via pip install . on CI server and the tests will be executed over an actual package installation. Another advantage is that this layout doesn't allow you to import your package's source code in setup script, you don't run into a chicken-egg problem when installing (to install your code, your code must be installed already).

Should you have loose modules, I prefer globbing with pathlib:

py_modules=[p.name for p in pathlib.Path('src').glob('*.py')]

Should you still need Python 2 compatibility, a solution could look like:

py_modules=[os.path.splitext(os.path.basename(p))[0] for p in glob.glob("src/*.py")]

Upvotes: 1

Charles Tapley Hoyt
Charles Tapley Hoyt

Reputation: 28

One solution might be to make a file called tox.ini at the top level to use with tox. This is a tool that takes care of building environments - it's like make but more specific for python stuff. If you make a tox.ini:

[tox]
envlist = py

[testenv]
commands = pytest tests
deps =
    pytest

then install tox with pip3 install tox, then just the command tox from in the command line, it will take care of installing the package and running the tests in an isolated virtual environment.

Then, you can do this inside your travis.yml:

language: python
python:
  - 3.6
install:
  - pip install tox
script:
  - tox

And Travis will use tox to run the tests in a more reproducible way.

Upvotes: 1

Related Questions