【问题标题】:Python distutils error: "[directory]... doesn't exist or not a regular file"Python distutils 错误:“[目录] ...不存在或不是常规文件”
【发布时间】:2010-09-14 18:59:51
【问题描述】:

让我们采用以下项目布局:

$ ls -R .
.:
package  setup.py

./package:
__init__.py  dir  file.dat  module.py

./package/dir:
tool1.dat  tool2.dat

以下内容为setup.py

$ cat setup.py 
from distutils.core import setup


setup(name='pyproj',
      version='0.1',

      packages=[
          'package',
      ],
      package_data={
          'package': [
              '*',
              'dir/*',
          ],
      },
     )

如您所见,我想将所有非 Python 文件包含在 package/package/dir/ 目录中。但是,运行 setup.py install 会引发以下错误:

$ python setup.py install
running install
running build
running build_py
creating build
creating build/lib
creating build/lib/package
copying package/module.py -> build/lib/package
copying package/__init__.py -> build/lib/package
error: can't copy 'package/dir': doesn't exist or not a regular file

什么给了?

【问题讨论】:

  • 这个答案给出了一个更简单的解决方案:stackoverflow.com/a/25375689/74632
  • 我第二个@BorisChervenkov 评论。结合 include_package_data 和对 MANIFEST.in 的更改是更简单和更清洁的解决方案。

标签: python distutils


【解决方案1】:

在您的package_data 中,您的'*' glob 将匹配package/dir 本身,并尝试将该目录复制为文件,从而导致失败。查找与目录 package/dir 不匹配的 glob,并按照以下方式重写您的 setup.py

from distutils.core import setup

setup(name='pyproj',
      version='0.1',

      packages=[
          'package',
      ],
      package_data={
          'package': [
              '*.dat',
              'dir/*'
          ],
      },
     )

以您的示例为例,这只是将 '*' 更改为 '*.dat',尽管您可能需要进一步改进您的 glob,但请确保它不会匹配 'dir'

【讨论】:

  • 啊。谢谢。你知道是否有一种更优雅的方式来指定“在这个包中递归地包含所有内容(在模块之上)”?
  • 您可能会通过使用 MANIFEST.in 文件逃脱,但不幸的是,它比理想更困难(正如此搜索 google.se/search?q=distutils+package_data+include+directory 所示,很多人都在问这个问题,但没有多少人得到答案)。最全面的文档是这样的:docs.python.org/distutils/setupscript.html,但写得不是很清楚(为了找到你的问题,我实际上是 pdb'd into distutils source)
  • 虽然这似乎是解决方案,但很糟糕的是 distributils 不能只处理开箱即用的子目录!这在 Windows 中运行良好,但在 Linux 中失败。我有大量的子目录嵌套,并且必须明确定义这将是真正的痛苦并且更难以维护。
  • @BuvinJ 是的,这听起来一点都不好。自从我做出答案以来,我一直在尝试越来越多地使用 MANIFEST.in 文件,如所述,例如here。不过,对于 Windows/Linux 之间的差异,我无法回答太多。
  • 作为部分解决方案,添加了在 Windows 和 Linux 之间切换的逻辑 package_data:import platform if platform.system() == 'Windows' : packageData= ...
【解决方案2】:

您可以使用Distribute 代替 distutils。它的工作原理基本相同(在大多数情况下,您不必更改 setup.py)并且它为您提供了 exclude_package_data 选项:

from distribute_setup import use_setuptools
use_setuptools()

from setuptools import setup

setup(name='pyproj',
      version='0.1',

      packages=[
          'package',
      ],
      package_data={
          'package': [
              '*.dat',
              'dir/*'
          ],
      },
      exclude_package_data={
          'package': [
              'dir'
          ],
      },
     )

【讨论】:

    【解决方案3】:

    我创建了一个函数,它提供了我需要的所有文件

    def find_files(directory, strip):
      """
      Using glob patterns in ``package_data`` that matches a directory can
      result in setuptools trying to install that directory as a file and
      the installation to fail.
    
      This function walks over the contents of *directory* and returns a list
      of only filenames found. The filenames will be stripped of the *strip*
      directory part.
      """
    
      result = []
      for root, dirs, files in os.walk(directory):
        for filename in files:
          filename = os.path.join(root, filename)
          result.append(os.path.relpath(filename, strip))
      return result
    

    并将其用作package_data 的参数

    【讨论】:

      【解决方案4】:

      不太清楚为什么,但经过一些故障排除后,我意识到重命名名称中带有点的目录可以解决问题。例如

      chart.js-2.4.0 => chart_js-2_4_0
      

      注意:我使用的是 Python 2.7.10,SetupTools 12.2

      【讨论】:

        猜你喜欢
        • 2021-02-24
        • 1970-01-01
        • 2015-05-19
        • 2015-04-29
        • 1970-01-01
        • 1970-01-01
        • 2013-10-02
        • 1970-01-01
        • 2018-11-18
        相关资源
        最近更新 更多