如何在 Python 源分发中正确包含杂项文件

How To Properly Include Miscellaneous File With Python Source Distribution

我无法将其他(非Python)文件包含在 Python 项目中 pip install

我尝试在 setup() 中使用 include_package_data = TrueMANIFEST.in,但它没有包含我想要的 .html 文件。 我也尝试在 setup() 中使用 package_data,但结果相同。

setup() 中的 data_files 有效。但它不会将 .html 文件复制到 site-packages 文件夹,而只是复制到环境的根目录,文件夹名称为 resource,如配置中指定的那样。

有人知道我在这里遗漏了什么吗?

我有以下项目结构

ProjectA
- res/
    - template_a.html
    - template_b.html
    - template_c.html
- tool/
    - __init__.py
    - modulea.py
- constant.py
- project_a.py
- ...
- MANIFEST.in
- setup.py
- ...

setup.py

from setuptools import setup, find_packages

# Read the contents of the README.md file
from pathlib import Path
current_directory = Path(__file__).parent
long_description = (current_directory/'README.md').read_text()

setup(
    name = 'project_a',
    version = '0.0.1',

    # Dependency
    install_requires = [
        'requests',
        ],

    # Metadata
    author = '',
    author_email = '',
    description = 'Project A.',
    long_description=long_description,
    long_description_content_type='text/markdown',
    packages = [
        'tool',
        ],
    py_modules = [
        'project_a',
        'constant',
        '...'
        ],
    # data_files = [
    #     ('resource', [
    #         'res/template_a.html',
    #         'res/template_b.html',
    #         'res/template_c.html',
    #         ]),
    #     ],
    python_requires = '>=3.7',
    # include_package_data = True,
    # package_data = {
    #     '': ['res/*.html']
    #     },
    )

MANIFEST.in

include res/*.html

第一个问题是 res 未声明为包的一部分 - package_data 仅适用于作为 packages 的一部分包含的实体。

另一个问题是 package_data 的声明有一个空字符串作为键 - 它实际上并不引用根目录而是包目录,并且仅引用文件路径。它不对应site-packages(或dist-packages)的根目录。

MANIFEST.in 文件和 setup.py 中的 data_files 声明不是所需解决方案的一部分,因此可以省略。

要解决这些问题,您需要将 res 声明为包的一部分,并且 package_data 需要将该“包”引用为根(以限制包含文件)和所需文件的列表(在本例中,所有 html 文件通过 *.html glob 语法)作为安装的一部分。更正后的 setup.py 需要包含更改以确保 python setup.py bdist_wheel 包含您希望包含在程序包中的文件:

setup(
    name = 'project_a',
    # ...
    packages = [
        'tool',
        'res',  # add this
    ],
    # ...
    include_package_data = True,
    package_data = {
        'res': ['*.html'],   # include this; pairs with the entry in `packages`
        # alternatively, uncomment the following to include any
        # *.html files from any of the `packages` declared above, 
        # e.g tools/*.html and res/*.html will be included as tool 
        # and res are inside `packages`.
        # '': ['*.html'],
    },
)

运行 python setup.py bdist_wheel 现在应该产生:

$ python setup.py bdist_wheel
running bdist_wheel
...
writing manifest file 'project_a.egg-info/SOURCES.txt'
creating build/lib/res
copying res/template_a.html -> build/lib/res
copying res/template_b.html -> build/lib/res
copying res/template_c.html -> build/lib/res
...
copying build/lib/tool/modulea.py -> build/bdist.linux-x86_64/wheel/tool
copying build/lib/project_a.py -> build/bdist.linux-x86_64/wheel
creating build/bdist.linux-x86_64/wheel/res
copying build/lib/res/template_c.html -> build/bdist.linux-x86_64/wheel/res
copying build/lib/res/template_a.html -> build/bdist.linux-x86_64/wheel/res
copying build/lib/res/template_b.html -> build/bdist.linux-x86_64/wheel/res
copying build/lib/constant.py -> build/bdist.linux-x86_64/wheel
...
creating 'dist/project_a-0.0.1-py3-none-any.whl' and adding 'build/bdist.linux-x86_64/wheel' to it
adding 'constant.py'
adding 'project_a.py'
adding 'res/template_a.html'
adding 'res/template_b.html'
adding 'res/template_c.html'
adding 'tool/__init__.py'
adding 'tool/modulea.py'
adding 'project_a-0.0.1.dist-info/METADATA'
adding 'project_a-0.0.1.dist-info/WHEEL'
adding 'project_a-0.0.1.dist-info/top_level.txt'
adding 'project_a-0.0.1.dist-info/RECORD'
removing build/bdist.linux-x86_64/wheel

请注意,与 packagespackage_data 中没有 'res' 条目的版本相比,额外的输出已加粗。然后可以在 dist 中找到生成的 .whl 文件,它可以用于安装(例如通过 pip install -U dist/project_a-0.0.1-py3-none-any.whl)。