【问题标题】:How to include license file in setup.py script?如何在 setup.py 脚本中包含许可证文件?
【发布时间】:2012-04-16 04:47:22
【问题描述】:

我用 C++ 编写了一个 Python 扩展模块。 我计划使用 setuptools 分发模块。 将有适用于 32 位和 64 位 Windows 的二进制分发版(使用 setup.py bdist_egg 构建)和适用于类 UNIX 平台的源代码分发版(使用 setup.py sdist 构建)。

我计划在 BSD 许可下许可该模块。 在我的源代码树中,文件 LICENSE.txt 与 setup.py 一起位于顶部文件夹中。 我应该如何将它包含在安装包中?

我尝试了以下 setup.py 脚本:

from setuptools import setup, Extension
from glob import glob

setup(
    name = 'Foo',
    version = '0.1.0',
    ext_modules = [Extension('Foo', glob('Source/*.cpp'))],
    package_data = {'': ['LICENSE.txt']}
)

没有用,安装包中不包含许可文件。 可能是因为 setup.py 文件没有定义任何包, 只有一个扩展模块。

我该如何解决这个问题?

【问题讨论】:

    标签: python setuptools


    【解决方案1】:

    编写一个setup.cfg 文件并在其中指定:

    [metadata]
    license_files = LICENSE.txt
    

    为此,似乎需要安装轮子。那就是:

    pip install wheel
    

    如果您已经安装了wheel,但它不起作用,请尝试更新它:

    pip install --upgrade wheel
    

    然后,当通过pip install <path> 安装软件包时,许可证文件将被包含在内。

    【讨论】:

    • @user 是的,我猜它只对生成的轮子文件这样做,即当你运行时:python setup.py bdist_wheelwheel.readthedocs.io/en/stable/…
    • 适用于我,适用于车轮和 sdists。
    • 提示: 新的 license_files 元数据仅适用于 wheels - 不适用于 sdists - 而现在已弃用的 @ 987654332@ 为两者工作。使用 MANIFEST.in 单独控制 sdist 内容。
    • 对于setuptoolslicense_file 键是very much not deprecatedsetuptools 继续透明地支持license_file license_files 键,没有任何抱怨。只有wheel 包单方面弃用了license_file 密钥——当然,用功能更少的密钥替换该密钥。考虑要么定义两者(是的,这是土豆),要么只定义 license_file 键(即忽略 wheel)。 </picard_facepalm>
    • 注意: setuptools 本身 only defines the license_file key in its setup.cfg。伙计们,没有比这更不被弃用的了。 tl;dr: 只需使用license_file
    【解决方案2】:

    由于setuptools 42.0.0,您可以使用license_files 密钥指定要包含在分发中的许可证文件列表。

    请注意,由于implementation details 您实际上不需要将此密钥放入setup.cfg 文件中(如documentationanother answer 建议的那样)。您可以简单地将其作为参数提供给setup() 函数:

    from setuptools import setup
    
    setup(
        ...
        license_files = ('LICENSE.txt',),
        ...
    )
    

    另请注意,虽然这些文件将包含在二进制(轮子)和源代码分发中,但它们不会安装您的软件包来自setup.py-style 源如果用户没有安装 wheel 软件包,则分发!
    为确保许可证文件将与您的软件包一起安装,您需要对安装脚本进行一些额外的修改:

    from setuptools import setup
    from setuptools.command.egg_info import egg_info
    
    
    class egg_info_ex(egg_info):
        """Includes license file into `.egg-info` folder."""
    
        def run(self):
            # don't duplicate license into `.egg-info` when building a distribution
            if not self.distribution.have_run.get('install', True):
                # `install` command is in progress, copy license
                self.mkpath(self.egg_info)
                self.copy_file('LICENSE.txt', self.egg_info)
    
            egg_info.run(self)
    
    
    setup(
        ...
        license_files = ('LICENSE.txt',),
        cmdclass = {'egg_info': egg_info_ex},
        ...
    )
    

    如果您的项目是pyproject.toml-style project,并且您认为它将由兼容 PEP 517 的前端(例如 pip>=19)安装,则将从您的源代码强制构建一个轮子,并且许可证文件将安装到 @987654337自动@文件夹。

    【讨论】:

      【解决方案3】:

      使用 METADATA.in 文件,许可证可以自动包含在源包和轮子中:

      METADATA.in include README.md include COPYING

      在此处查看示例: https://github.com/node40/smsh

      【讨论】:

      • 你的项目没有 MANIFEST.in 文件 :)
      • 我修正了答案。该文件是“METADATA.in”。
      • 该文件需要命名为MANIFEST.in 才能工作。我希望 puta METADATA.in 文件名只会做一件事而不是 nothing
      【解决方案4】:

      新的 setuptools (40.x) 允许将元数据(包括许可证)存储在 setup.cfg's "metadata" section 中。如果您使用较旧的 setuptools,您可以使用 setup() 中的“license”命名参数提供许可证:

      def read_text(file_name: str):
          return open(os.path.join(base_path, file_name)).read()
      
      
      setup(
          name = 'Foo',
          version = '0.1.0',
          ext_modules = [Extension('Foo', glob('Source/*.cpp'))],
          # package_data = {'': ['LICENSE.txt']}
          license=read_text("LICENSE.txt")
      )
      

      【讨论】:

      • 这个 setup.py 结构很糟糕,因为它不会创建一个上传有效到 PyPI 的包。 license 字段需要单行描述(即许可证名称),而不是来自文件 github.com/pypa/setuptools/issues/1390 的多行文本
      • 我更新了示例以修复 ^(无法编辑我的第一条评论)
      【解决方案5】:

      您必须将 LICENSE.txt 文件移动到项目的包目录中。它不能驻留在顶层。部署 Python 目录,而不是部署工件。如果您创建一个 python 包,该包实际上包含许多子包。每个子包必须包含与部署相关的所有文件。

      不要使用data_files,因为它实际上会将文件作为单独的包分发。 (我听说package_files 有效,但我还没有看到一个有效的例子来做到这一点)。

      【讨论】:

        【解决方案6】:

        例如:

        setup(
            ...
            license="ZPL",
            classifiers=[
                ...
                'License :: OSI Approved :: Zope Public License',
                ...
                ],
             ...)
        

        此外,您还可以将许可证文本插入“long_description”:

        setup(
            ...
            long_description="Package description. \nLicense Text",
            ...)
        

        【讨论】:

        • 如何在任何地方包含许可证文件
        猜你喜欢
        • 2018-12-28
        • 1970-01-01
        • 2018-01-09
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-08-27
        • 2019-08-27
        相关资源
        最近更新 更多