Reputation: 2426
I have the following file structure:
bot
├── LICENSE.md
├── README.md
├── bot.py # <-- file that is executed from command line
├── plugins
│ ├── __init__.py
│ ├── debug.py
│ └── parsemessages.py
├── helpers
│ ├── __init__.py
│ ├── parse.py
│ └── greetings.py
└── commands
├── __init__.py
└── search.py
bot.py
, when executed from the command line, will load in everything in the plugins
directory.
I want plugins/parsemessages.py
to import parse
from the helpers
directory, so I do that:
# parsemessages.py
from ..helpers import parse
parse.execute("string to be parsed")
I run python3 bot.py
from the command line.
I get the following error:
File "/home/bot/plugins/parsemessages.py", line 2, in <module>
from ..helpers import parse
ValueError: attempted relative import beyond top-level package
So I change two dots to one:
# parsemessages.py
from .helpers import parse
parse.execute("string to be parsed")
...but I get another error:
File "/home/bot/plugins/parsemessages.py", line 2, in <module>
from .helpers import parse
ImportError: No module named 'plugins.helpers'
How can I get this import to work?
It's worth noting that I'm not attempting to make a package here, this is just a normal script. That being said, I'm not willing to mess around with sys.path
- I want this to be clean to use.
Additionally, I want parse
to be imported as parse
- so for the example above, I should be typing parse.execute()
and not execute()
.
I found this post and this post, but they start with a file that's quite deep in the file structure (mine is right at the top). I also found this post, but it seems to be talking about a package rather than just a regular .py.
What's the solution here?
Upvotes: 11
Views: 4625
Reputation: 19
If you want to execute your code from the root, my best answer to this is adding to the Path your root folder with os.getcwd(). Be sure your sibling folder has a init.py file.
import os
os.sys.path.insert(0, os.getcwd())
from sibling import module
Upvotes: 1
Reputation: 2533
You could remove the dots, and it should work:
# parsemessages.py
from helpers import parse
parse.execute("string to be parsed")
That's probably your best solution if you really don't want to make it a package. You could also nest the entire project one directory deeper, and call it like python3 foo/bot.py
.
Explanation:
When you're not working with an actual installed package and just importing stuff relative to your current working directory, everything in that directory is considered a top-level package. In your case, bot
, plugins
, helpers
, and commands
are all top-level packages/modules. Your current working directory itself is not a package.
So when you do ...
from ..helpers import parse
... helpers
is considered a top-level package, because it's in your current working directory, and you're trying to import from one level higher than that (from your current working directory itself, which is not a package).
When you do ...
from .helpers import parse
... you're importing relative to plugins
. So .helpers
resolves to plugins.helpers
.
When you do ...
from helpers import parse
... it finds helpers
as a top-level package because it's in your current working directory.
Upvotes: 6