【问题标题】:How to include package data with setuptools/distutils?如何使用 setuptools/distutils 包含包数据?
【发布时间】:2011-11-23 05:58:03
【问题描述】:

使用 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/ 是数据文件的位置。

【问题讨论】:

  • 我遇到了同样的问题...手动指定data_files 解决了这个问题。但这很容易出错,对我来说“感觉不对”。有人可以验证在package_datadata_files 中复制配置是否真的有必要
  • github.com/wimglenn/resources-example 展示了一个现代的 setuptools 项目结构,它可以使用pyproject.toml 正确地将数据文件打包到轮子和 sdists 中。不需要setup.py 文件。
  • 出于对它的热爱,我无法得到以下任何答案才能正常工作,并且上面的 cmets 需要对我的许多项目进行完全重写。

标签: python setuptools distutils


【解决方案1】:

我意识到这是一个老问题,但对于通过 Google 找到这里的人来说:package_data 是一个低调的,dirty lie。它仅在构建 binary 包 (python setup.py bdist ...) 时使用,但在构建源包 (python setup.py sdist ...) 时。这当然是荒谬的——人们会期望构建源代码分发会产生一组文件,这些文件可以发送给其他人以构建二进制分发。

在任何情况下,使用MANIFEST.in适用于二进制和源代码分发。

【讨论】:

  • 过去一个小时我一直在研究这个问题,并尝试了很多方法。正如你所说,package_data 适用于 bdist 而不是 sdist然而MANIFEST.in 适用于sdist,但 适用于bdist!因此,我能想到的最好的办法是同时包含package_dataMANIFEST.in,以便同时容纳bdistsdist
  • 我找到了另一个来支持@WesleyBaugh。在stackoverflow.com/a/2969087/261718 中,将MANIFEST.in 用于您不会安装的文件,例如文档,将package_data 用于您使用的不是Python 代码的文件(例如图像或模板)。
  • 我正在使用 sdist,并且必须同时包含 MANIFEST.in package_data。似乎MANIFEST.in 控制分发中包含的内容,而 package_data 控制随后在安装过程中复制到 site_packages 目录中的内容。令人困惑的是,MANIFEST.in 中的路径是相对于 setup.py 的位置,package_data 是相对于各个包(例如模块)的根目录。
  • "2.7 版更改:如果没有提供模板,所有匹配 package_data 的文件都将添加到 MANIFEST 文件中。请参阅指定要分发的文件。" from distutils。因此,如果您没有现有的 MANIFEST.in 文件,并且仅当您使用 2.7+ 时,您只会看到 package_data 中文件的行为被自动包含在 ZIP 中。跨度>
  • 说真的,我觉得这张票是针对使用设置工具并发现他们在生活中发现自己的可怕地方的人们的集体治疗课程。
【解决方案2】:

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

reading here 之后,我意识到include_package_data 旨在包含来自版本控制 的文件,而不仅仅是顾名思义的“包含包数据”。来自文档:

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

...

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

去掉那个参数可以修复它,巧合的是,当你切换到 distutils 时它也可以工作,因为它不接受那个参数。

【讨论】:

  • 我的经历不同,我遇到了同样的问题,但不包括 include_package_data=True 条目。对我来说唯一的解决方案是按照上面的建议在清单中添加一个条目。请注意我使用的是 setuptools,也许您的版本适用于“分发”?
  • 删除include_package_data 解决问题的实际原因在original text 中更进一步- 如果使用setuptools 特定的include_package_data 参数,package_data 指定的文件将不会自动添加到清单中,除非它们在 MANIFEST.in 文件中列出。
  • package_data 设置为非空列表并指定include_package_data=False 的用例是什么?为什么需要在MANIFEST.inpackage_data 中指定两次文件?
【解决方案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,请记住在 install_requires 中包含 setuptools-git。远比拥有Manifest 或包含package_data 中的所有路径(在我的情况下,它是一个具有各种静态的django 应用程序)更无聊

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

    【讨论】:

      【解决方案5】:

      更新:此答案已过时,信息不再有效。所有 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'],
         },
      )
      

      【讨论】:

      • distribute 没有被弃用,它正在替换 distutils。我不知道你为什么会遇到这个问题,但这不是原因。
      • 这是我从 IRC 得到的回复,那我相信谁呢?如果您有一个使用分发的工作示例,我将不胜感激。
      • 澄清:distribute 是为了取代 setuptools,两者都建立在 distutils 之上。 distutils 本身最终会被一个新的包所取代,在 python2 中称为“distutils2”,在 python3 中称为“packaging”
      • 切换到 distutils 解决了我的问题,即 include_package_data=True 没有得到尊重。因此,使用该设置您只需要 MANIFEST.in - 无需在 package_data 设置中复制您的文件列表。
      【解决方案6】:

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

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

      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'],
         },
      )
      

      The full other stackoverflow answer here

      【讨论】:

      • 试过了,但仍然没有复制任何内容。
      【解决方案7】:

      使用 setup.cfg (setuptools ≥ 30.3.0)

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

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

      在这种情况下,您的setup.py 可以短于:

      from setuptools import setup
      setup()
      

      有关详细信息,请参阅configuring setup using setup.cfg files

      some talk of deprecating setup.cfg 支持 pyproject.toml,正如 PEP 518 中所提议的那样,但截至 2020 年 2 月 21 日,这仍然是临时的。

      【讨论】:

      • 这个答案忽略了 MANIFEST 文件,所以我认为它实际上不适用于 sdists。只带轮子。你应该提到这一点。
      • @wim 我对 MANIFEST、sdist 和 Wheels 没有足够的了解来回答这个问题。这对我使用 pip install 有用。
      • 那是因为pip install,对于足够现代的 pip 版本,将首先构建一个轮子,然后安装它。对于许多用户来说,这种方法仍然会默默地无法包含包数据。有关详细信息,请参阅接受的答案及其下的 cmets。使用setup.cfg 实际上只是编写问题中setup.py 中OP 已经在做的事情的另一种方式(通过在对setup 的调用中传递package_data 关键字参数),所以我不认为这对于这个问题的回答特别有用。它根本没有解决根本问题。
      【解决方案8】:

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

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

      【讨论】:

        【解决方案9】:

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

        看到这个问题:MANIFEST.in ignored on "python setup.py install" - no data files installed?

        【讨论】:

          【解决方案10】:

          只需删除该行:

          include_package_data=True,
          

          从您的设置脚本中,它会正常工作。 (刚刚使用最新的 setuptools 进行了测试。)

          【讨论】:

          • 这很疯狂,但它同时适用于 sdistbdist_wheel,你检查过为什么吗?
          • 我确实可以确认sdist 在设置时会忽略package_data
          • 现在已经过去几个月了,但我似乎记得我在代码中四处挖掘,迷路了两次,对文档进行了极其精细的梳理,并获得了满足感。显然,各种示例脚本都包含此标志,它会导致无穷无尽的头痛。
          【解决方案11】:

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

          我的经验与其他答案中的经验相矛盾include_package_data=True 确实将数据包含在 bdist!setuptools中的解释 documentation 缺乏上下文和故障排除提示,但 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() 没有找到它。

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

          确实使它成为一个包,所以它确实包含文件夹 _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 以确保 MANIFEST.in 匹配 版本控制下文件的 Git 索引:
          $ check-manifest
          

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

          $ check-manifest --create
          

          这是创建的MANIFEST.in

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

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

          只要应该受版本控制的所有东西都是 在版本控制下(即,是 Git 索引的 part), 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('\n'.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
          

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

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

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

          注意:pip install -e . 显示数据文件是否 包含在bdist中。

          符号链接使安装的行为就像 包括数据文件(因为它们已经存在于本地 开发者的计算机)。

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

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

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

          【讨论】:

            【解决方案12】:

            像这个线程中的其他人一样,我对长寿和仍然缺乏清晰度的组合感到有点惊讶,但对我来说最好的答案是使用check-manifest,如推荐的那样@mike-gazes 的回答

            因此,仅使用 setup.cfg 而没有 setup.py 以及包中所需的其他文本和 python 文件,对我有用的是将其保留在 setup.cfg 中:

            [options]
            packages = find:
            include_package_data = true
            

            并根据check-manifest 输出更新MANIFEST.in

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

            【讨论】:

              【解决方案13】:

              对于像这样的目录结构:

              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 有效。

              【讨论】:

                猜你喜欢
                • 2010-11-28
                • 2017-05-01
                • 2011-05-29
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2012-08-04
                相关资源
                最近更新 更多