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

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?

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)

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:
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.
... 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:
Configure how setuptools looks for test suites.
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

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

Related

pytest cannot locate dynamically created module

I have a directory structure like what follows:
package_root/lib/package_name/foo.py
in foo.py I have a function that creates a file (bar.py) that contains a function (f). foo.py also has a function that then imports bar and runs bar.f().
When I state, within foo.py "import bar", it works, and I have access to bar.f and it runs just fine.
However, this is not the case when running pytest. We run pytest from package_root and it cannot find the module bar when it attempts to import it. This is because (I believe) when running pytest, it creates bar.py in /package_root which contains no init.py file. Since our tests run automatically for our cicd pipeline, I need it to be able to properly import when running pytest from package_root. Any suggestions?
As far as I comprehend from your question is that you are facing imports issue in your pipeline(Correct me if am wrong). In pytest, normally your framework should contain the pytest.ini/tox.ini along with all the test scripts. Please refer link(http://doc.pytest.org/en/latest/customize.html).
Create a file pytest.ini/tox.ini in your framework design from where you are running your code(in your case in the package_root/ directory).
#pytest.ini
[pytest]
python_paths = . lib/<package-name>/

__init__.py code called twice and its significance with package import

I have a simple python project fro learning with two files __init__.py and __main__.py
When I executed python -m pkg_name
it runs both __init__.py and __main__.py
When I execute python -m pkg_name.__init__.py
it invokes __init__.py twice.
I want to know why __init__.py is called twice when i call __init__.py
Is it like the static code in java where when we call the class all the data
in static code is automatically triggered.
What is the relevance of __init__.py in python and benefits of it getting executed when package is imported/loaded or called for processing.
Please help me understand the concepts better.
"""Run a sequence of programs, testing python code __main__ variable
Each program (except the first) receives standard output of the
previous program on its standard input, by default. There are several
alternate ways of passing data between programs.
"""
def _launch():
print('Pipeline Launched')
if __name__ == '__main__':
print('This module is running as the main module!')
_launch()
> __init__.py
"""This is the __init__.py file of pipleline package
Used for testing the import statements.
"""
print(__name__)
print('This is the __init__.py file of pipleline package')
print('Exiting __init__ of pipeline package after all initialization')
The following command is used to execute a Python module or package:
python -m module
Where module is the name of the module/package without .py extension.
if the name matches a script, it is byte-compiled and executed,
if the name matches a directory with a __init__.py file and a __main__.py file, the directory is considered as being a Python package and is loaded first. Then the __main__.py script is executed.
if the name contains dots, e.g.: "mylib.mypkg.mymodule", Python browse each package and sub-package matching the dotted name and load it. Then it execute the last module (or last package which must contain a __main__.py file).
A (short) description is done in the official documentation: 29.4. main — Top-level script environment.
Your problem
If you run this command:
python -m pkg_name
It loads (and run) the __init__.py and __main__.py: this is the normal behavior.
If you run this command:
python -m pkg_name.__init__.py
It should fail if you leave the ".py" extension.
If it runs, the command loads the pkg_name package first: it execute the __init__.py first. Then it runs it again.
It is used to define a folder as a package, which contains required modules and resources.
You can use is as an empty file or add docs about the package or setup initial conditions for the module.
Please checkout the python documentation.
Also, as mentioned by Natecat, __init__.py gets executed whenever you load a package. That's why when you explicitly call __init__.py, it loads the package (1st load) then executes __init__.py (2nd load).

How do I tell pbr to use pytest when setup.py test command is invoked?

While using pbr to simplify Python packaging what do we need to configure in order to make it use pytest when python setup.py test command is executed.
Running pytest works without any problems.
In setup.py:
setup(
setup_requires=['pbr>=1.9', 'setuptools>=17.1', 'pytest-runner'],
pbr=True,
)
In setup.cfg (after standard pbr config):
[aliases]
test=pytest
In test-requirements.txt (same directory as requirements.txt):
pytest
If your tests are outside the application code, you will also need to specify your test directory using addopts in setup.cfg. For example, if your directory structure looks like the first example on this page, you should have
[tool:pytest]
addopts = tests

How to run tests without installing package?

I have some Python package and some tests. The files are layed out following http://pytest.org/latest/goodpractices.html#choosing-a-test-layout-import-rules
Putting tests into an extra directory outside your actual application
code, useful if you have many functional tests or for other reasons
want to keep tests separate from actual application code (often a good
idea):
setup.py # your distutils/setuptools Python package metadata
mypkg/
__init__.py
appmodule.py
tests/
test_app.py
My problem is, when I run the tests py.test, I get an error
ImportError: No module named 'mypkg'
I can solve this by installing the package python setup.py install but this means the tests run against the installed package, not the local one, which makes development very tedious. Whenever I make a change and want to run the tests, I need to reinstall, else I am testing the old code.
What can I do?
I know this question has been already closed, but a simple way I often use is to call pytest via python -m, from the root (the parent of the package).
$ python -m pytest tests
This works because -m option adds the current directory to the python path, and hence mypkg is detected as a local package (not as the installed).
See:
https://docs.pytest.org/en/latest/usage.html#calling-pytest-through-python-m-pytest
The normal approach for development is to use a virtualenv and use pip install -e . in the virtualenv (this is almost equivalent to python setup.py develop). Now your source directory is used as installed package on sys.path.
There are of course a bunch of other ways to get your package on sys.path for testing, see Ensuring py.test includes the application directory in sys.path for a question with a more complete answer for this exact same problem.
On my side, while developing, I prefer to run tests from the IDE (using a runner extension) rather than using the command line. However, before pushing my code or prior to a release, I like to use the command line.
Here is a way to deal with this issue, allowing you to run tests from both the test runner used by your IDE and the command line.
My setup:
IDE: Visual Studio Code
Testing: pytest
Extension (test runner): https://marketplace.visualstudio.com/items?itemName=LittleFoxTeam.vscode-python-test-adapter
Work directory structure (my solution should be easily adaptable to your context):
project_folder/
src/
mypkg/
__init__.py
appmodule.py
tests/
mypkg/
appmodule_test.py
pytest.ini <- Use so pytest can locate pkgs from ./src
.env <- Use so VsCode and its extention can locate pkgs from ./src
.env:
PYTHONPATH="${PYTHONPATH};./src;"
pytest.ini (tried with pytest 7.1.2):
[pytest]
pythonpath = . src
./src/mypkg/appmodule.py:
def i_hate_configuring_python():
return "Finally..."
./tests/mypkg/appmodule_test.py:
from mypkg import app_module
def test_demo():
print(app_module.i_hate_configuring_python())
This should do the trick
Import the package using from .. import mypkg. For this to work you will need to add (empty) __init__.py files to the tests directory and the containing directory. py.test should take care of the rest.

Does unittest allow single case/suite testing through "setup.py test"?

I'm a newbie when it comes to python unit testing, but I'm eager to learn!
I just read python setup.py test can run all suites derived from unittest classes. I wonder if I also can use setup.py to run a single suite and/or a single test case, maybe adding some modifier to the previous command like python setup.py tests suitename. If so, can you please point me to any docs/examples?
You guys are all wrong, setup.py test can be used with the -s option the same way python -m unittest does:
cd root_of_your_package
python setup.py test -s tests.TestClass.test_method
The setup.py test runner is rather limited; it only supports letting you specify a specific module. The documentation for the command-line switches is given when you use the --help switch:
python setup.py test --help
Common commands: (see '--help-commands' for more)
[ ... cut ... ]
Options for 'test' command:
--test-module (-m) Run 'test_suite' in specified module
--test-suite (-s) Test suite to run (e.g. 'some_module.test_suite')
[ ... more cut ... ]
so python setup.py test -m your.package.tests.test_module would limit running the tests from the test_module.py file only.
All the test command does, really, is make sure your egg has been built already, extract the test_suite value from setup() metadata, configure a test loader that understands about zipped eggs, then run the unittest.main() function.
If you need to run a single test only, have already built your egg, are not running this with a zipped egg, then you can also just use the unittest command line interface, which does pretty much everything else:
python -m unittest yourpackage.tests.TestClass.test_method
would instruct unittest to only run a very specific test method.
setup.py test
setup.py test is not that flexible, but here's an alternative:
The unittest module can run specific test methods
From the Documentation on unittest
The unittest module can be used from the command line to run tests from modules, classes or even individual test methods:
python -m unittest test_module1 test_module2
python -m unittest test_module.TestClass
python -m unittest test_module.TestClass.test_method
You can pass in a list with any combination of module names, and fully qualified class or method names.
You can run tests with more detail (higher verbosity) by passing in the -v flag:
python -m unittest -v test_module
For a list of all the command-line options:
python -m unittest -h
in case of pytest run specific test:
python setup.py test --addopts tests/jobs/test_data_monitoring.py::TestDataMonitoring::test_dataframe__bulk_update
In case you use pytest for unit testing you can run a file directly by avoiding the setup.py file.
Running all tests for me is:
python setup.py test
Running a specific test:
pytest --pyargs path/to/test.py
Hope it helps.
(I know this post is already a bit old, but I came here looking for running a way to run pytest tests alone, figured it might be of use to someone)

Categories