让Python的distutils自动查找包

48

在 Python 的 distutils 中的 setup.py 中描述一个 Python 包时,有没有一种方法可以自动获取每个包含 __init__.py 的目录,并将其作为子包含在其中?

例如,如果结构如下:

mypackage/__init__.py
mypackage/a/__init__.py
mypackage/b/__init__.py
我希望避免做以下事情:
packages = ['mypackage', 'mypackage.a', 'mypackage.b']

并且只需执行:

packages = ['mypackage']

我希望它可以自动查找像ab这样的东西,因为它们有一个init文件。谢谢。


请确保在构建之前不要让任何垃圾堆积在您的软件包结构中。 - asmeurer
4个回答

113
我建议使用setuptools中可用的find_packages()函数,例如:
from setuptools import setup, find_packages

然后执行

packages=find_packages()

6
注意,find_packages() 函数不能找到 3.3 版本之后的没有 __init__.py 文件的包。有关这个问题的票据在 https://bitbucket.org/pypa/setuptools/issue/97。因此,现在需要在包中添加 __init__.py 文件以进行 find_packages() 自动收集,或者必须在 packages 中显式命名它们。 - Iodnas
1
更新:即使是显式列表,在Python 3.3中没有使用__init__.py文件也会出现问题(package init file 'mymod/mysubmod/__init__.py' not found (or not a regular file))。 - Iodnas
1
现在还有find_namespace_packages()(请参见https://setuptools.readthedocs.io/en/latest/setuptools.html#find-namespace-packages),它可以与PEP 420命名空间包(https://www.python.org/dev/peps/pep-0420/)一起使用。 - ead

22
我所知道的最简单方法是使用pkgutil.walk_packages函数来生成包:
from distutils.core import setup
from pkgutil import walk_packages

import mypackage

def find_packages(path=__path__, prefix=""):
    yield prefix
    prefix = prefix + "."
    for _, name, ispkg in walk_packages(path, prefix):
        if ispkg:
            yield name

setup(
    # ... snip ...
    packages = list(find_packages(mypackage.__path__, mypackage.__name__)),
    # ... snip ...
)

1
可能有更好的方法来做这件事...我只是还不知道而已 :-) - Sean Vieira
7
setuptools 中的 find_packages() 实质上做了完全相同的事情,详见我的回答。 - dm76
1
@dm76 是的,但是 OP 正在询问 distutils,而不是 setuptools。我有什么遗漏吗? - Paolo
__path__ 返回 NameError,我使用 '.' 代替。 - Zulu
Downvoter - 你能解释一下吗,这样我就可以改进答案了吗? - Sean Vieira
我认为@dm76的答案更好,尽管它不是disutils。 - guilhermecgs

19
与dm76的答案相同,只是我在我的代码库中也有测试,所以我使用了以下方式:
from setuptools import find_packages

packages=find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"]),

2
import re, os
def find_packages(path='.'):
    ret = []
    for root, dirs, files in os.walk(path):
        if '__init__.py' in files:
            ret.append(re.sub('^[^A-z0-9_]+', '', root.replace('/', '.')))
    return ret

setup(
...
packages = find_packages()
...
)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接