Python 3 import class from subfolder problem - python-3.x

I have a strange problem regarding import classes from subfolders.
I'm using Python 3.6, therefore an __init__.py should be not required in the subfolders.
I have the following file structure:
root
├── script.py (main)
└── custom
├── class1.py
└── class2.py
This is script.py:
from custom.class1 import Class1
from custom.class2 import Class2
if __name__ == '__main__':
cl1 = Class1()
cl2 = Class2()
This is class1.py:
class Class1():
def __init__(self):
print('Class1')
if __name__ == '__main__':
cl1 = Class1()
This is class2.py, which imports also class1:
from class1 import Class1
class Class2():
def __init__(self):
cl1 = Class1()
print('Class2')
if __name__ == '__main__':
cl2 = Class2()
And now the problem:
It works without error, when i am running python class1.py in the custom subfolder.
It works without error, when i am running python class2.py in the custom subfolder.
But when i am running python script.py in the root folder, i get the following error:
Traceback (most recent call last):
File .... in <module>
from custom.class2 import Class2
File .... line 1, in <module>
from class1 import Class1
ModuleNotFoundError: No module named 'class1'
How can this be fixed in a way, that the scripts in the custom subfolders can be run on its own and also the script in the root folder works?

The problem is that you are running custom.class2 inside of script.py, which means while running custom.class2, you are still in the root directory.
To fix this, you should replace from class1 import Class1 from class2.py with from custom.class1 import Class1.
If you need to be able to run the file from any working directory, you may replace it's contents with this:
import os
import sys
path = os.path.abspath(os.path.dirname(__file__))
if not path in sys.path:
sys.path.append(path)
from class1 import Class1
class Class2():
def __init__(self):
cl1 = Class1()
print('Class2')
if __name__ == '__main__':
cl2 = Class2()
The code adds the file's path in to the sys.path list, which holds different paths from which you can import modules.

Related

Is it possible to make a module available as an import from another module?

I'm refactoring some code and have moved around some files. But for backwards compatibility, I would like to make all of my modules keep their old import paths.
my file structure is as follows
--| calcs/
----| __init__.py
----| new_dir
------| new_file1.py
------| new_file2.py
What do I need to do ensure that I can use an import like
import calcs.newfile1.foo
# OR
from calcs.newfile1 import foo
I have tried a few methods of adding the imports to the top level __init__.py file. As is reccommended here
But while this seems to allow an import such as import calcs.newfile1, An import such as import calcs.newfile1.foo raises ModuleNotFoundError: No module named calcs.newfile1
I expect that I need python to recognize calcs.newfile1 as a **module **. At the moment it seems to just be importing it as a class or other object of some sort
The only way i know how to do it is by creating a custom import hook.
Here is the PEP for more information.
If you need some help on how to implement one, i'll suggest you to take a look at the six module,
here
and here
Basically your calcs/__init__.py will become like this:
''' calcs/__init__.py '''
from .new_dir import new_file1, new_file2
import sys
__path__ = []
__package__ = __name__
class CalcsImporter:
def __init__(self, exported_mods):
self.exported_mods = {
f'{__name__}.{key}': value for key, value in exported_mods.items()
}
def find_module(self, fullname, path=None):
if fullname in self.exported_mods:
return self
return None
def load_module(self, fullname):
try:
return sys.modules[fullname]
except KeyError:
pass
try:
mod = self.exported_mods[fullname]
except KeyError:
raise ImportError('Unable to load %r' % fullname)
mod.__loader__ = self
sys.modules[fullname] = mod
return mod
_importer = CalcsImporter({
'new_file1': new_file1,
'new_file2': new_file2,
})
sys.meta_path.append(_importer)
and you should be able to do from calcs.new_file1 import foo

Python Code Coverage Failing with import statements

I have the following project structure:
v/b.py
v/e.py
v/e_test.py
v/p.py
v/__init__.py
#v/e.py
from b import B
from p import P
class E(object):
def run(self): pass
#v/p.py
class P(object):
def run(self): pass
#v/b.py
from p import P
class B(object):
def run(self): pass
#v/e_test.py
from e import E
import unittest
class ETest(unittest.Testcase):
def testSomething(self): pass
if __name__ == '__main__':
unittest.main()
Then I run this pip3 install coverage.
Then coverage run e_test.py
It's giving me this error
Traceback (most recent call last):
File "e_test.py", line 4, in <module>
from e import E
File "~/v/e.py", line 1, in <module>
from b import B
File "~/v/b.py", line 1, in <module>
from p import P
ImportError: cannot import name 'P' from 'p' (/Library/Developer/CommandLineTools/Library/Frameworks/Python3.framework/Versions/3.8/lib/python3.8/p.py)
My question now is how to fix this. Also I want to be able to run:
coverage run e_test.py b_test.py files_test.py
python3 e_test.py
python3 e.py
mm, I think P Class is not declared yet, so you got an error like that
#v/p.py
class P(object):
def run(self): pass
You haven't defined what P is, the error is reporting that p.py does not have a P object.
At last I found the answer. If I'm in the parent folder which contains the v folder, I can run the following command.
PYTHONPATH=./v:$PYTHONPATH coverage run -m unittest discover -s v -p "*_test.py"
or without coverage
PYTHONPATH=./v:$PYTHONPATH python3 -m unittest discover -s v -p "*_test.py"
If I'm inside the v folder, I can run
python3 e.py
and
python3 e_test.py

how to import class object in python from another file

Suppose I have a directory with files classfile.py and a test_classfile.py
here's classfile.py
class Student:
def __init__(self, fname, lname):
self.firstname = fname
self.lastname = lname
def printname(self):
return (self.firstname + self.lastname)
if __name__ == '__main__':
s = Student('John', 'Doe')
What I'd like to do is import just the object s
into the test_file.py
something like this,
from dir.classfile.Student import s
def test_classfile():
assert str(s.printname()).isalpha()
I get the following error
ModuleNotFoundError: No module named 'dir.classfile.Student'; 'dir.classfile' is not a package
As it is said 'dir.classfile' is not a package. If your .py files are both in the same directory, you just do 'import classfile'. Suppose the classfile.py was in the different directory, you would have to do the following:
import os.path
path_to_mods = os.path.abspath('path/to/classfile')
sys.path.insert(0, path_to_mods)
import classfile
You cannot import from 'Student' - this is an object. But you can import Student like this:
from classfile import Student
Also you cannot import instance of the class which is s. You should import the object like it it said above and then create an instance of it.

Python3 import issues when running directly vs pytests

I have a project like
.
|-application
|---foo.py
|---bar.py
|-tests
|---test_foo.py
Contents are:
bar.py
def myfunc():
return "hello from bar"
foo.py
from bar import myfunc
def do_work():
return myfunc()
if __name__ == "__main__":
print(do_work())
test_bar.py
from application import foo
class TestFoo:
def test_foo_do_work(self):
assert foo.do_work() == "hello from bar"
Running python application/foo.py works great, but running python -m pytest tests/ returns error ModuleNotFoundError: No module named 'bar'
In foo.py one way to fix this is to change the import line from from bar import myfunc to from application.bar import myfunc. This causes pytest to pass, but running python application/foo.py gives an error ModuleNotFoundError: No module named 'application'
I don't know what I'm doing wrong. I'm using python 3.7 so I heard __init__.py is unnecessary.
After a little more googling, I have an answer. I created an __init__.py file inside of tests dir with the contents:
import os
import sys
project_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
app_path = os.path.join(project_path, 'application')
sys.path.append(app_path)
From Python nested subpackage doesn't import when running tests

How to import all python file and there method from a project folder

Here is my python project structure:
`-- Test
|-- searchservice
| `-- Applandingsearch.py
`-- test.py
In Applandingsearch.py, my code is :
class Applandingsearch(unittest.TestCase):
def test_responsestatus_correct(self):
self.assertEqual(statuscode, 200)
def test_responsetime_lessthan800ms(self):
self.assertLessEqual(responsetime, 0.8)
if __name__ == '__main__':
unittest.main()
In test.py, I want to import the Applandingsearch, my code in test.py is :
from Test.searchservice import *
if __name__ == '__main__':
suite = unittest.TestSuite()
tests = [Applandingsearch("test_responsestatus_correct")]
suite.addTests(tests)
When I run test.py, the console report " Applandingsearch("test_responseresult_correct"),
NameError: name 'Applandingsearch' is not defined
"
How can I import all the method in Applandingsearch.py?
if I write :
from Test.searchservice.Applandingsearch import Applandingsearch
or
from Test.searchservice.Applandingsearch import *
It can run success, but if I have many files, I have to import all of them, it is too long.
Can someone give some suggestions? Thanks!

Resources