Python distutils 错误:"[directory]... 不存在或不是常规文件"

28

让我们来看一下下面的项目布局:

$ 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/*',
          ],
      },
     )

如您所见,我希望将package/package/dir/中的所有非Python文件都包含在内。但运行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

怎么回事?


1
这个问题的一个更简单的解决方案在这个答案中给出:https://dev59.com/-WYr5IYBdhLWcg3w6OGd#25375689 - Boris Chervenkov
我赞同@BorisChervenkov的评论。包含include_package_data和对MANIFEST.in的更改组合是一种更简单、更清晰的解决方案。 - SKG
4个回答

21

在你的package_data中,'*'通配符会匹配到package/dir目录本身,并尝试将该目录作为文件进行复制,导致失败。找到一个不会匹配到package/dir的通配符,按照以下方式重写你的setup.py

from distutils.core import setup

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

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

根据您的示例,只需将'*'更改为'*.dat',但您可能需要进一步改进全局通配符的匹配规则,以确保其不会匹配到'dir'


6
啊,谢谢。你知道有没有更优雅的方式来指定“包括这个包中的所有内容(除了模块),并进行递归搜索”吗? - Santa
1
虽然这似乎是解决方案,但 distributils 不能直接处理子目录真的很糟糕!在 Windows 中可以正常工作,但在 Linux 中失败了。我有大量嵌套的子目录,必须明确定义它们,这将是真正的痛苦,并且更难维护。 - BuvinJ
@BuvinJ 是的,这听起来一点也不好。自从我回答这个问题以来,我一直在尝试越来越多地使用 MANIFEST.in 文件,如 这里 所述。不过,对于 Windows/Linux 之间的差异,我无法回答太多问题。 - Jacob Oscarson
作为部分解决方案,添加了逻辑以在Windows和Linux包数据之间切换:import platform if platform.system() == 'Windows' : packageData= ... - BuvinJ
显示剩余2条评论

4

你可以使用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'
          ],
      },
     )

0

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

chart.js-2.4.0 => chart_js-2_4_0

注意:我正在使用Python 2.7.10,SetupTools 12.2


0
我创建了一个函数,它可以给我所有需要的文件。
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 的参数


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接