启用Python包中所有子模块导入的正确方法

问题描述 投票:0回答:3

我正在使用 setuptools 制作一个 python 包,但在安装包后无法使源代码中的所有嵌套文件夹可供导入。我正在工作的目录具有如下所示的结构。

├── setup.py
└── src
    └── foo
        ├── a
        │   ├── aa
        │   │   └── aafile.py
        │   └── afile.py
        ├── b
        │   └── bfile.py
        └── __init__.py

目前,我无法导入子模块,例如

from foo.a import aa
from foo.a.aa import some_method
,除非我明确地将子模块的名称传递给 setuptools。也就是说,setup.py 需要包含类似的内容

from setuptools import setup

setup(
    version="0.0.1",
    name="foo",
    py_modules=["foo", "foo.a", "foo.a.a", "foo.b"],
    package_dir={"": "src"},
    packages=["foo", "foo.a", "foo.a.a", "foo.b"],
    include_package_data=True,
    #  more arguments go here
)

这使得组织代码变得相当麻烦。有没有一种简单的方法来允许包的用户安装 src/foo 中包含的任何子模块?

python setuptools python-packaging
3个回答
2
投票

你会想要

setuptools.find_packages()
——尽管总而言之,你可能想考虑完全放弃
setup.py
,转而支持 PEP 517 风格的构建,没有任意的 Python ,而只是
pyproject.toml
(可能还有
setup.cfg
) )
.

from setuptools import setup, find_packages

setup(
    version="0.0.1",
    name="foo",
    package_dir={"": "src"},
    packages=find_packages(where='src'),
    include_package_data=True,
)

1
投票

每个包/子包必须包含一个(至少为空)

__init__.py
文件才能被视为如此。

如果您希望仅使用一个

import foo
导入整个包和子包树,请考虑使用相关子包的导入来填充您的
__init__.py
文件。

# src/foo/__init__.py

import foo.a
import foo.b
# src/foo/a/__init__.py

import foo.a.aa
# src/foo/b/__init__.py

import foo.b.bb

否则将

__init__.py
文件留空,用户将需要手动加载他想要的子包/子模块。


0
投票

使用 setup.py 是合法的,并且不被弃用,正如评论中错误建议的那样。

同样,正确的解决方案与提出的答案不同。第一个答案找不到任何东西,因为顶部模块是命名空间包。

正确的方法是正确注释模块、命名空间包、数据文件使用正确的工具进行包发现或遵守受支持的布局,例如“src”

考虑这个结构(修复:添加缺失的

__init__.py
来注释
aa
模块)。

📦tmp
 ┣ 📂src
 ┃ ┗ 📂foo
 ┃ ┃ ┣ 📂a
 ┃ ┃ ┃ ┣ 📂aa
 ┃ ┃ ┃ ┃ ┣ 📜__init__.py
 ┃ ┃ ┃ ┃ ┗ 📜aa_file.py
 ┃ ┃ ┃ ┗ 📜afile.py
 ┃ ┃ ┗ 📂b
 ┃ ┃ ┃ ┣ 📜__init__.py
 ┃ ┃ ┃ ┣ 📜bfile.py
 ┃ ┃ ┃ ┗ 📜config.yaml
 ┗ 📜setup.py

请注意,

foo
foo/a
都是命名空间包,并且不会被
find_packages
找到。请注意,我还添加了配置文件作为基本非 Pythonic 文件的示例。这是正确的安装设置:

# setup.py

from setuptools import setup, find_packages, find_namespace_packages

setup(name='proper-package',
      version='0.1',
      description='A demo showing a proper setup for a complex package.',
      packages = find_namespace_packages(where="src"), # NOTE: better than <find_packages> which ignores namespace packages and their nested modules!
      package_dir={'':'src'},
      include_package_data=True,
      package_data={"": ["*.yaml"]}, # NOTE: use this for non-pythonic files needed, such as configs, demo data etc...
)

pip install .
目录触发的
project
安装会生成
build
文件夹作为中间步骤,并最终生成
lib/python3.x/site-packages
下的预期内容:

📦foo
 ┣ 📂a
 ┃ ┣ 📂__pycache__
 ┃ ┃ ┗ 📜afile.cpython-310.pyc
 ┃ ┣ 📂aa
 ┃ ┃ ┣ 📂__pycache__
 ┃ ┃ ┃ ┣ 📜__init__.cpython-310.pyc
 ┃ ┃ ┃ ┗ 📜aa_file.cpython-310.pyc
 ┃ ┃ ┣ 📜__init__.py
 ┃ ┃ ┗ 📜aa_file.py
 ┃ ┗ 📜afile.py
 ┗ 📂b
 ┃ ┣ 📂__pycache__
 ┃ ┃ ┣ 📜__init__.cpython-310.pyc
 ┃ ┃ ┗ 📜bfile.cpython-310.pyc
 ┃ ┣ 📜__init__.py
 ┃ ┣ 📜bfile.py
 ┃ ┗ 📜config.yaml
© www.soinside.com 2019 - 2024. All rights reserved.