0

i'm struggling with something that feels like it should be simple. my current dir looks like this:

root/
  └─ __init__.py (tried with it and without)
  └─ file_with_class.py
  └─ tests_folder/
       └─ __init__.py  (tried with it and without)
       └─ unittest_for_class.py  

unittest_for_class.py needs to import the class from file_with_class to test it, i tried to import it in various ways i found online but i just keep getting errors like: (class name is same as file name lets say its called file_with_class)

File "tests_folder/unittest_for_class.py", line 3, in <module>
from ..file_with_class import file_with_class
ValueError: Attempted relative import in non-package

File "tests_folder/unittest_for_class.py", line 3, in <module>
from file_with_class import file_with_class
ImportError: No module named file_with_class

and others..

what is the correct way to import a class from a .py file that is in the parent folder ?

  • From wich directory are you executing the script? Also, relative imports are not generally recommended in python. Normally full imports from the parent package are recommenden, and the execution is from root directory – Fran Arenas Oct 11 '22 at 07:18
  • tried executing from the root dir, what do you mean by "full imports from the parent package" ? – TheBadBoss Oct 11 '22 at 10:52

3 Answers3

1

As a short explanation

import * from ..parent works if your program started at the parent level. You import submodules which can have cross relations to other submodules or files in the package -> They are only relative inside a package not the os structure.

Option 1

you actually enter via a script in your parent folder and import your mentioned file as a submodule. Nicest, cleanest and intended way, but then your file is no standalone.

Option 2 - Add the parent dictionary to your path

sys.path.append('/path/to/parent')
import parent

This is a little bit dirty as you now have an extra path for your imports but still one of most easiest ones without much trickery.

Further Options and theory There are quite a few posts here covering this topic relative imports covers quite a few good definitions and concepts in the answers.

Option 3 - Deprecated and not future proof importlib.find_loader

import os
import importlib
current = os.getcwd()  # for rollback
os.chdir("..")         # change to arbitrary path
loader = importlib.find_loader("parent") # load filename
assert loader 
parent = loader.load_module() # now this is your module
assert parent
os.chdir(current)      # change back to working dictionary

(Half of an) Option 4

When working with an IDE this might work, Spyder allows the following code. Standard python console does NOT.

import os
current = os.getcwd()
os.chdir("..")
import parent
os.chdir(current)
Daraan
  • 1,797
  • 13
  • 24
  • thank you for answering, but option 1 isn't really what i want. option 2 seems to be the popular approach, but it feels to me like a workaround that could introduce other problems, do you think thats a safe approach? – TheBadBoss Oct 11 '22 at 10:58
  • You have to use importlib then. The problem as it is one of python core modules it is likely to change in the middle to far future. I've eddited a solution that still works BUT will not work in somewhere in the future! – Daraan Oct 11 '22 at 11:34
0

You can add the parent folder to the search path with sys.path.append() like so:

import sys
sys.path.append('/path/to/parentdir')
from file_with_class import file_with_class
...

See also the tutorial for how Python modules and packages are handled

richyen
  • 8,114
  • 4
  • 13
  • 28
  • thank you for answering, it feels to me like a workaround that could introduce other problems, do you think thats a safe approach? – TheBadBoss Oct 11 '22 at 11:00
  • In terms of safety, as in security/permissions/injection risk, I don't think this should be a problem, assuming that you have read access to the path you specify into `sys.path.append()`. Note that this is indeed the prescribed solution in the tutorial that I have linked to – richyen Oct 11 '22 at 17:22
0

Just keep from file_with_class import file_with_class. Then run python -m test_folder.unittest_for_class. This supports running the script as if it is a module.

namespace-Pt
  • 1,604
  • 1
  • 14
  • 25