Reputation: 117
I am used to a scripting language where it is very common to lay out modules like so:
lib/Foo.pm
lib/Foo/Bar.pm
lib/Foo/Baz.pm
Where you have Foo.pm, then there is a directory called Foo and underneath you place submodules.
In Python, I tried to mimic the layout like so:
modules/foo.py
modules/foo/bar.py
modules/foo/baz.py
However, this wouldn't work because when I do:
from foo import Foo
Python thinks I'm importing a directory named 'foo' rather than foo.py. I tried to mess with init.py business to no avail.
Is there a way to get around this issue? I find it quite annoying that Python cannot distinguish between directory foo vs file foo.py.
EDIT: I think I was missing an important piece. I am sourcing the modules/ directory via my unit tests, so the entire directory structure looks like so:
modules/foo.py
modules/foo/bar.py
modules/foo/baz.py
tests/unit.py
In unit.py, I have the following:
#!/usr/bin/python
import sys
import os
findbin = os.path.abspath(os.path.dirname(sys.argv[0]))
sys.path.append(findbin + "/../modules")
from foo import Foo
obj = Foo()
When I run this, I get:
from foo import Foo
ImportError: cannot import name Foo
Upvotes: 1
Views: 1846
Reputation: 10904
Creating packages with Python can be done using __init__.py
files, but it's important to understand the import priority. The command
from foo import Foo
will look for something named foo
in sys.path
and then look for something named Foo
"inside" foo
. For example, suppose you did not have any __init__.py
files in your directory foo
and your foo.py
file contains,
class Foo:
def __init__(self):
print 'hello'
Giving the command from foo import Foo
from the module directory will work.
>>> from foo import Foo
>>> f = Foo()
hello
If you want the directory foo
to be a package (i.e. a collection of related modules) then you can put an empty __init__.py
file in the foo
directory. This however will make the import
command ambiguous. I haven't found any documentation that claims packages have higher import priority than modules, but you can verify that Python is importing the package by doing the following,
import foo
>>> foo.__file__
foo/__init__.py
which means the package got imported. I imagine the fact that packages have priority will not always be the case. In the end it's best to remove the name conflict by renaming either the file or the package.
Upvotes: 0
Reputation: 48594
You must do this:
modules/foo/__init__.py # instead of foo.py
modules/foo/bar.py
modules/foo/baz.py
Perl's layout doesn't work; if a module has children, the root lives alongside them.
And for what it's worth, some Python devs feel strongly that you should leave __init__.py
empty (or at least minimal), since it's imported first anytime you import any of the children.
It's possible you're also having some other problems if __init__.py
didn't work for you, especially if you literally have a modules/
directory, but I can't diagnose without more details :)
Aha. DO NOT mess with sys.path
; that's always a sign you're making life harder for yourself. (Do you have to mess with sys.path
to import anyone else's code? Then why should you have to do it for your own, when it's right there?)
Just put them directly in the root of your source tree, as project.git/foo/bar.py
. As long as you run your test harness (or whatever) from your root, you can import them with no problems.
You might also want to try a test library like pytest which does discovery and some various fancy things for you.
See also: Filesystem structure of a Python project
Or a smallish project of mine for a more practical example: https://github.com/eevee/dywypi
Upvotes: 1
Reputation: 2295
What you want to create is packages.
in the folder foo
put a blank file with the name __init__.py
. Python will then treat foo as a package. If modules
is also to be treated as a package put a blank __init__.py
inside it as well.
So from your question, if the packages are done correctly and you have this structure:
modules/__init__.py
modules/foo/__init__.py
modules/foo/bar.py
modules/foo/baz.py
You can do these:
import module
from module import foo
from module.foo import bar
import module.foo.bar as foobar
And a lot of other stuffs!
Upvotes: 5