问题
From Namespace Packages in distribute, I know I can make use of namespace packages to separate a big Python package into several smaller ones. It is really awesome. The document also mentions:
Note, by the way, that your project’s source tree must include the namespace packages’ __init__.py files (and the __init__.py of any parent packages), in a normal Python package layout. These
__init__
.py files must contain the line:__import__('pkg_resources').declare_namespace(__name__)
This code ensures that the namespace package machinery is operating and that the current package is registered as a namespace package.
I'm wondering are there any benefits to keep the same hierarchy of directories to the hierarchy of packages? Or, this is just the technical requirement of the namespace packages feature of distribute/setuptools?
Ex,
I would like to provide a sub-package foo.bar, such that I have to build the following hierarchy of folders and prepare a __init__.py to make setup.py work the namespace package:
~foo.bar/
~foo.bar/setup.py
~foo.bar/foo/__init__.py <= one-lined file dedicated to namespace packages
~foo.bar/foo/bar/__init__.py
~foo.bar/foo/bar/foobar.py
I'm not familiar with namespace packages but it looks to me that 1) foo/bar and 2) (nearly) one-lined __init__.py are routine tasks. They do provide some hints of "this is a namespace package" but I think we already have that information in setup.py?
edit:
As illustrated in the following block, can I have a namespace package without that nested directory and one-lined __init__.py in my working directory? That is, can we ask setup.py to automatically generate those by just putting one line namespace_packages = ['foo']
?
~foo.bar/
~foo.bar/setup.py
~foo.bar/src/__init__.py <= for bar package
~foo.bar/src/foobar.py
回答1:
A namespace package mainly has a particular effect when it comes time to import a sub-package. Basically, here's what happens, when importing foo.bar
- the importer scans through
sys.path
looking for something that looks likefoo
. - when it finds something, it will look inside of the discovered
foo
forbar
. - if
bar
is not found:- if
foo
is a normal package, anImportError
is raised, indicating thatfoo.bar
doesn't exist. - if
foo
is a namespace package, the importer goes back to looking throughsys.path
for the next match offoo
. theImportError
is only raised if all paths have been exhausted.
- if
So that's what it does, but doesn't explain why you might want that. Suppose you designed a big, useful library (foo
) but as part of that, you also developed a small, but very useful utility (foo.bar
) that others python programmers find useful, even when they don't have a use for the bigger library.
You could distribute them together as one big blob of a package (as you designed it) even though most of the people using it only ever import the sub-module. Your users would find this terribly inconvenient because they'd have to download the whole thing (all 200MB of it!) even though they are only really interested in a 10 line utility class. If you have an open license, you'll probably find that several people end up forking it and now there are a half dozen diverging versions of your utility module.
You could rewrite your whole library so that the utility lives outside the foo
namespace (just bar
instead of foo.bar
). You'll be able to distribute the utility separately, and some of your users will be happy, but that's a lot of work, especially considering that there actually are lots of users using the whole library, and so they'll have to rewrite their programs to use the new.
So what you really want is a way to install foo.bar
on its own, but happily coexist with foo
when that's desired too.
A namespace package allows exactly this, two totally independent installations of a foo
package can coexist. setuptools
will recognize that the two packages are designed to live next to each other and politely shift the folders/files in such a way that both are on the path and appear as foo
, one containing foo.bar
and the other containing the rest of foo
.
You'll have two different setup.py
scripts, one for each. foo/__init__.py
in both packages have to indicate that they are namespace packages so the importer knows to continue regardless of which package is discovered first.
来源:https://stackoverflow.com/questions/8380381/the-way-to-make-namespace-packages-in-python