Im new to python and have looked at various stack overflow posts. i feel like this should work but it doesnt. How do you import a class from another file in python?
This folder structure
src/example/ClassExample
src/test/ClassExampleTest
I have this class
class ClassExample:
def __init__(self):
pass
def helloWorld(self):
return "Hello World!"
I have this test class
import unittest
from example import ClassExample
class ClassExampleTest(unittest.TestCase):
def test_HelloWorld(self):
hello = ClassExample()
self.assertEqual("Hello World!", hello.helloWorld())
if __name__ == '__main__':
unittest.main()
When the unit test runs the object is None:
AttributeError: 'NoneType' object has no attribute 'helloWorld'
What's wrong with this? How do you import a class in python?
If you're using Python 3, then imports are absolute by default. This means that import example will look for an absolute package named example, somewhere in the module search path.
So instead, you probably want a relative import. This is useful when you want to import a module that is relative the module doing the importing. In this case:
from ..example.ClassExample import ClassExample
I'm assuming that your folders are Python packages, meaning that they contain __init__.py files. So your directory structure would look like this.
src
|-- __init__.py
|-- example
| |-- __init__.py
| |-- ClassExample.py
|-- test
| |-- __init__.py
| |-- ClassExampleTest.py
Related
I'm implementing a class which uses a can bus instance (Bus instance is static because all object should use the same).
# ./mymodule/__init__.py
import can
class UsingCanBUS:
_bus = can.ThreadSafeBus(channel='can0', bustype='socketcan')
def __init__(self) -> None:
# other code
# v here every object interacts with the bus
self._listener = can.Listener()
self._listener.on_message_received = self._handle_message
def _send_message(self, id, data) -> bool:
msg = can.Message(arbitration_id=id, data=data, extended_id=False)
try:
self._bus.send(msg)
except can.CanError:
return False
else:
return True
This code will eventually run on a raspberry so the can interface is correctly setup in the system.
Now, if I want to unit test any of the class methods or any file in the module the bus tries to initialize and, since I'm not on the target system, it throws an os error (which is to be expected).
The folder structure is as follows:
.
|- mymodule/
| |- __init__.py
| |- utils.py
|
|- tests/
| |- __init__.py
| |- test_utils/
| | |- __init__.py
| | |- test_utils.py
It's not clear to me how I should test this piece of code.
I tried patching the can module:
#./tests/test_utils/test_utils.py
import pytest
from unittest.mock import patch
#patch('mymodule.can')
def test_something():
from mymodule.utils import some_function
# This doesn't work as the real python-can methods get called instead of the mocked ones
assert some_function() == expected_result
I don't understand if I'm using the patch decorator wrong or if my approach is completely off course.
I expect every class in the can module imported from mymodule to be patched with mocked classes but it doesn't seem like it.
The Raspberry Pi doesn't come with the CAN driver so you can't directly install the can-utils and simulate the virtual CAN. Use the CAN transceiver on top of Raspberry Pi. You could go with this particular one, which I'm also using for the simulations.
RS485-CAN-HAT
I'm fairly new to Python. I'm working on a small Python project, structured as so:
artwork_grabber/
|
|-- artwork_grabber/
| |-- __init__.py
| |-- helpers.py
|
|-- tests/
| |-- __init__.py
| |-- test_module.py
|
|-- README
Both __init__.py files are empty of any content.
The helpers.py file contains a few functions, one of which is as follows:
from os import path
from tinytag import TinyTag
def create_search_term(file_path_to_song):
"""
Takes in a file path to a song and returns a phrase that will be used to search for the song's corresponding album artwork.
:param file_path_to_song: a file path to an .mp3 or .m4a file.
:type file_path_to_song: `string`, required.
:return: an object of type string that represents the search term to be used when finding album artwork for song file passed into the function.
:rtype: `string`.
"""
if str(path.isfile(file_path_to_song)):
tag = TinyTag.get(file_path_to_song)
album = tag.get_album()
artist = tag.get_artist()
term = f"{artist} {album} Album Cover"
return term
else:
return False
I would like to write a test for create_search_term() using the Mock Object Library. In the test_module.py function, I have the following:
from unittest import TestCase
from mock import patch
import unittest
from artwork_grabber.helpers import create_search_term
class UnitTests(TestCase):
mock_song_info = {
"album": "A Deeper Understanding",
"artist": "The War On Drugs"
}
# patch where the function is USED, not where it is DEFINED
#mock.patch('artwork_grabber.helpers.create_search_term', return_value=mock_song_info)
def test_create_search_term(self, mock_song):
actual_result = create_search_term(mock_song_info)
expected_result = "A Deeper Understanding The War On Drugs Album Cover"
self.assertEqual(actual_result, expected_result,
"Expected the search terms to match.")
if __name__ == "__main__":
unittest.main()
The problem is that when I run python test_module.py from the terminal (where pwd outputs /path/to/artwork_grabber/tests), I get the following error:
Traceback (most recent call last):
File "test_module.py", line 5, in <module>
from artwork_grabber.artwork_grabber import create_search_term
ModuleNotFoundError: No module named 'artwork_grabber'
Any idea what I might be missing? I've viewed several tutorials on using Mock, but they haven't seemed to help.
As documented in The Module Search Path:
When a module named spam is imported, the interpreter first searches for a built-in module with that name. If not found, it then searches for a file named spam.py in a list of directories given by the variable sys.path. sys.path is initialized from these locations:
The directory containing the input script (or the current directory when no file is specified).
PYTHONPATH (a list of directory names, with the same syntax as the shell variable PATH).
...
So in your case, the path that python would look for your modules is at /path/to/artwork_grabber/tests which obviously doesn't contain any artwork_grabber/helpers.py since it only contains an __init__.py and test_module.py. To satisfy either of the 2 ways above as documented, do either of the following:
Go to the parent folder cd /path/to/artwork_grabber and then execute the test python tests/test_module.py. This will satisfy the 1st above which will include the current path thus including artwork_grabber/helpers.py and the subdirectories and files in it.
Define it in the variable export PYTHONPATH=${PYTHONPATH}:/path/to/artwork_grabber to satisfy the 2nd above.
Note that as pointed out by #MatiasCicero it wouldn't make sense to mock function-x to return y and assert that function-x returned y, the point of testing is to test source code, not to test the test if it correctly did the mock. Ideally, you should run the actual create_search_term and pass it a test file file_path_to_song.
I am creating a Python package/library. My directory structure looks like this:
my_package/
|-- my_package/
| |-- tests/
| | |-- __init__.py
| | |-- my_tests.py
| |
| |-- __init__.py
| |-- main.py
|
|-- setup.py
I have all my functions in the main.py file:
def sum_nums(a,b):
res = a + b
return(res)
def mult_nums(a,b):
res = a * b
return(res)
def sub_nums(a,b):
res = a - b
return(res)
my_tests.py looks like this:
from unittest import TestCase
import my_package
def test_sum():
assert sum_nums(3,4) == 7
def test_mult():
assert mult_nums(3,4) == 12
def test_sub():
assert sub_nums(3,4) == -1
When I run my tests from the package root directory as follows:
python setup.py test
... I get the following error:
NameError: name 'sum_nums' is not defined
Is my package directory structure correct?
Am I missing an _ init _.py file?
Does every directory require an _ init _.py file?
Is it okay to place all my functions inside a single main.py file
without using if name == "main"?
You need to indicate that the functions under test came for the my_package package like:
from unittest import TestCase
import my_package
def test_sum():
assert my_package.sum_nums(3,4) == 7
def test_mult():
assert my_package.mult_nums(3,4) == 12
def test_sub():
assert my_package.sub_nums(3,4) == -1
tests should not be in the package so move it up one dir. See sanic or any other module in github for example. Your functions need to be available in init.py. You can import them like is done in sanic.
https://github.com/huge-success/sanic
You also need
from my_package import sum_nums, mult_nums, sub_nums
Or prefix with my_package.
I have this structure:
myApp
|---> module
| |---> __init__.py
| |---> constants.py
| |---> job.py
|---> processor.py
job.py has a Job class that imports some constants in constants.py
import constants
class Job:
def __init__(self, id):
self.id = id
self.status = constants.JOB_WAITING
.
.
.
Then in my processor.py I'm trying to use the Job class.
from module1 import job
j = job.Job(123)
print(j.id)
I'm met with the exception "ModuleNotFoundError: No module named 'constants'" from just the first line "from module1 import job"
A naive solution of adding "from module1 import constants" before that doesn't help. Nor would that be desirable because from the perspective of processor.py it just care about importing job and not worry about importing whatever else job needs.
Is the problem due to when I import job, it then looks for the import constants in the wrong path? Not sure how I fix it if that's the case.
Since you are in the same package, try
from . import constants
I have a python script saved to file.
test1.py
import maya.cmds as cmds
import sys
def process():
print 'working'
I need to run the function from this script inside another python script, inside maya. I have:
import sys
sys.path.append('J:\scripts\src\maya')
from test1 import process
test1.process()
but it gives me:
from test1 import process
# Error: ImportError: file <maya console> line 4: cannot import name process #
What am I doing wrong here?
('import test1' gives no error, so the path is correct).
Solution:
Reload your test1 module, my guess is that you created and imported test1 without the process method inside. To effectively reload a module, you can't just re-import it, you have to use the reload.
reload(test1)
from test1 import process
Other observations:
Use raw string when using paths:
Add r before your path string:
sys.path.append(r'J:\scripts\src\maya')
Python Doc
The backslash () character is used to escape characters that
otherwise have a special meaning, such as newline, backslash itself,
or the quote character. String literals may optionally be prefixed
with a letter 'r' or 'R'; such strings are called raw strings and use
different rules for interpreting backslash escape sequences.
Check the way you import your modules:
You wrote, which is not valid:
from test1 import process
test1.process()
But you can have either way:
import test1
test1.process()
or:
from test1 import process
process()
To sum-up these are the ways to import a module or package:
>>> import test_imports
>>> from test_imports import top_package
>>> from test_imports import top_module
test_imports.top_module
>>> from test_imports.top_package import sub_module
test_imports.top_package.sub_module
assuming you have the following hierarchy:
J:\scripts\src\maya # <-- you are here
.
`-- test_imports
|-- __init__.py
|-- top_package
| |-- __init__.py
| |-- sub_package
| | |-- __init__.py
| | `-- other_module.py
| |-- sub_module.py
`-- top_module.py
Credits goes to Sam & Max blog (French)
First you need to add the script location path in system path.
and if you are making this as a python package than do not forget to add
a __init__.py file in the package directory.
Than you can execute following code.
import sys
path = r'J:\scripts\src\maya'
if path not in sys.path:
sys.path.append(path)
import test1
test1.process()