我如何使setup.py包含一个不是代码一部分的文件?(具体来说,它是一个许可证文件,但也可以是其他任何东西。)
我希望能够控制文件的位置。在原始源文件夹中,文件位于包的根目录中。(即与最顶层的__init__.py在同一层。)我希望它在安装包时保持在那里,而不管操作系统是什么。我怎么做呢?
我如何使setup.py包含一个不是代码一部分的文件?(具体来说,它是一个许可证文件,但也可以是其他任何东西。)
我希望能够控制文件的位置。在原始源文件夹中,文件位于包的根目录中。(即与最顶层的__init__.py在同一层。)我希望它在安装包时保持在那里,而不管操作系统是什么。我怎么做呢?
当前回答
现在是2019年,以下是行之有效的方法 尽管这里和那里的建议,我在互联网上发现的是使用setuptools_scm,作为选项传递给setuptools.setup。这将包括VCS中版本化的任何数据文件,无论是git还是其他任何文件,到wheel包中,并将从git存储库中进行“pip install”以将这些文件带进来。
因此,我只是在“setup.py”的设置调用中添加了这两行。无需额外安装或导入:
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)
其他回答
对于要包含在安装中的非python文件,它们必须位于已安装包目录中的一个目录中。如果您在MANIFEST中指定包目录之外的非python文件。中,它们将包含在您的发行版中,但不会被安装。在包目录之外安装任意文件的“文档化”方式并不可靠(现在每个人都注意到了)。
The above answer from Julian Mann copies the files to your package directory in the build directory, so it does work, but not if you are installing in editable/develop mode (pip install -e or python setup.py develop). Based on this answer to a related question (and Julian's answer), below is an example that copies files to your installed package location either way after all the other install/develop tasks are done. The assumption here is that files file1 and file2 in your root-level data directory will be copied to your installed package directory (my_package), and that they will be accessible from python modules in your package using os.path.join(os.path.dirname(__file__), 'file1'), etc.
记得也要做清单。在上面描述的东西中,以便这些文件也包含在您的发行版中。为什么setuptools会在你的发行版中包含文件,然后默默地不安装它们,这超出了我的理解范围。尽管在包目录之外安装它们可能更可疑。
import os
from setuptools import setup
from setuptools.command.develop import develop
from setuptools.command.install import install
from shutil import copyfile
HERE = os.path.abspath(os.path.dirname(__file__))
NAME = 'my_package'
def copy_files (target_path):
source_path = os.path.join(HERE, 'data')
for fn in ["file1", "file2"]:
copyfile(os.path.join(source_path, fn), os.path.join(target_path,fn))
class PostDevelopCommand(develop):
"""Post-installation for development mode."""
def run(self):
develop.run(self)
copy_files (os.path.abspath(NAME))
class PostInstallCommand(install):
"""Post-installation for installation mode."""
def run(self):
install.run(self)
copy_files (os.path.abspath(os.path.join(self.install_lib, NAME)))
setup(
name=NAME,
cmdclass={
'develop': PostDevelopCommand,
'install': PostInstallCommand,
},
version='0.1.0',
packages=[NAME],
include_package_data=True,
setup_requires=['setuptools_scm'],
)
我想对其中一个问题发表评论,但我没有足够的声誉来做>.>
以下是对我有效的方法(参考文档后想到的):
package_data={
'mypkg': ['../*.txt']
},
include_package_data: False
奇怪的是,最后一行对我来说也很重要(你也可以省略这个关键字参数——它的工作原理是一样的)。
它的作用是复制顶级目录或根目录中的所有文本文件(比您想分发的包mypkg高一级)。
最好的方法可能是使用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)的字典。例如,如果你只想在你的包中指定文件,你也可以这样做:
package_data={'yourpackage': ['*.txt', 'path/to/resources/*.txt']}
这里的解决方案肯定不是用.py扩展名重命名非py文件。
更多信息请看Ian Bicking的演讲。
更新:另一个[更好的]方法
如果你只是想控制源分发(sdist)的内容,并且在包之外有文件(例如顶级目录),另一种工作得很好的方法是添加一个MANIFEST。在文件中。有关此文件的格式,请参阅Python文档。
写完这篇文章后,我发现使用MANIFEST。In通常是一种不那么令人沮丧的方法,可以确保您的源发行版(tar.gz)有您需要的文件。
例如,如果你想包含顶层的requirements.txt,递归地包含顶层的"data"目录:
include requirements.txt
recursive-include data *
然而,为了在安装时将这些文件复制到site-packages内的包的文件夹中,您需要向setup()函数提供include_package_data=True。有关更多信息,请参见添加非代码文件。
要实现你所描述的需要两步……
需要将该文件添加到源压缩文件中 需要修改Setup.py,将数据文件安装到源路径
步骤1:要将文件添加到源tarball,请将其包含在MANIFEST中
在包含setup.py的文件夹中创建MANIFEST模板
MANIFEST基本上是一个文本文件,其中包含将包含在源tarball中的所有文件的列表。
下面是我项目的MANIFEST:
CHANGELOG.txt INSTALL.txt LICENSE.txt pypreprocessor.py 固定 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'])]
注意:上面的步骤应该以标准的方式完成您所描述的工作,而不需要任何扩展库。
在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
},