如何使用 setuptools/distutils 包含包数据?

2025-01-09 08:47:00
admin
原创
15
摘要:问题描述:使用 setuptools 时,我无法让安装程序提取任何package_data文件。我读到的所有内容都表明以下是正确的方法。有人能给出建议吗?setup( name='myapp', packages=find_packages(), package_data={ '...

问题描述:

使用 setuptools 时,我无法让安装程序提取任何package_data文件。我读到的所有内容都表明以下是正确的方法。有人能给出建议吗?

setup(
   name='myapp',
   packages=find_packages(),
   package_data={
      'myapp': ['data/*.txt'],
   },
   include_package_data=True,
   zip_safe=False,
   install_requires=['distribute'],
)

myapp/data/数据文件的位置在哪里。


解决方案 1:

我知道这是一个老问题,但对于通过 Google 找到这里的人来说: package_data这是一个卑鄙的肮脏谎言。它只在构建二进制包时使用(python setup.py bdist ...),但在构建源包时不适用python setup.py sdist ...( )。这当然是荒谬的——人们会认为构建源分发版会产生一组文件,这些文件可以发送给其他人来构建二进制分发版。

无论如何,使用对二进制和源分布MANIFEST.in有效。

解决方案 2:

我刚刚遇到了同样的问题。解决方案就是删除include_package_data=True

阅读此处后,我意识到其include_package_data目的是包含来自版本控制的文件,而不是像名称所暗示的那样仅仅“包含包数据”。摘自文档:

[include_package_data] 的数据文件必须受 CVS 或 Subversion 控制

...

如果您想要对包含的文件进行更细粒度的控制(例如,如果您的包目录中有文档文件并想要将其从安装中排除),那么您也可以使用关键字package_data

删除该参数可以修复此问题,这恰好就是为什么当您切换到 distutils 时它也能起作用,因为它不接受该参数。

解决方案 3:

按照@Joe 的建议删除该include_package_data=True行对我也有效。

更详细地说,我没有文件 MANIFEST.in。我使用 Git 而不是 CVS。

存储库采用这种形状:

/myrepo
    - .git/
    - setup.py
    - myproject
        - __init__.py
        - some_mod
            - __init__.py
            - animals.py
            - rocks.py
        - config
            - __init__.py
            - settings.py
            - other_settings.special
            - cool.huh
            - other_settings.xml
        - words
            - __init__.py
            word_set.txt

setup.py

from setuptools import setup, find_packages
import os.path

setup (
    name='myproject',
    version = "4.19",
    packages = find_packages(),  
    # package_dir={'mypkg': 'src/mypkg'},  # didnt use this.
    package_data = {
        # If any package contains *.txt or *.rst files, include them:
        '': ['*.txt', '*.xml', '*.special', '*.huh'],
    },

#
    # Oddly enough, include_package_data=True prevented package_data from working.
    # include_package_data=True, # Commented out.
    data_files=[
#               ('bitmaps', ['bm/b1.gif', 'bm/b2.gif']),
        ('/opt/local/myproject/etc', ['myproject/config/settings.py', 'myproject/config/other_settings.special']),
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'cool.huh')]),
#
        ('/opt/local/myproject/etc', [os.path.join('myproject/config', 'other_settings.xml')]),
        ('/opt/local/myproject/data', [os.path.join('myproject/words', 'word_set.txt')]),
    ],

    install_requires=[ 'jsonschema',
        'logging', ],

     entry_points = {
        'console_scripts': [
            # Blah...
        ], },
)

我运行python setup.py sdist源分发(还没有尝试二进制)。

在一个全新的虚拟环境中,我有一个myproject-4.19.tar.gz, 文件,然后我使用

(venv) pip install ~/myproject-4.19.tar.gz
...

除了所有内容都安装到我的虚拟环境中之外site-packages,那些特殊数据文件还安装到/opt/local/myproject/data/opt/local/myproject/etc

解决方案 4:

include_package_data=True对我有用。

如果你使用 git,记得将其包含setuptools-git在内。这比在 中包含所有路径install_requires要轻松得多(在我的例子中,它是一个包含各种静态数据的 Django 应用程序)Manifest`package_data`

(粘贴我发表的评论,正如k3-rnc所提到的,它实际上很有帮助)

解决方案 5:

使用 setup.cfg(setuptools ≥ 30.3.0)

从 setuptools 30.3.0(发布于 2016-12-08)开始,您可以保持setup.py非常小的配置并将配置移动到setup.cfg文件中。使用这种方法,您可以将包数据放在一个[options.package_data]部分中:

[options.package_data]
* = *.txt, *.rst
hello = *.msg

在这种情况下,您setup.py可以简短地写成:

from setuptools import setup
setup()

有关更多信息,请参阅使用 setup.cfg 文件配置设置。

有人讨论弃用setup.cfgPEP 518中pyproject.toml提出的建议,但截至 2020-02-21,这仍然是临时的。

解决方案 6:

当我遇到同样的问题时,我发现了这篇文章。

我的经验与其他答案中的经验
相矛盾。include_package_data=True 确实包括 bdist 中的数据!setuptools
文档中的解释
缺乏上下文和故障排除提示,但
include_package_data可以像广告中说的那样工作。

我的设置:

  • Windows / Cygwin

  • git 版本 2.21.0

  • Python 3.8.1 Windows 发行版

  • setuptoolsv47.3.1

  • check-manifestv0.42

这是我的实用指南。

如何包含包数据

这是我在 PyPI 上发布的项目的文件结构。(它将应用程序安装在中__main__.py)。

├── LICENSE.md
├── MANIFEST.in
├── my_package
│   ├── __init__.py
│   ├── __main__.py
│   └── _my_data          <---- folder with data
│       ├── consola.ttf   <---- data file
│       └── icon.png      <---- data file
├── README.md
└── setup.py

起点

setuptools.setup()这是 的通用
起点setup.py

setuptools.setup(
    ...
    packages=setuptools.find_packages(),
    ...
)

setuptools.find_packages()包括发行版中的所有软件包。我唯一的软件包是my_package

包含我的数据的子文件夹_my_data不被 Python 视为包,因为它不包含__init__.py,因此find_packages()找不到它。

一种经常被引用但不正确的解决方案是将一个空
__init__.py文件放在文件夹中_my_data

确实使其成为一个包,因此它确实_my_data在分发中
包含了文件夹
。但其中的数据文件_my_data并未包含在内。

所以做成_my_data包裹并没有帮助

解决方案是:

  • sdist已经包含数据文件

  • 添加以包括数据include_package_data=True文件bdist

实验(如何测试解决方案)

有三个步骤可以使这个实验可重复:

$ rm -fr build/ dist/ my_package.egg-info/
$ check-manifest
$ python setup.py sdist bdist_wheel

我将逐步分解这些内容:

  1. 清除旧构建:

$ rm -fr build/ dist/ my_package.egg-info/
  1. 运行check-manifest以确保与版本控制下的文件的 Git 索引MANIFEST.in 相匹配:

$ check-manifest

如果MANIFEST.in尚不存在,则从版本控制下的文件的 Git 索引中创建它:

$ check-manifest --create

这是MANIFEST.in创建的:

include *.md
recursive-include my_package *.png
recursive-include my_package *.ttf

没有理由手动编辑此文件。

只要所有应该受到版本控制的内容都受到版本控制(即,是Git 索引的
一部分check-manifest --create),那就做对了。

注意:如果文件属于以下情况,则它们属于 Git 索引:

  • 被忽略.gitignore

  • 排除在外.git/info/exclude

  • 或者只是尚未添加索引中的新文件

如果任何文件处于不应受到版本控制的版本控制之下,check-manifest则会发出警告并指定建议从 Git 索引中删除哪些文件。

  1. 建造:

$ python setup.py sdist bdist_wheel

现在检查sdist(源分布)和bdist_wheel
(构建分布)以查看它们是否包含数据文件。

查看内容sdist(下面仅显示相关行):

$ tar --list -f dist/my_package-0.0.1a6.tar.gz
my_package-0.0.1a6/
...
my_package-0.0.1a6/my_package/__init__.py
my_package-0.0.1a6/my_package/__main__.py
my_package-0.0.1a6/my_package/_my_data/
my_package-0.0.1a6/my_package/_my_data/consola.ttf <-- yay!
my_package-0.0.1a6/my_package/_my_data/icon.png    <-- yay!
...

因此,sdist已经包含了数据文件,因为它们在中列出MANIFEST.in。无需执行任何额外操作即可将数据文件包含在中sdist

查看其内容bdist(它是一个 .zip 文件,用 进行解析zipfile.ZipFile):

$ python check-whl.py
my_package/__init__.py
my_package/__main__.py
my_package-0.0.1a6.dist-info/LICENSE.md
my_package-0.0.1a6.dist-info/METADATA
my_package-0.0.1a6.dist-info/WHEEL
my_package-0.0.1a6.dist-info/entry_points.txt
my_package-0.0.1a6.dist-info/top_level.txt
my_package-0.0.1a6.dist-info/RECORD

注意:您需要创建自己的check-whl.py脚本来生成上述输出。它只有三行:

from zipfile import ZipFile
path = "dist/my_package-0.0.1a6-py3-none-any.whl" # <-- CHANGE
print('
'.join(ZipFile(path).namelist()))

正如预期的那样,bdist缺少数据文件。

_my_data文件夹完全丢失。

如果我创建了怎么办_my_data/__init__.py?我重复实验,发现数据文件仍然不存在!文件
_my_data/已包含,但不包含数据文件!

解决方案

与其他人的经验相反,这确实有效:

setuptools.setup(
    ...
    packages=setuptools.find_packages(),
    include_package_data=True, # <-- adds data files to bdist
    ...
)

修复完成后,重新进行实验:

$ rm -fr build/ dist/ my_package.egg-info/
$ check-manifest
$ python.exe setup.py sdist bdist_wheel

确保sdist仍然有数据文件:

$ tar --list -f dist/my_package-0.0.1a6.tar.gz
my_package-0.0.1a6/
...
my_package-0.0.1a6/my_package/__init__.py
my_package-0.0.1a6/my_package/__main__.py
my_package-0.0.1a6/my_package/_my_data/
my_package-0.0.1a6/my_package/_my_data/consola.ttf <-- yay!
my_package-0.0.1a6/my_package/_my_data/icon.png    <-- yay!
...

查看内容bdist

$ python check-whl.py
my_package/__init__.py
my_package/__main__.py
my_package/_my_data/consola.ttf        <--- yay!
my_package/_my_data/icon.png           <--- yay!
my_package-0.0.1a6.dist-info/LICENSE.md
my_package-0.0.1a6.dist-info/METADATA
my_package-0.0.1a6.dist-info/WHEEL
my_package-0.0.1a6.dist-info/entry_points.txt
my_package-0.0.1a6.dist-info/top_level.txt
my_package-0.0.1a6.dist-info/RECORD

如何测试是否包含数据文件

我建议使用上面概述的方法检查sdist和进行故障排除/测试bdist

pip 在可编辑模式下安装不是有效的测试

注意:如果数据文件包含在内pip install -e . 则不bdist显示。

符号链接使安装表现得好像数据文件被包含在内(因为它们已经存在于开发人员的计算机上)。

之后pip install my_package,数据文件位于虚拟环境的lib/site-packages/my_package/文件夹中,使用与上面内容列表中显示的完全相同的文件结构whl

发布到 TestPyPI 是一种缓慢的测试方式

发布到TestPyPI然后安装并查看
lib/site-packages/my_packages是一个有效的测试,但它太耗时了。

解决方案 7:

更新:这个答案已经过时了,信息不再有效。所有setup.py配置都应该使用import setuptools。我在https://stackoverflow.com/a/49501350/64313添加了更完整的答案


我通过切换到 distutils 解决了这个问题。看起来分发已被弃用和/或损坏。

from distutils.core import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_data={
      'myapp': ['data/*.txt'],
   },
)

解决方案 8:

几天来我遇到了同样的问题,但即使是这个帖子也无法帮助我,因为一切都令人困惑。所以我做了研究并找到了以下解决方案:

基本上在这种情况下你应该这样做:

from setuptools import setup

setup(
   name='myapp',
   packages=['myapp'],
   package_dir={'myapp':'myapp'}, # the one line where all the magic happens
   package_data={
      'myapp': ['data/*.txt'],
   },
)

完整的其他 stackoverflow 答案在这里

解决方案 9:

这是一个古老的问题,然而……python 的包管理确实有很多不足之处。因此,我使用 pip 在本地安装到指定目录,但令我惊讶的是,package_data 和 data_files 路径都没有起作用。我并不想再向 repo 添加另一个文件,所以我最终利用了 data_files 和 setup.py 选项 --install-data;类似这样的操作

pip install . --install-option="--install-data=$PWD/package" -t package  

解决方案 10:

与本帖中的其他人一样,我对这种持续时间长但仍然缺乏清晰度的结合感到有些惊讶,但对我来说最好的答案是使用check-manifest@mike-gazes 的答案中的建议

因此,只需使用包中所需的asetup.cfg和 no以及附加文本和 python 文件,对我有用的就是将其保存在 setup.cfg 中:setup.py

[options]
packages = find:
include_package_data = true

MANIFEST.in根据check-manifest输出进行更新:

include *.in
include *.txt
include *.yml
include LICENSE
include tox.ini
recursive-include mypkg *.py
recursive-include mypkg *.txt

解决方案 11:

将包含包数据的文件夹移到模块文件夹中解决了我的问题。

请参阅此问题:MANIFEST.in 在“python setup.py install”上被忽略 - 没有安装数据文件?

解决方案 12:

只需删除以下行:

include_package_data=True,

从您的安装脚本,它将正常工作。(刚刚使用最新的安装工具进行了测试。)

解决方案 13:

Setuptools 62.3.0开始,您现在可以使用递归通配符( "**") 以递归方式包含 (子) 目录。这样,您就可以包含整个文件夹及其中的所有文件夹和文件。

例如,当使用pyproject.toml文件时,您可以这样递归地包含两个文件夹:

[tool.setuptools.package-data]
"ema_workbench.examples.data" = ["**"]
"ema_workbench.examples.models" = ["**"]

但你也可以只包含文件夹及其所有子文件夹中的某些文件类型。如果你想包含所有 markdown ( .md) 文件,例如:

[tool.setuptools.package-data]
"ema_workbench.examples.data" = ["**/*.md"]

使用setup.py或时它也应该可以工作setup.cfg

有关详细信息,请参阅https://github.com/pypa/setuptools/pull/3309

解决方案 14:

对于类似这样的目录结构:

foo/
├── foo
│   ├── __init__.py
│   ├── a.py
│   └── data.txt
└── setup.py

setup.py

#!/usr/bin/env python
# -*- coding: utf-8 -*-

from setuptools import setup


NAME = 'foo'
DESCRIPTION = 'Test library to check how setuptools works'
URL = 'https://none.com'
EMAIL = 'gzorp@bzorp.com'
AUTHOR = 'KT'
REQUIRES_PYTHON = '>=3.6.0'

setup(
    name=NAME,
    version='0.0.0',
    description=DESCRIPTION,
    author=AUTHOR,
    author_email=EMAIL,
    python_requires=REQUIRES_PYTHON,
    url=URL,
    license='MIT',
    classifiers=[
        'Programming Language :: Python',
        'Programming Language :: Python :: 3',
        'Programming Language :: Python :: 3.6',
    ],
    packages=['foo'],
    package_data={'foo': ['data.txt']},
    include_package_data=True,
    install_requires=[],
    extras_require={},
    cmdclass={},
)

python setup.py bdist_wheel作品。

相关推荐
  为什么项目管理通常仍然耗时且低效?您是否还在反复更新电子表格、淹没在便利贴中并参加每周更新会议?这确实是耗费时间和精力。借助软件工具的帮助,您可以一目了然地全面了解您的项目。如今,国内外有足够多优秀的项目管理软件可以帮助您掌控每个项目。什么是项目管理软件?项目管理软件是广泛行业用于项目规划、资源分配和调度的软件。它使项...
项目管理软件   984  
  在项目管理领域,CDCP(Certified Data Center Professional)认证评审是一个至关重要的环节,它不仅验证了项目团队的专业能力,还直接关系到项目的成功与否。在这一评审过程中,沟通技巧的运用至关重要。有效的沟通不仅能够确保信息的准确传递,还能增强团队协作,提升评审效率。本文将深入探讨CDCP...
华为IPD流程   0  
  IPD(Integrated Product Development,集成产品开发)是一种以客户需求为核心、跨部门协同的产品开发模式,旨在通过高效的资源整合和流程优化,提升产品开发的成功率和市场竞争力。在IPD培训课程中,掌握关键成功因素是确保团队能够有效实施这一模式的核心。以下将从五个关键成功因素展开讨论,帮助企业和...
IPD项目流程图   0  
  华为IPD(Integrated Product Development,集成产品开发)流程是华为公司在其全球化进程中逐步构建和完善的一套高效产品开发管理体系。这一流程不仅帮助华为在技术创新和产品交付上实现了质的飞跃,还为其在全球市场中赢得了显著的竞争优势。IPD的核心在于通过跨部门协作、阶段性评审和市场需求驱动,确保...
华为IPD   0  
  华为作为全球领先的通信技术解决方案提供商,其成功的背后离不开一套成熟的管理体系——集成产品开发(IPD)。IPD不仅是一种产品开发流程,更是一种系统化的管理思想,它通过跨职能团队的协作、阶段评审机制和市场需求驱动的开发模式,帮助华为在全球市场中脱颖而出。从最初的国内市场到如今的全球化布局,华为的IPD体系在多个领域展现...
IPD管理流程   0  
热门文章
项目管理软件有哪些?
云禅道AD
禅道项目管理软件

云端的项目管理软件

尊享禅道项目软件收费版功能

无需维护,随时随地协同办公

内置subversion和git源码管理

每天备份,随时转为私有部署

免费试用