Why does running "setup.py test" run my console script?

911 Views Asked by At

My very simple example project contains:

addtest/
  setup.py
  addtest/
    __init__.py
    __main__.py
    app.py

My app.py is just:

def main():
    raise SystemExit("Command line entry point called.")

My __main__.py is just:

from addtest.app import main
main()

My setup.py contains:

from setuptools import setup, find_packages

setup(
    name='AddTest',
    version='1.0',
    packages=find_packages(),

    entry_points={
        'console_scripts': ['addtest = addtest.app:main']
    },
)

I would expect that running python setup.py test would do nothing, since no unit tests are written. However, running it in a clean virtualenv (Python 3.6.6 on Ubuntu 18.04.1) gives me:

$ python setup.py test
running test
running egg_info
writing AddTest.egg-info/PKG-INFO
writing dependency_links to AddTest.egg-info/dependency_links.txt
writing entry points to AddTest.egg-info/entry_points.txt
writing top-level names to AddTest.egg-info/top_level.txt
reading manifest file 'AddTest.egg-info/SOURCES.txt'
writing manifest file 'AddTest.egg-info/SOURCES.txt'
running build_ext
Command line entry point called.

Note the Command line entry point called. which means it's invoking the console script it generates from my __main__.py (or maybe just calling python -m addtest).

Why is setup.py calling the console script when I want it to run tests? Further inspection of the script's execution shows that sys.argv is ['setup.py','test'] - why?

3

There are 3 best solutions below

0
Dragonborn On

I think you have to specify the entry point for test in your setup.py. Otherwise it passes it to the main.py.

0
Leo K On

The test scanner in setuptools will look for tests in any *.py file found in your sub-directories, except for __init__.py. Yes, this includes __main__.py, it will call __import__() on it, causing its main suite to be executed.

If you want to be able to run python -m addtest and have that run your __main__.py code, you may want to add the standard 'run only if this is really main' protection:

if __name__ == "__main__":
   ...

(then, if you do python -m the code will run, but it won't run if the file is loaded by setup.py test)

4
hmedia1 On

The setuptools docs explains how setuptools scans for unit tests by default.

Since, as you say, no unit tests are written or specified, setuptools can be using default behavior.

A couple references from the setuptools docs that elaborates on this as well as specifies how to set up test suites are below:

  1. test_loader If you would like to use a different way of finding tests to run than what setuptools normally uses, you can specify a module name and class name in this argument.
    ...
    The module name and class name must be separated by a :. The default value of this argument is "setuptools.command.test:ScanningLoader". If you want to use the default unittest behavior, you can specify "unittest:TestLoader" as your test_loader argument instead. This will prevent automatic scanning of submodules and subpackages.

  2. ... test_suite A string naming a unittest.TestCase subclass (or a package or module containing one or more of them, or a method of such a subclass), or naming a function that can be called with no arguments and returns a unittest.TestSuite.

    If the named suite is a module, and the module has an additional_tests() function, it is called and the results are added to the tests to be run. If the named suite is a package, any submodules and subpackages are recursively added to the overall test suite.

    Specifying this argument enables use of the test command to run the specified test suite, e.g. via setup.py test. See the section on the test command below for more details.
    ...

         setup(
         # ...
         test_suite="my_package.tests.test_all"
     )

This, in conjunction with another answer on this thread leaves you with at least a couple of options to ensure that python setup.py test doesn't run the console script:

  1. Configure how setuptools looks for test suites.
  2. Add if __name__ == "__main__": around main()

Yet another option may be given by the unit testing library you're using. For example, Pytest has an integration guide for setuptools that replaces the test command with its own.


Regarding the sysargs

sys.argv is ['setup.py','test'] because you invoked python with the args setup.py test