Doctest and relative imports - python

I'm having trouble using doctest with relative imports. The simple solution is just to get rid of the relative imports. Are there any others?
Say I have a package called example containing 2 files:
example/__init__.py
"""
This package is entirely useless.
>>> arnold = Aardvark()
>>> arnold.talk()
I am an aardvark.
"""
from .A import Aardvark
if __name__ == "__main__":
import doctest
doctest.testmod()
example/A.py
class Aardvark(object):
def talk(self):
print("I am an aardvark.")
If I now attempt
python example/__init__.py
then I get the error
Traceback (most recent call last):
File "example/__init__.py", line 8, in <module>
from .A import Aardvark
ValueError: Attempted relative import in non-package

Create another file my_doctest_runner.py:
if __name__ == "__main__":
import doctest
import example
doctest.testmod(example)
Execute my_doctest_runner.py to run doctests in example/__init__.py:
$ python2.7 my_doctest_runner.py
**********************************************************************
File "/tmp/example/__init__.py", line 4, in example
Failed example:
arnold.talk()
Expected:
I am an aaardvark.
Got:
I am an aardvark.
**********************************************************************
1 items had failures:
1 of 2 in example
***Test Failed*** 1 failures.

Pytest's --doctest-modules flag takes care of relative imports:
$ ls example/
A.py __init__.py
$ pytest --doctest-modules example
==================== test session starts ====================
...
example/__init__.py . [100%]
===================== 1 passed in 0.03s =====================

Just do
from A import Aardvark

Related

How to import a python function from a sibling folder

This question has been asked before. Even though I couldn't get an answer that solves this issue.
I have the following directory and subdirectories:
I have a function hello() in test1.py that I want to import in test2.py.
test1.py:
def hello():
print("hello")
test2.py:
import demoA.test1 as test1
test1.hello()
Output:
Traceback (most recent call last):
File "c:/Users/hasli/Documents/Projects/test/demoB/test2.py", line 1, in <module>
import demoA.test1 as test1
ModuleNotFoundError: No module named 'demoA'
This is exactly as explained in https://www.freecodecamp.org/news/module-not-found-error-in-python-solved/ but I can't access hello()
I am using python 3: Python 3.8.9
You need to add demoA to the list of paths used for import.
import sys
sys.path.append('..')
import demoA.test1 as test1
test1.hello()

Python unittest with test/ and src/ not finding src modules

I have a simple directory structure:
proj/
src/
__init__.py
foo.py
bar.py
test/
__init__.py
test_foo.py
test_foo.py
import unittest
import sys
sys.path.append('../src')
from src import foo
class TestFoo(unittest.TestCase):
def test_foo(self):
foo.start()
if __name__ == '__main__':
unittest.main()
foo.py
import bar
def start():
bar.do_stuff()
When running my test (I'm using vscode), I get the following error:
Failed to import test module: test_foo
Traceback (most recent call last):
File "~/.pyenv/versions/3.8.6/lib/python3.8/unittest/loader.py", line 436, in _find_test_path
module = self._get_module_from_name(name)
File "~/.pyenv/versions/3.8.6/lib/python3.8/unittest/loader.py", line 377, in _get_module_from_name
__import__(name)
File "~/proj/test/test_foo.py", line 6, in <module>
from src import foo
File "~/proj/src/foo.py", line 1, in <module>
import bar
ModuleNotFoundError: No module named 'bar'
I'm not sure why the test can't discover the src/bar when importing src/foo
Instead of import bar try from src import bar.
from src import bar
def start():
bar.do_stuff()
Notice
Keep in mind, that you only adjusted the path in the test. You do not want the tests to work, but the application to fail because you forgot to adjust the path in the application at some point.
What is in your bar.py file? Just the do_stuff() function? Or is the do_stuff() function a method of object bar?
Depending on the answer to the question above, try doing something like this in your foo.py file:
from proj.src.bar import [name of function you're testing here]
In your specific case, it would be like this if do_stuff() is a standalone function:
from proj.src.bar import do_stuff
and then your foo.py file would be:
from proj.src.bar import do_stuff
def stuff():
do_stuff()
However, if do_stuff() is a method of the bar object, it would probably be something like this, although it's hard to tell without knowing the contents of the bar.py file:
from proj.src.bar import bar
and then your foo.py file would be similar to the way you have it now:
from proj.src.bar import bar
def stuff():
bar.do_stuff()
I recently ran into a similar problem when trying to import one neighboring file into another. This link helped me out, too.
Two changes:
from src import bar in foo.py,
before import modules in test_foo.py, add
import sys
sys.path.append("./")
Then you should be able to run code successfully.
In my case, I had imports of modules which are not actually installed, in my bar.py file, such as:
import requests
After installing those modules, VSCode showed tests properly.

How can I write the python import statements, such that the file works from both inside and outside its package? [duplicate]

This question already has answers here:
Relative imports for the billionth time
(12 answers)
Closed 1 year ago.
Here's a file structure I'm working with:
package_example/
main.py
the_package/
__init__.py
pkg_file_1.py
pkg_file_2.py
Here are the files:
# main.py
from the_package.pkg_file_1 import foo_1
def main():
foo_1()
if __name__ == "__main__":
main()
# pkg_file_1.py
from the_package import pkg_file_2
def foo_1():
print("this is the second version, foo_1")
pkg_file_2.foo_2()
if __name__ == "__main__":
foo_1()
# pkg_file_2.py
def foo_2():
print("this is foo_2")
If I run main.py, everything works fine.
But if I run pkg_file_1.py, I get:
Traceback (most recent call last):
File "the_package/pkg_file_1.py", line 1, in <module>
from the_package import pkg_file_2
ModuleNotFoundError: No module named 'the_package'
Obviously in this simplified example, it doesn't matter, since main.py and pkg_file_1.py run the same code.
But practically, I have test scripts baked into my package structure, so I can test the code in my new environment and data. But I'm scratching my head on how I'm supposed write import statements such that I don't get a ModuleNotFoundError from both inside or outside the package.
Edit: From pavel's suggest I tried changing pkg_file_1.py to:
from ..the_package import pkg_file_2
def foo_1():
print("this is the second version, foo_1")
pkg_file_2.foo_2()
if __name__ == "__main__":
foo_1()
But running that same file gives:
Traceback (most recent call last):
File "the_package/pkg_file_1.py", line 1, in <module>
from ..the_package import pkg_file_2
ImportError: attempted relative import with no known parent package
This is why we need relative import.
# pkg_file_1.py
from . import pkg_file_2
...
How to run main.py and pkg_file1.py from package_example dir:
python3 main.py
python3 -m the_package.pkg_file1

Test not being executed from command line

Below test works as expected when I execute from PyCharm but when I attempt to run python TestData.py from command line I receive :
Traceback (most recent call last):
File "TestData.py", line 3, in <module>
from service.DataAccessor import Data
ModuleNotFoundError: No module named 'service'
When I try python -m unittest
I receive:
----------------------------------------------------------------------
Ran 0 tests in 0.000s
OK
Both commands are run from project directory.
Project structure is:
project/service
- DataAccessor.py
project/test
- TestData.py
Code:
TestData.py :
import unittest
from service.DataAccessor import Data
class TestData(unittest.TestCase):
def test_get_data(self):
print(Data.getDf(self))
self.assertEqual(3, 3)
if __name__ == '__main__':
unittest.main()
DataAccessor.py:
import pandas as pd
class Data():
def getDf(self):
df = pd.read_csv("./data.csv")
return df
Coming from Java background I'm not 100% sure why this works but adding an empty __init__.py to the dirs: test & service and executed python -m unittest test.TestData executes the test.

Running python scripts within a subpackage of my package

Having some trouble figuring out the correct, python 2.x preferred way to do relative imports so that I can keep test scripts together in one subpackage, and have those test scripts be able to test my library.
$ farm\testpad\testpad.py
Traceback (most recent call last):
File "C:\farm\testpad\testpad.py", line 4, in <module>
from ..animals.dog import dog
ValueError: Attempted relative import in non-package
$ python -m farm\testpad\testpad
C:\Python27\python.exe: No module named farm\testpad\testpad
In the following example, what do I need to modify to do what I want? Please, and thanks for any help given.
e.g.
Package structure:
farm/
__init__.py # empty
animals/
__init__.py # empty
dog.py
testpad/
__init__.py # empty
testpad.py
dog.py
import sys
import os
class dog():
def __init__(self, name):
self.name = name
def speak(self):
print "woof"
testpad.py
import os
import sys
from ..animals.dog import dog
# create a dog and test its speak action
def testpad():
d = dog("Allen")
d.speak()
if __name__ == "__main__":
testpad()
Three things:
always run your tests scripts from root of your project. This simple rule does not harm anything and will simplify your scenario
prefer absolute imports
-m option for python expects module in dotted form
Applying this to your code
Modify testpad.py
import os
import sys
from farm.animals.dog import dog
# Create a dog and test its speak action
def testpad():
d = dog("Allen")
d.speak()
if __name__ == "__main__":
testpad()
Call it from python -m <module>
$ python -m farm.testpad.testpad
woof
Bonus - testing from nose
For my testing I use following pattern
keep all testing code in project subdir called test or better tests
use nose testing framework
Being in root of your project
Install nose:
$ pip install nose
What creates a command nosetests
Reorganize your code
$ mkdir tests
$ mv farm/testpad/testpad.py tests/test_animals.py
$ rm -rf farm/testpad/
Run the tests (currently just one)
The simples way, wich does the work, but tries to keep the output minimal:
$ nosetests
.
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
As you see, nose is discovering test cases on his own (searching for whatever starts with test)
Make it a bit more verbose:
$ nosetests -v
test_animals.testpad ... ok
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
Now you know, what tests were run.
To see captured outut, use -s switch:
$ nosetests -vs
test_animals.testpad ... woof
ok
----------------------------------------------------------------------
Ran 1 test in 0.003s
OK
Add more tests to test_animals.py
import os
import sys
# create a dog and test its speak action
def test_dog():
from farm.animals.dog import dog
d = dog("Allen")
d.speak()
def test_cat():
from farm.animals.dog import cat
c = cat("Micy")
d.speak()
def test_rabbit():
from farm.animals.dog import rabbit
r = rabbit()
r.speak()
Test it:
$ nosetests -vs
test_animals.test_dog ... woof
ok
test_animals.test_cat ... ERROR
test_animals.test_rabbit ... ERROR
======================================================================
ERROR: test_animals.test_cat
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/javl/Envs/so/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/home/javl/sandbox/so/testpad/tests/test_animals.py", line 12, in test_cat
from farm.animals.dog import cat
ImportError: cannot import name cat
======================================================================
ERROR: test_animals.test_rabbit
----------------------------------------------------------------------
Traceback (most recent call last):
File "/home/javl/Envs/so/local/lib/python2.7/site-packages/nose/case.py", line 197, in runTest
self.test(*self.arg)
File "/home/javl/sandbox/so/testpad/tests/test_animals.py", line 17, in test_rabbit
from farm.animals.dog import rabbit
ImportError: cannot import name rabbit
----------------------------------------------------------------------
Ran 3 tests in 0.004s
FAILED (errors=2)
Add --pdb switch to jump to debugger (if you install ipdbplugin, you might use --ipdb):
$ nosetests -vs --pdb
test_animals.test_dog ... woof
ok
test_animals.test_cat ... > /home/javl/sandbox/so/testpad/tests/test_animals.py(12)test_cat()
-> from farm.animals.dog import cat
(Pdb) l
7 from farm.animals.dog import dog
8 d = dog("Allen")
9 d.speak()
10
11 def test_cat():
12 -> from farm.animals.dog import cat
13 c = cat("Micy")
14 d.speak()
15
16 def test_rabbit():
17 from farm.animals.dog import rabbit
(Pdb)
Use h or find some tutorial for debugger, it is great tool
Real test cases shall assert that behaviour is as expected.
Assert printed values are as expected
As you are printing to stdout, we can capture the output using mocking (in Python 2.x you have to install it, in Python 3.x you from unittest import mock):
$ pip install mock
And modify your test_animals.py:
from mock import patch
from StringIO import StringIO
# create a dog and test its speak action
#patch("sys.stdout", new_callable=StringIO)
def test_dog(mock_stdout):
from farm.animals.dog import Dog
d = Dog("Allen")
d.speak()
assert mock_stdout.getvalue() == "woof\n"
#patch("sys.stdout", new_callable=StringIO)
def test_cat(mock_stdout):
from farm.animals.cat import Cat
c = Cat("Micy")
c.speak()
assert mock_stdout.getvalue() == "mnau\n"
#patch("sys.stdout", new_callable=StringIO)
def test_rabbit(mock_stdout):
from farm.animals.rabbit import Rabbit
r = Rabbit("BB")
r.speak()
assert mock_stdout.getvalue() == "Playboy, Playboy\n"
Final test case of my taste test_mytaste.py
def test_dog():
from farm.animals.dog import Dog
d = Dog("Allen")
sound = d.speak()
assert sound == "woof", "A dog shall say `woof`"
def test_cat():
from farm.animals.cat import Cat
c = Cat("Micy")
sound = c.speak()
assert sound == "mnau", "A cat shall say `mnau`"
def test_rabbit():
from farm.animals.rabbit import Rabbit
r = Rabbit("BB")
sound = r.speak()
assert sound == "Playboy, Playboy", "A Rabbit shall say ..."
This requires you refactor your code (class names starting uppercase, speak method not printing but returning the sound).
In fact, you may start writing your code from test cases and adding tested modules later on, this often leads to nicer design as you start thinking of real use from the very beginning.
Selective call of test cases
nose is great in searching for test cases (generally whatever starts with test), but sometime you might focus on particular test or use differently named python file. You can tell nose to use just one test file:
$ nosetests -vs tests/test_mytaste.py
test_mytaste.test_dog ... ok
test_mytaste.test_cat ... ok
test_mytaste.test_rabbit ... ok
----------------------------------------------------------------------
Ran 3 tests in 0.002s
OK
or even target nose to run specific test from it:
$ nosetests -vs tests/test_mytaste.py:test_dog
test_mytaste.test_dog ... ok
----------------------------------------------------------------------
Ran 1 test in 0.001s
OK

Categories

Resources