【问题标题】:Including non-Python files with setup.py使用 setup.py 包含非 Python 文件
【发布时间】:2010-12-09 10:09:06
【问题描述】:

如何使setup.py 包含不属于代码的文件? (具体来说,它是一个许可文件,但也可以是其他任何东西。)

我希望能够控制文件的位置。在原始源文件夹中,该文件位于包的根目录中。 (即与最顶层的__init__.py 处于同一级别。)我希望它在安装软件包时完全保留在那里,而不管操作系统如何。我该怎么做?

【问题讨论】:

  • 你现在是怎么做的?您之前的问题表明您熟悉如何添加许可证文件,那么您的“不起作用”的代码是什么?
  • data_files = [('', ['lgpl2.1_license.txt',]),] 将其放入 Python26 文件夹中。
  • 在一些负面反馈之后,我再次阅读了您的问题并意识到我错过了什么。我已经更新了我的答案,以便为您的问题提供一个不需要任何额外模块(例如 setuptools 或分发)的非黑客解决方案。
  • 谢谢埃文。但是,我完全可以使用 setuptools,因为它非常流行。

标签: python distutils


【解决方案1】:

以上都不适合我。救了我的是this 答案。
显然,为了在安装过程中提取这些数据文件,我必须做几件事:

  1. 如前所述 - 将MANIFEST.in 添加到项目并指定要包含的文件夹/文件。就我而言:recursive-include folder_with_extra_stuff *
  2. 同样,就像已经提到的 - 将 include_package_data=True 添加到您的 setup.py。这一点至关重要,因为没有它,只会带来匹配 *.py 的文件。
  3. 这就是缺少的! - 在您的数据文件夹中添加一个空的__init__.py。对我来说,我必须将此文件添加到我的 folder-with-extra-stuff
  4. 额外 - 不确定这是否是必需的,但使用我自己的 python 模块,我看到它们被压缩在site-packages 的 .egg 文件中。所以我不得不将zip_safe=False 添加到我的setup.py 文件中。

最终目录结构

my-app/
├─ app/
│  ├─ __init__.py
│  ├─ __main__.py
├─ folder-with-extra-stuff/
│  ├─ __init__.py
│  ├─ data_file.json
├─ setup.py
├─ MANIFEST.in

【讨论】:

  • 简洁的概述 - 对我来说,问题是如何我在 MANIFEST.in 中声明文件 - 谢谢!
  • 你能用目录结构更新这个吗?这是一个很好的网站ascii-tree-generator.com
  • @Led 当然,没问题。
  • 非常感谢,这个解决方案对我有用。额外的 init.py 成功了
【解决方案2】:

没有一个答案对我有用,因为我的文件位于包外的顶层。我改用自定义构建命令。

import os
import setuptools
from setuptools.command.build_py import build_py
from shutil import copyfile

HERE = os.path.abspath(os.path.dirname(__file__))
NAME = "thepackage"

class BuildCommand(build_py):
    def run(self):
        build_py.run(self)

        if not self.dry_run:
            target_dir = os.path.join(self.build_lib, NAME)
            for fn in ["VERSION", "LICENSE.txt"]:
                copyfile(os.path.join(HERE, fn), os.path.join(target_dir,fn))

 
 
setuptools.setup(
    name=NAME,
    cmdclass={"build_py": BuildCommand},
    description=DESCRIPTION,
    ...
)

【讨论】:

    【解决方案3】:

    现在是 2019 年,这就是有效的方法 - 尽管到处都有建议,但我在互联网上发现的一半记录是使用setuptools_scm,作为选项传递给setuptools.setup。这将包括在您的 VCS 上进行版本控制的任何数据文件,无论是 git 还是其他任何数据文件,都将包含在 wheel 包中,并将从 git 存储库进行“pip install”以将这些文件带入。

    所以,我只是将这两行添加到“setup.py”的设置调用中。无需额外安装或导入:

        setup_requires=['setuptools_scm'],
        include_package_data=True,
    

    无需手动列出 package_data,或在 MANIFEST.in 文件中列出 - 如果它是版本控制的,它包含在包中。 “setuptools_scm”上的文档强调从提交位置创建版本号,而忽略了添加数据文件的真正重要部分。 (如果我的中间轮文件被命名为“*0.2.2.dev45+g3495a1f”或者将使用我输入的硬编码版本号“0.3.0dev0”,我不在乎 - 但将关键文件留给程序后面的工作有点重要)

    【讨论】:

      【解决方案4】:

      这在 2020 年有效!

      正如其他人所说,在 setup.py 所在的位置创建“MANIFEST.in”。

      清单中的下一步包括/排除所有必要的东西。请注意这里的语法。 例如:假设我们有模板文件夹要包含在源包中。

      在清单文件中这样做:

      recursive-include template *
      

      确保在 dir-name 和 pattern 之间为上述文件/目录留出空格。 不要像我们在 .gitignore 中那样做

      recursive-include template/* [this won't work]
      

      其他选项是使用包含。有很多选择。 Look up here at their docs for Manifest.in

      最后一个重要步骤,将这个参数包含在你的 setup.py 中,你就可以开始了!

         setup(
          ...
          include_package_data=True,
          ......
      )
      

      希望对您有所帮助!快乐编码!

      【讨论】:

        【解决方案5】:

        第 1 步:在与 setup.py 相同的文件夹中创建一个MANIFEST.in 文件

        第 2 步:MANIFEST.in中包含要添加的文件的相对路径

        include README.rst
        include docs/*.txt
        include funniest/data.json
        

        第三步:setup()函数中设置include_package_data=True,将这些文件复制到site-package

        Reference is here.

        【讨论】:

          【解决方案6】:

          我想对其中一个问题发表评论,但我没有足够的声誉来这样做 >.>

          这对我有用(参考文档后想出的):

          package_data={
              'mypkg': ['../*.txt']
          },
          
          include_package_data: False
          

          奇怪的是,最后一行对我来说也很重要(你也可以省略这个关键字参数 - 它的工作原理相同)。

          它的作用是复制您的顶级或根目录中的所有文本文件(您要分发的包mypkg 的上一级)。

          希望这会有所帮助!

          【讨论】:

          • 我正在寻找一种不必创建MANIFEST.in 的方法,这对我有用。最后一行对我来说也很重要。我的台词是include_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
          【解决方案7】:

          可能最好的方法是使用setuptools package_data 指令。这确实意味着使用setuptools(或distribute)而不是distutils,但这是一个非常无缝的“升级”。

          这是一个完整(但未经测试)的示例:

          from setuptools import setup, find_packages
          
          setup(
              name='your_project_name',
              version='0.1',
              description='A description.',
              packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']),
              package_data={'': ['license.txt']},
              include_package_data=True,
              install_requires=[],
          )
          

          请注意此处关键的特定行:

          package_data={'': ['license.txt']},
          include_package_data=True,
          

          package_data 是包名称(空 = 所有包)到模式列表(可以包括 glob)的dict。例如,如果您只想指定包中的文件,您也可以这样做:

          package_data={'yourpackage': ['*.txt', 'path/to/resources/*.txt']}
          

          这里的解决方案绝对.py 扩展名重命名您的非py 文件。

          请参阅Ian Bicking's presentation 了解更多信息。

          更新:另一种 [更好] 方法

          如果您只想控制源代码分发的内容 (sdist) 并在包之外拥有文件(例如顶级目录),另一种效果很好的方法是添加一个 MANIFEST.in 文件。该文件的格式见the Python documentation

          自从写下这个回复后,我发现使用MANIFEST.in 通常是一种不那么令人沮丧的方法,它可以确保您的源代码分发 (tar.gz) 具有您需要的文件。

          例如,如果您想从顶层包含requirements.txt,则递归地包含顶层“数据”目录:

          include requirements.txt
          recursive-include data *
          

          不过,为了在安装时将这些文件复制到 site-packages 内的包文件夹中,您需要将 include_package_data=True 提供给 setup() 函数。请参阅Adding Non-Code Files 了解更多信息。

          【讨论】:

          • package_data 自 Python 2.3 起也可用于纯 distutils 设置脚本。
          • 这个答案看起来很明智,但对我不起作用。由于 package_data 是出了名的不可靠(需要协调 MANIFEST.in 和 setup.py 以将文件添加到 sdist 并安装它们,作为单独的步骤)并且此答案的作者指出它“未经测试”,任何人都可以否则确认它是否适用于他们?我的 LICENSE 文件包含在 sdist 中,但在我运行“python setup.py install”或“pip install Package”时没有安装
          • Ian Bicking 的演示文稿仅展示了如何为包中的文件安装包数据。我的 LICENSE 文件位于项目的顶层,即不在任何包中。我还能使用 package_data 吗?使用 data_files 是不可能的,因为它将文件放在系统范围的位置。与我的项目无关,更糟糕的是,位置会根据我是从同一个 sdist 运行“setup.py install”还是“pip install”而改变。
          • 我猜它对我不起作用的原因是该文件不位于任何包中 - 它是存储库顶层的 LICENSE 文件,因此不能不能使用 'package_data' 安装
          • 这个答案对我不起作用。附加文件没有被放入压缩包中......
          【解决方案8】:

          在项目根目录中创建MANIFEST.in,将recursive-include添加到所需目录或include,并使用文件名。

          include LICENSE
          include README.rst
          recursive-include package/static *
          recursive-include package/templates *
          

          documentation can be found here

          【讨论】:

            【解决方案9】:

            我只是想跟进我发现在 Centos 6 上使用 Python 2.7 的一些事情。如上所述添加 package_data 或 data_files 对我不起作用。我添加了一个包含我想要的文件的 MANIFEST.IN,它将非 python 文件放入 tarball,但没有通过 RPM 将它们安装在目标机器上。

            最后,我能够使用 setup/setuptools 中的“选项”将文件放入我的解决方案中。选项文件允许您从 setup.py 修改规范文件的各个部分。如下。

            from setuptools import setup
            
            
            setup(
                name='theProjectName',
                version='1',
                packages=['thePackage'],
                url='',
                license='',
                author='me',
                author_email='me@email.com',
                description='',
                options={'bdist_rpm': {'install_script': 'filewithinstallcommands'}},
            )
            

            文件 - MANIFEST.in:

            include license.txt
            

            文件 - 带有安装命令的文件:

            mkdir -p $RPM_BUILD_ROOT/pathtoinstall/
            #this line installs your python files
            python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
            #install license.txt into /pathtoinstall folder
            install -m 700 license.txt $RPM_BUILD_ROOT/pathtoinstall/
            echo /pathtoinstall/license.txt >> INSTALLED_FILES
            

            【讨论】:

              【解决方案10】:

              在 setup.py 下 setup(:

              setup(
                 name = 'foo library'
                 ...
                package_data={
                 'foolibrary.folderA': ['*'],     # All files from folder A
                 'foolibrary.folderB': ['*.txt']  #All text files from folder B
                 },
              

              【讨论】:

              • 这实际上对实现 OP 的目标没有任何帮助。你在package_data 中写的任何内容都不会影响setup.py install 所做的事情,除非你修改了安装命令本身。除非这些文件位于包目录下,否则您通常会希望避免这种情况。
              【解决方案11】:

              这是一个对我有用的更简单的答案。

              首先,根据上述 Python 开发人员的评论,setuptools 不是必需的:

              package_data is also available to pure distutils setup scripts 
              since 2.3. – Éric Araujo
              

              这很好,因为在您的软件包中添加了 setuptools 要求意味着您也必须安装它。简而言之:

              from distutils.core import setup
              
              setup(
                  # ...snip...
                  packages          = ['pkgname'],
                  package_data      = {'pkgname': ['license.txt']},
              )
              

              【讨论】:

              • 它会抱怨目录pkgame不存在
              【解决方案12】:

              要完成您所描述的,需要两个步骤...

              • 需要将文件添加到源压缩包中
              • 需要修改setup.py,将数据文件安装到源路径

              第 1 步:要将文件添加到源 tarball,请将其包含在 MANIFEST 中

              在包含 setup.py 的文件夹中创建一个MANIFEST 模板

              MANIFEST 基本上是一个文本文件,其中包含将包含在源 tarball 中的所有文件的列表。

              我的项目的清单如下所示:

              • CHANGELOG.txt
              • 安装.txt
              • LICENSE.txt
              • pypreprocessor.py
              • README.txt
              • setup.py
              • test.py
              • TODO.txt

              注意:虽然sdist does add some files automatically,我更喜欢明确指定它们以确保而不是预测它做什么和不做什么。

              第 2 步:要将数据文件安装到源文件夹,请修改 setup.py

              由于您希望将数据文件 (LICENSE.txt) 添加到源安装文件夹,因此您需要修改数据安装路径以匹配源安装路径。这是必要的,因为默认情况下,数据文件安装到与源文件不同的位置。

              要修改数据安装目录以匹配源安装目录...

              从 distutils 中提取安装目录信息:

              from distutils.command.install import INSTALL_SCHEMES
              

              修改数据安装目录以匹配源安装目录:

              for scheme in INSTALL_SCHEMES.values():
                  scheme['data'] = scheme['purelib']
              

              并且,将数据文件和位置添加到 setup():

              data_files=[('', ['LICENSE.txt'])]
              

              注意:上述步骤应该完全按照您以标准方式描述的内容,而不需要任何扩展库。

              【讨论】:

              • MANIFEST 仅控制源 tarball 中包含的文件(由 sdist 生成)。不会安装那里列出的文件。
              • @David 我没有意识到我在第一种方法中的距离有多远。我已将答案更新为正确的,以完成问题所要求的内容,而无需任何其他第三方库。
              • @Éric 有什么特别的原因吗?并且,您是否有一个可行的安装程序替代方案,不需要 3rd 方包(如 setup_tools)才能工作。我选择 distutils 而不是 setuptools,因为它包含在 python 的 vanilla 安装中,并且我正在为 PYPI 构建模块。现在使用 distutils2 应该有更好的方法来做到这一点,但我有一段时间没有接触过 python,所以我不知道怎么做。由于您似乎对 distutils2 很了解,我认为拥有一个合适的 distutils2 替代品对我们其他人有益。
              • 正如其他线程中提到的那样,如果文件不在包中,package_data 将不起作用。
              • @ÉricAraujo:使用此解决方案不是一个坏主意,因为没有其他方法。这是一个糟糕的 distutils 设计 - 这是真的。但它是事实上的公共 API,它永远不会改变,因为它会破坏很多东西。希望distutils2能提供更好的推荐方式。
              【解决方案13】:

              想出了一个解决方法:我将我的lgpl2.1_license.txt 重命名为lgpl2.1_license.txt.py,并在文本周围加上了一些三引号。现在我不需要使用data_files 选项,也不需要指定任何绝对路径。我知道,把它变成一个 Python 模块很难看,但我认为它不如指定绝对路径那么难看。

              【讨论】:

              • 查看我的帖子。它不一定是丑陋的。在网上很难找到一个好的例子,因为很难找到设置软件包的好文档。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2017-12-22
              • 2015-07-21
              • 1970-01-01
              • 2019-11-03
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多