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

在...创建的问题 Thu, Mar 8, 2018 12:00 AM

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

 
 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文件呈现给用户时保留文件树,因此保留这个文件对我们有利。文件结构完好无损。

    
38
  1. 您想将所有这些文件编译为.exe?
    2014-12-27 05:01:42Z
  2. 不,只是希望它们可以在程序中访问。如果以这种方式安装它们,我可以在名为data_dir的路径中访问它们:pkg_dir = op.abspath(op.dirname( file ))data_dir = op.join(pkg_dir,'data')然后我可以在我的程序中打开('data_dir /somedatafile','r')其中data_dir将引用它们的安装位置。
    2014-12-27 05:05:56Z
  3. 醇>
    5答案                              5 跨度>                         
    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()
      
    24
    2019-05-02 20:34:34Z
    1. 这个答案澄清了数据文件和包文件之间的区别: stackoverflow.com/questions/4519127 /...
      2016-09-16 22:42:01Z
    2. 链接破碎的
      2019-05-02 20:28:17Z
    3. @ msudder:已修复。有人应该真的教这些人,酷的URI不会改变......
      2019-05-02 20:34:54Z
    4. 醇>

    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时,您将看到列出的其他文件(与包一起跟踪)。

        
    32
    2016-06-03 11:33:48Z
    1. 而不是paths.append('../' + os.path.join(path, filename))paths.append(os.path.join('..', path, filename))
      2016-06-02 15:46:24Z
    2. @ MadPhysicist谢谢。编辑我的答案。我一直在使用os.path.join错误。我没有意识到它需要可变数量的参数。
      2016-06-03 11:34:34Z
    3. 醇>

    如果您没有任何问题,请将setup.py代码弄脏,请使用distutils.dir_util.copy_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)
    

    一些笔记

  4. 此代码以递归方式将源代码复制到目标路径中。
  5. 您可以使用相同的setup(...),但使用copy_tree()将您想要的目录扩展到安装路径。
  6. distutil安装的默认路径可以在 API 中找到。
  7. 有关distutils的copy_tree()模块的更多信息,请访问这里。     
  8. 4
    2016-02-09 11:11:38Z
    1. 我比这个解决方案更喜欢这个解决方案。它适用于通用目录层次结构,而不是使用glob构建一个。即使使用os.walk也会比glob IMO好。
      2016-04-18 11:16:36Z
    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,
    )
    
        
    2
    2017-07-19 15:40:31Z

    使用glob选择setup.py

    中的所有子文件夹  
    ...
    packages=['your_package'],
    package_data={'your_package': ['data/**/*']},
    ...
    
        
    2
    2019-03-30 14:15:07Z
来源放置 这里