如何在Python setup.py中递归添加包数据?
我有一个新的库,它必须包含许多小数据文件的子文件夹,我正在尝试将它们添加为包数据。假设我的图书馆是这样的:如何在Python setup.py中递归添加包数据?,python,distutils,setup.py,Python,Distutils,Setup.py,我有一个新的库,它必须包含许多小数据文件的子文件夹,我正在尝试将它们添加为包数据。假设我的图书馆是这样的: library - foo.py - bar.py data subfolderA subfolderA1 subfolderA2 subfolderB subfolderB1 ... 我想通过setup.py添加所有子文件夹中的所有数据,但似乎我必须手动进入每个子文件夹(大约有100个)并添加一个init
library
- foo.py
- bar.py
data
subfolderA
subfolderA1
subfolderA2
subfolderB
subfolderB1
...
我想通过setup.py添加所有子文件夹中的所有数据,但似乎我必须手动进入每个子文件夹(大约有100个)并添加一个init.py文件。此外,setup.py是否会递归查找这些文件,或者是否需要手动将所有这些文件添加到setup.py中,如:
package_data={
'mypackage.data.folderA': ['*'],
'mypackage.data.folderA.subfolderA1': ['*'],
'mypackage.data.folderA.subfolderA2': ['*']
},
我可以用一个脚本来完成这项工作,但这似乎非常痛苦。如何在setup.py中实现这一点
PS,这些文件夹的层次结构很重要,因为这是一个材料文件的数据库,我们希望在GUI中向用户显示它们时保留文件树,因此保持此文件结构完整对我们有利
\uuuu init\uuuuu.py
data_files = []
directories = glob.glob('data/subfolder?/subfolder??/')
for directory in directories:
files = glob.glob(directory+'*')
data_files.append((directory, files))
# then pass data_files to setup()
如果将setup.py代码弄脏没有任何问题,请使用
distutils.dir\u util.copy\u tree
整个问题是如何从中排除文件。
下面是一些代码:
import os.path
from distutils import dir_util
from distutils import sysconfig
from distutils.core import setup
__packagename__ = 'x'
setup(
name = __packagename__,
packages = [__packagename__],
)
destination_path = sysconfig.get_python_lib()
package_path = os.path.join(destination_path, __packagename__)
dir_util.copy_tree(__packagename__, package_path, update=1, preserve_mode=0)
一些注释:设置(…)
,但可以使用copy_tree()
将所需目录扩展到安装路径中copy_树
答案的问题是,卸载时会留下复制的文件
正确的解决方案是递归的,它允许您在设置调用中设置package\u data
参数
我编写了这个小方法来实现这一点:
import os
def package_files(directory):
paths = []
for (path, directories, filenames) in os.walk(directory):
for filename in filenames:
paths.append(os.path.join('..', path, filename))
return paths
extra_files = package_files('path_to/extra_files_dir')
setup(
...
packages = ['package_name'],
package_data={'': extra_files},
....
)
您会注意到,当您执行
pip卸载程序包\u name
时,您会看到列出了其他文件(与程序包一起跟踪)。我可以建议您使用一些代码在setup()中添加数据文件:
使用glob选择setup.py中的所有子文件夹
...
packages=['your_package'],
package_data={'your_package': ['data/**/*']},
...
要使用setup.py中的package_数据添加所有子文件夹,请执行以下操作: 根据子目录结构添加*条目数
package_data={
'mypackage.data.folderA': ['*','*/*','*/*/*'],
}
我可以用一个脚本来完成这项工作,但这似乎非常痛苦。如何在setup.py中实现这一点
以下是一种可重复使用的简单方法:
在setup.py
中添加以下函数,并根据使用说明调用它。这基本上是公认答案的通用版本
def find_package_data(specs):
"""recursively find package data as per the folders given
Usage:
# in setup.py
setup(...
include_package_data=True,
package_data=find_package_data({
'package': ('resources', 'static')
}))
Args:
specs (dict): package => list of folder names to include files from
Returns:
dict of list of file names
"""
return {
package: list(''.join(n.split('/', 1)[1:]) for n in
flatten(glob('{}/{}/**/*'.format(package, f), recursive=True) for f in folders))
for package, folders in specs.items()}
@gbonetti使用递归全局模式,即***
,将是完美的
然而,正如@daniel himmelstein所评论的,在setuptoolspackage\u data
中
因此,目前,我喜欢使用以下基于pathlib
的变通方法:
这将返回相对于包路径的路径字符串列表,如下所示
这里有一种使用方法:
setuptools.setup(
...
package_data={'my_package': [*glob_fix('my_package', 'my_data_dir/**/*'),
'my_other_dir/some.file', ...], ...},
...
)
只要setuptools支持
**
中的package\u data
中的glob\u fix()
就可以删除。如果以这种方式安装,我可以在名为data\u dir
的路径中访问它们:pkg\u dir=op.abspath(op.dirname(file))data\u dir=op.join(pkg\u dir,'data'),然后我可以在我的程序open('data\u dir/somedatafile,'r')中执行操作,其中data\u dir将引用它们安装的任何位置。而不是path.append('../'+os.path.join(path,filename))
dopath.append(os.path.join('..',path,filename))
@madpysicast谢谢。编辑了我的答案。我一直在使用os.path.join
错误。我没有意识到它需要可变数量的参数。谢谢,非常有用。值得注意的是,为了让它工作,我必须通过添加directory=str来包含setup.py
文件所在目录的路径(pathlib.Path(uuu file_uuu).parent.absolute())+str(pathlib.Path(directory))
作为包文件(directory)
方法中的第一行。文档说要始终使用正斜杠,而不是os.Path.join;请参阅此答案澄清数据文件和包文件之间的区别:根据,setuptools不支持递归glob。
def glob_fix(package_name, glob):
# this assumes setup.py lives in the folder that contains the package
package_path = Path(f'./{package_name}').resolve()
return [str(path.relative_to(package_path))
for path in package_path.glob(glob)]
setuptools.setup(
...
package_data={'my_package': [*glob_fix('my_package', 'my_data_dir/**/*'),
'my_other_dir/some.file', ...], ...},
...
)