我有一个新的库,必须包含很多小数据文件的子文件夹,我试图将它们作为包数据添加。想象一下,我有我的图书馆:
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文件呈现给用户时保留文件树,因此保留这个文件对我们有利。文件结构完好无损。
答案 0 :(得分:32)
glob
答案的问题在于它只做了这么多。即它没有完全递归。 copy_tree
答案的问题是复制的文件将在卸载时留下。
正确的解决方案是递归解决方案,您可以在设置调用中设置package_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 uninstall package_name
时,您会看到列出的其他文件(与软件包一起跟踪)。
答案 1 :(得分:24)
__init__.py
。使用标准Python代码生成文件和目录列表,而不是按字面编写:
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()
答案 2 :(得分:4)
如果您在使用distutils.dir_util.copy_tree
时无法解决setup.py代码问题。
整个问题是如何从中排除文件
下面是一些代码:
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)
一些笔记:
setup(...)
,但可以使用copy_tree()
将您想要的目录扩展到安装路径中。
答案 3 :(得分:2)
我可以建议一些代码在setup()中添加data_files:
data_files = []
start_point = os.path.join(__pkgname__, 'static')
for root, dirs, files in os.walk(start_point):
root_files = [os.path.join(root, i) for i in files]
data_files.append((root, root_files))
start_point = os.path.join(__pkgname__, 'templates')
for root, dirs, files in os.walk(start_point):
root_files = [os.path.join(root, i) for i in files]
data_files.append((root, root_files))
setup(
name = __pkgname__,
description = __description__,
version = __version__,
long_description = README,
...
data_files = data_files,
)
答案 4 :(得分:1)
使用glob在setup.py中选择所有子文件夹。
...
packages=['your_package'],
package_data={'your_package': ['data/**/*']},
...
答案 5 :(得分:0)
要使用setup.py中的package_data添加所有子文件夹: 根据您的子目录结构添加*条目数
package_data={
'mypackage.data.folderA': ['*','*/*','*/*/*'],
}
答案 6 :(得分:0)
我可以使用脚本来执行此操作,但是似乎很痛苦。如何在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()}
答案 7 :(得分:0)
@gbonetti的answer(使用递归glob模式,即**
)将是完美的。
但是,正如@ daniel-himmelstein所评论的那样,setuptools package_data
中的does not work yet。
因此,暂时,我想根据pathlib
的{{3}}使用以下变通方法:
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)]
这将返回与包路径有关的路径字符串列表,如Path.glob()。
这是使用它的一种方法:
setuptools.setup(
...
package_data={'my_package': [*glob_fix('my_package', 'my_data_dir/**/*'),
'my_other_dir/some.file', ...], ...},
...
)
只要setuptools在glob_fix()
中支持**
,就可以删除package_data
。