如何在Python setup.py中递归添加包数据?

Das*_*hes 38 python distutils setup.py

我有一个新的库,必须包含很多小数据文件的子文件夹,我正在尝试将它们添加为包数据.想象一下,我有我的图书馆:

 library
    - foo.py
    - bar.py
 data
   subfolderA
      subfolderA1
      subfolderA2
   subfolderB
      subfolderB1 
      ...
Run Code Online (Sandbox Code Playgroud)

我想通过setup.py添加所有子文件夹中的所有数据,但似乎我手动必须进入每个子文件夹(有100个左右)并添加一个init .py文件.此外,setup.py会递归地找到这些文件,还是需要在setup.py中手动添加所有这些文件,如:

package_data={
  'mypackage.data.folderA': ['*'],
  'mypackage.data.folderA.subfolderA1': ['*'],
  'mypackage.data.folderA.subfolderA2': ['*']
   },
Run Code Online (Sandbox Code Playgroud)

我可以用脚本做到这一点,但似乎是一种超级痛苦.我怎样才能在setup.py中实现这一点?

PS,这些文件夹的层次结构非常重要,因为这是一个包含材料文件的数据库,我们希望在向用户提供GUI时保留文件树,因此保持文件结构的完整性对我们有利. .

San*_*man 37

glob答案的问题在于它只做了这么多.即它不是完全递归的.copy_tree答案的问题是复制的文件将在卸载时留下.

正确的解决方案是递归解决方案,您可以package_data在setup调用中设置参数.

我写了这个小方法来做到这一点:

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},
    ....
)
Run Code Online (Sandbox Code Playgroud)

您会注意到,当您执行某项操作pip uninstall package_name时,您将看到列出的其他文件(与程序包一起跟踪).

  • 而不是 `paths.append('../' + os.path.join(path, filename))` 而是 `paths.append(os.path.join('..', path, filename))` (2认同)
  • 文档说始终使用正斜杠,而不是 os.path.join;请参阅 https://setuptools.readthedocs.io/en/latest/setuptools.html#include-data-files (2认同)

Kev*_*vin 27

  1. 使用Setuptools而不是distutils.
  2. 使用数据文件而不是包数据.这些不需要__init__.py.
  3. 使用标准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()
    
    Run Code Online (Sandbox Code Playgroud)

  • 这个答案澄清了数据文件和包文件之间的区别:http://stackoverflow.com/questions/4519127/setuptools-package-data-folder-location (3认同)

mah*_*esh 13

要使用 setup.py 中的 package_data 添加所有子文件夹:根据您的子目录结构添加 * 条目的数量

package_data={
  'mypackage.data.folderA': ['*','*/*','*/*/*'],
}
Run Code Online (Sandbox Code Playgroud)


djv*_*jvg 8

更新

根据更改日志, setuptools现在支持递归 glob,使用**, in package_data(截至v62.3.02022 年 5 月发布)。

原答案

@gbonetti 的答案,使用递归 glob 模式,即**,将是完美的。

然而,正如 @daniel-himmelstein 所评论的,这在 setuptools 中还不起作用package_data。

因此,目前,我喜欢使用以下基于pathlibPath.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)]
Run Code Online (Sandbox Code Playgroud)

这将根据需要返回相对于包路径的路径字符串列表。

这是使用它的一种方法:

setuptools.setup(
    ...
    package_data={'my_package': [*glob_fix('my_package', 'my_data_dir/**/*'), 
                                 'my_other_dir/some.file', ...], ...},
    ...
)
Run Code Online (Sandbox Code Playgroud)

一旦setuptoolsglob_fix()支持.**package_data


gbo*_*tti 7

使用 glob 选择 setup.py 中的所有子文件夹

...
packages=['your_package'],
package_data={'your_package': ['data/**/*']},
...
Run Code Online (Sandbox Code Playgroud)

  • 根据 [pypa/setuptools#1806](https://github.com/pypa/setuptools/issues/1806),setuptools 不支持递归 glob。 (4认同)
  • 有趣的是, `package_data={'': ['**/*.yml']}` 可以在我的整个项目文件夹中递归地获取所有 `.yml` 文件。当然,这不是一个理想的解决方案。但值得注意的是。 (2认同)