Edit: I'm not trying to import from different folders, they're all in the same folder.
This has been asked countless times and there's always contradicting answers.
In my case I have this file structure:
import_test
├── custom_hashing
│ ├── __init__.py
│ ├── hashing_type_a.py
│ ├── hashing_type_b.py
│ └── utils.py
└── main.py
And these are the contents in order:
init.py
from hashing_type_a import hashing_function as hashing_function_a
from hashing_type_b import hashing_function as hashing_function_b
# Only "expose" this function in case we need tu update the real hashing functions in the future
def hash_this(string_to_hash, type='a') -> str:
if type == 'a':
return hashing_function_a(string_to_hash)
if type == 'b':
return hashing_function_b(string_to_hash)
hashing_type_a.py
from utils import split_and_switch
def hashing_function(string) -> str:
prepared_string = split_and_switch(string)
hashed_string = ''.join([chr(ord(character)+2)
for character in prepared_string])
return hashed_string
def main():
print("Testing hashing_type_a")
hashed_string = hashing_function("abcdefghijk")
print(f"{hashed_string=}")
if __name__ == '__main__':
main()
hashing_type_b.py
from utils import split_and_switch
def hashing_function(string) -> str:
prepared_string = split_and_switch(string)
hashed_string = ''.join([chr(ord(character)-2)
for character in prepared_string])
return hashed_string
def main():
print("Testing hashing_type_b")
hashed_string = hashing_function("abcdefghijk")
print(f"{hashed_string=}")
if __name__ == '__main__':
main()
utils.py
def split_and_switch(string) -> str:
length = len(string)
half = int(length/2)
start_bit = string[:half]
last_bit = string[half:]
return last_bit+start_bit
main.py
from custom_hashing import hash_this
def main():
hashed_string = hash_this("Lorem ipsum dolr sit amet", type="a")
print(f"Hashed string type a is {hashed_string}")
hashed_string = hash_this("Lorem ipsum dolr sit amet", type="b")
print(f"Hashed string type b is {hashed_string}")
if __name__ == '__main__':
main()
Now, from a terminal window on import_test
I can run:
python.exe .\custom_hashing\hashing_type_a.py
python.exe .\custom_hashing\hashing_type_b.py
And I get the expected output, however if I try to run
python.exe .\main.py
I get the error
Traceback (most recent call last):
File ".\main.py", line 1, in <module>
from custom_hashing import hash_this
File "C:\Users\David\import_test\custom_hashing\__init__.py", line 1, in <module>
from hashing_type_a import hashing_function as hashing_function_a
ModuleNotFoundError: No module named 'hashing_type_a'
I've been told that, since I'm importing from the same folder, I need to change
from hashing_type_a import hashing_function as hashing_function_a
from hashing_type_b import hashing_function as hashing_function_b
to
from .hashing_type_a import hashing_function as hashing_function_a
from .hashing_type_b import hashing_function as hashing_function_b
# ^ Added these periods
And sure enough, I get past the first error, but I run into a "new" one
Traceback (most recent call last):
File ".\main.py", line 1, in <module>
from custom_hashing import hash_this
File "C:\Users\David\import_test\custom_hashing\__init__.py", line 1, in <module>
from .hashing_type_a import hashing_function as hashing_function_a
File "C:\Users\David\import_test\custom_hashing\hashing_type_a.py", line 1, in <module>
from utils import split_and_switch
ModuleNotFoundError: No module named 'utils'
which seems like we can fix the same way
from .utils import split_and_switch
# ^ Added the period on both files
Okay, fixed all the errors, now the program runs:
PS C:\Users\David\import_test> python.exe .\main.py
Hashed string type a is fqnt"ukv"cogvNqtgo"kruwo"
Hashed string type b is bmjpqgr_kcrJmpckgnqsk
But now, if we go back to testing individual modules:
PS C:\Users\David\import_test> python.exe .\custom_hashing\hashing_type_a.py
Traceback (most recent call last):
File ".\custom_hashing\hashing_type_a.py", line 1, in <module>
from .utils import split_and_switch
ImportError: attempted relative import with no known parent package
I guess I could put something like
try:
from .utils import split_and_switch
except ImportError:
from utils import split_and_switch
but I feel like there has to be a better way that I just don't know about.
Here's the folder structure with files if anyone doesn't feel like copy&pasting everything