包含setup.py的非Python文件

时间:2009-10-23 11:04:58

标签: python distutils

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

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

13 个答案:

答案 0 :(得分:166)

执行此操作的最佳方法可能是使用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是一个dict包名称(空=所有包)到模式列表(可以包括globs)。例如,如果您只想在包中指定文件,也可以这样做:

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,则递归地包含顶级“data”目录:

include requirements.txt
recursive-include data *

尽管如此,为了将这些文件在安装时复制到site-packages中的软件包文件夹,您需要向include_package_data=True函数提供setup()。有关详细信息,请参阅Adding Non-Code Files

答案 1 :(得分:41)

要完成您所描述的内容,需要采取两个步骤......

  • 该文件需要添加到源tarball
  • 需要修改setup.py以将数据文件安装到源路径

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

在包含setup.py

的文件夹中创建MANIFEST模板

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

以下是我的项目的MANIFEST:

  • CHANGELOG.txt
  • INSTALL.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'])]

注意:上述步骤应完全按标准方式完成,无需任何扩展库。

答案 2 :(得分:10)

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

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

documentation can be found here

答案 3 :(得分:5)

在setup(:

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

答案 4 :(得分:3)

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

首先,根据上面的Python Dev评论,不需要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']},
)

答案 5 :(得分:3)

步骤1:使用setup.py在同一文件夹中创建一个MANIFEST.in文件

第2步:在MANIFEST.in

中包含要添加的文件的相对路径。
include README.rst
include docs/*.txt
include funniest/data.json

第3步:在include_package_data=True函数中设置setup(),将这些文件复制到站点包中

reference is here

答案 6 :(得分:2)

这将在2020年生效!

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

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

在清单文件中执行以下操作:

recursive-include template *

请确保在目录名和模式之间留出上述文件/目录的空间。 不要像我们在.gitignore

中那样做
recursive-include template/* [this won't work]

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

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

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

希望有帮助!祝您编码愉快!

答案 7 :(得分:2)

以上都不对我有用。救了我的是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

答案 8 :(得分:1)

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

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

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

file - filewithinstallcommands:

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

答案 9 :(得分:1)

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

以下是对我有用的内容(请参阅文档后再进行研究):

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

include_package_data: False

奇怪的是,最后一行对我也很关键(您也可以省略此关键字参数-效果相同)。

这是将所有文本文件复制到顶层或根目录(要分发的软件包mypkg的上一层)。

希望这会有所帮助!

答案 10 :(得分:1)

现在是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”,则我不太在意-但将程序的关键文件留给了我后面的工作有些重要)

答案 11 :(得分:1)

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

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,
    ...
)

答案 12 :(得分:-14)

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