在setup.py中包含非Python文件

问题:在setup.py中包含非Python文件

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

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

How do I make setup.py include a file that isn’t part of the code? (Specifically, it’s a license file, but it could be any other thing.)

I want to be able to control the location of the file. In the original source folder, the file is in the root of the package. (i.e. on the same level as the topmost __init__.py.) I want it to stay exactly there when the package is installed, regardless of operating system. How do I do that?


回答 0

可能最好的方法是使用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_datadict包名称(空=所有包)中的一组模式(可以包含glob)。例如,如果只想指定包中的文件,也可以这样做:

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

此处的解决方案绝对是py使用.py扩展名重命名非文件。

有关更多信息,请参见Ian Bicking的演示文稿

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

如果您只想控制源代码分发(sdist)的内容并使文件位于软件包外部(例如,顶级目录),则另一种有效的方法是添加MANIFEST.in文件。有关此文件的格式,请参见Python文档

自编写此回复以来,我发现使用MANIFEST.in通常是一种较为轻松的方法,只需确保您的源分发(tar.gz)具有所需的文件即可。

例如,如果您要包括requirements.txt来自顶层的目录,则递归包括顶层“数据”目录:

include requirements.txt
recursive-include data *

不过,为了在安装时将这些文件复制到site-packages内软件包的文件夹中,您需要提供include_package_data=Truesetup()功能。有关更多信息,请参见添加非代码文件

Probably the best way to do this is to use the setuptools package_data directive. This does mean using setuptools (or distribute) instead of distutils, but this is a very seamless “upgrade”.

Here’s a full (but untested) example:

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=[],
)

Note the specific lines that are critical here:

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

package_data is a dict of package names (empty = all packages) to a list of patterns (can include globs). For example, if you want to only specify files within your package, you can do that too:

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

The solution here is definitely not to rename your non-py files with a .py extension.

See Ian Bicking’s presentation for more info.

UPDATE: Another [Better] Approach

Another approach that works well if you just want to control the contents of the source distribution (sdist) and have files outside of the package (e.g. top-level directory) is to add a MANIFEST.in file. See the Python documentation for the format of this file.

Since writing this response, I have found that using MANIFEST.in is typically a less frustrating approach to just make sure your source distribution (tar.gz) has the files you need.

For example, if you wanted to include the requirements.txt from top-level, recursively include the top-level “data” directory:

include requirements.txt
recursive-include data *

Nevertheless, in order for these files to be copied at install time to the package’s folder inside site-packages, you’ll need to supply include_package_data=True to the setup() function. See Adding Non-Code Files for more information.


回答 1

要完成您要描述的内容,将需要两个步骤…

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

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

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

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

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

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • TODO.txt

注意:尽管sdist 确实会自动添加一些文件,但我更愿意明确指定它们以确保确定,而不是预测它会做什么和不做什么。

步骤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'])]

注意:上面的步骤应该完全按照您所描述的标准方式完成,而无需任何扩展库。

To accomplish what you’re describing will take two steps…

  • The file needs to be added to the source tarball
  • setup.py needs to be modified to install the data file to the source path

Step 1: To add the file to the source tarball, include it in the MANIFEST

Create a MANIFEST template in the folder that contains setup.py

The MANIFEST is basically a text file with a list of all the files that will be included in the source tarball.

Here’s what the MANIFEST for my project look like:

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • TODO.txt

Note: While sdist does add some files automatically, I prefer to explicitly specify them to be sure instead of predicting what it does and doesn’t.

Step 2: To install the data file to the source folder, modify setup.py

Since you’re looking to add a data file (LICENSE.txt) to the source install folder you need to modify the data install path to match the source install path. This is necessary because, by default, data files are installed to a different location than source files.

To modify the data install dir to match the source install dir…

Pull the install dir info from distutils with:

from distutils.command.install import INSTALL_SCHEMES

Modify the data install dir to match the source install dir:

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

And, add the data file and location to setup():

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

Note: The steps above should accomplish exactly what you described in a standard manner without requiring any extension libraries.


回答 2

创建MANIFEST.in在项目根与recursive-include所需目录或include使用的文件名。

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

文档可以在这里找到

create MANIFEST.in in the project root with recursive-include to the required directory or include with the file name.

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

documentation can be found here


回答 3

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

这是对我有用的东西(在参考文档后进行介绍):

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

include_package_data: False

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

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

希望这可以帮助!

I wanted to post a comment to one of the questions but I don’t enough reputation to do that >.>

Here’s what worked for me (came up with it after referring the docs):

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

include_package_data: False

The last line was, strangely enough, also crucial for me (you can also omit this keyword argument – it works the same).

What this does is it copies all text files in your top-level or root directory (one level up from the package mypkg you want to distribute).

Hope this helps!


回答 4

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

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

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

第3步:设置include_package_data=Truesetup()功能将这些文件复制到站点包

参考在这里。

Step 1: create a MANIFEST.in file in the same folder with setup.py

Step 2: include the relative path to the files you want to add in MANIFEST.in

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

Step 3: set include_package_data=True in the setup() function to copy these files to site-package

Reference is here.


回答 5

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

It is 2019, and here is what is working – despite advice here and there, what I found on the internet halfway documented is using setuptools_scm, passed as options to setuptools.setup. This will include any data files that are versioned on your VCS, be it git or any other, to the wheel package, and will make “pip install” from the git repository to bring those files along.

So, I just added these two lines to the setup call on “setup.py”. No extra installs or import required:

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

No need to manually list package_data, or in a MANIFEST.in file – if it is versioned, it is included in the package. The docs on “setuptools_scm” put emphasis on creating a version number from the commit position, and disregard the really important part of adding the data files. (I can’t care less if my intermediate wheel file is named “*0.2.2.dev45+g3495a1f” or will use the hardcoded version number “0.3.0dev0” I’ve typed in – but leaving crucial files for the program to work behind is somewhat important)


回答 6

在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
   },

In setup.py under setup( :

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

回答 7

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

首先,根据上述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']},
)

Here is a simpler answer that worked for me.

First, per a Python Dev’s comment above, setuptools is not required:

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

That’s great because putting a setuptools requirement on your package means you will have to install it also. In short:

from distutils.core import setup

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

回答 8

我只是想跟进我发现在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

文件-filewithinstall命令:

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

I just wanted to follow up on something I found working with Python 2.7 on Centos 6. Adding the package_data or data_files as mentioned above did not work for me. I added a MANIFEST.IN with the files I wanted which put the non-python files into the tarball, but did not install them on the target machine via RPM.

In the end, I was able to get the files into my solution using the “options” in the setup/setuptools. The option files let you modify various sections of the spec file from setup.py. As follows.

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

file – 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

找出解决方法:将我重命名lgpl2.1_license.txtlgpl2.1_license.txt.py,并在文本周围加上了三引号。现在,我不需要使用该data_files选项,也无需指定任何绝对路径。我知道将其制作为Python模块很丑陋,但我认为它比指定绝对路径丑陋得多。

Figured out a workaround: I renamed my lgpl2.1_license.txt to lgpl2.1_license.txt.py, and put some triple quotes around the text. Now I don’t need to use the data_files option nor to specify any absolute paths. Making it a Python module is ugly, I know, but I consider it less ugly than specifying absolute paths.