属性错误:使用 petsc4py 'list'对象没有属性'rfind'



当我使用petsc4py和cython的组合时,我面临一个错误:AttributeError:"list"对象没有属性"rfind"

咆哮的代码是/petsc4py/demo/wrapp-cython/setup.py,该项目的一个示例,其中有一个错误:

def configuration(parent_package='',top_path=None):
INCLUDE_DIRS = []
LIBRARY_DIRS = []
LIBRARIES    = []
# PETSc
import os
PETSC_DIR  = os.environ['PETSC_DIR']
PETSC_ARCH = os.environ.get('PETSC_ARCH', '')
from os.path import join, isdir
if PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH)):
    INCLUDE_DIRS += [join(PETSC_DIR, PETSC_ARCH, 'include'),
                     join(PETSC_DIR, 'include')]
    LIBRARY_DIRS += [join(PETSC_DIR, PETSC_ARCH, 'lib')]
else:
    if PETSC_ARCH: pass # XXX should warn ...
    INCLUDE_DIRS += [join(PETSC_DIR, 'include')]
    LIBRARY_DIRS += [join(PETSC_DIR, 'lib')]
LIBRARIES += [#'petscts', 'petscsnes', 'petscksp',
              #'petscdm', 'petscmat',  'petscvec',
              'petsc']
# PETSc for Python
import petsc4py
INCLUDE_DIRS += [petsc4py.get_include()]
# Configuration
from numpy.distutils.misc_util import Configuration
config = Configuration('', parent_package, top_path)
config.add_extension('_Bratu3D',
                     sources = ['Bratu3D.pyx',
                                'Bratu3Dimpl.c'],
                     depends = ['Bratu3Dimpl.h'],
                     include_dirs=INCLUDE_DIRS + [os.curdir],
                     libraries=LIBRARIES,
                     library_dirs=LIBRARY_DIRS,
                     runtime_library_dirs=LIBRARY_DIRS)
return config
if __name__ == "__main__":
    from numpy.distutils.core import setup
    setup(**configuration(top_path='').todict())

错误消息为:

CC=/usr/local/openmpi-1.10.2/bin/mpicc F90=/usr/local/openmpi-1.10.2/bin/mpif90 LDSHARED='/usr/local/openmpi-1.10.2/bin/mpicc -fPIC  -Wall -Wwrite-strings -Wno-strict-aliasing -Wno-unknown-pragmas -fvisibility=hidden -g3  -shared' 
    python setup.py -q build_ext --inplace
Traceback (most recent call last):
  File "setup.py", line 66, in <module>
    setup(**configuration(top_path='').todict())
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/site-packages/numpy/distutils/core.py", line 169, in setup
    return old_setup(**new_attr)
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/distutils/core.py", line 148, in setup
    dist.run_commands()
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/distutils/dist.py", line 955, in run_commands
    self.run_command(cmd)
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/site-packages/numpy/distutils/command/build_ext.py", line 82, in run
    self.run_command('build_src')
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/distutils/cmd.py", line 313, in run_command
    self.distribution.run_command(command)
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/distutils/dist.py", line 974, in run_command
    cmd_obj.run()
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/site-packages/numpy/distutils/command/build_src.py", line 147, in run
    self.build_sources()
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/site-packages/numpy/distutils/command/build_src.py", line 164, in build_sources
    self.build_extension_sources(ext)
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/site-packages/numpy/distutils/command/build_src.py", line 329, in build_extension_sources
    sources, py_files = self.filter_py_files(sources)
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/site-packages/numpy/distutils/command/build_src.py", line 389, in filter_py_files
    return self.filter_files(sources, ['.py'])
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/site-packages/numpy/distutils/command/build_src.py", line 398, in filter_files
    (base, ext) = os.path.splitext(source)
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/posixpath.py", line 122, in splitext
    return genericpath._splitext(p, sep, None, extsep)
  File "/home/zhangji/anaconda3/envs/obenv/lib/python3.5/genericpath.py", line 118, in _splitext
    sepIndex = p.rfind(sep)
AttributeError: 'list' object has no attribute 'rfind'
make: *** [Bratu3D.so] Error 1

另一个例子在/petsc4py/demo/wrapp-swig/setup.py有几乎相同的代码,但没有任何错误。唯一的例外是:

config.add_extension('_Bratu3D',
                     sources = ['Bratu3D.i',
                                'Bratu3D.c'],
                     depends = ['Bratu3D.h'],
                     include_dirs=INCLUDE_DIRS + [os.curdir],
                     libraries=LIBRARIES,
                     library_dirs=LIBRARY_DIRS,
                     runtime_library_dirs=LIBRARY_DIRS)

非常感谢。

我在项目petsc4py/demo/wrap-cython 中遇到了同样的问题

事实上,该代码在这个文件上进行了一些猴子补丁,效果很好:numpy/distutils/command/build_src.py

该方法在numpy 1.11.0上定义为:

def generate_a_pyrex_source(self, base, ext_name, source, extension):
    """Pyrex is not supported, but some projects monkeypatch this method.
    That allows compiling Cython code, see gh-6955.
    This method will remain here for compatibility reasons.
    """
    return []

您必须使用自己的定义覆盖此方法。以下是用python 3.5.1、petsc4py 2.0.0:测试的更新脚本

包装cython/setup.py

#!/usr/bin/env python
# $ python setup.py build_ext --inplace
from numpy.distutils.command import build_src
# a bit of monkeypatching ...
import Cython.Compiler.Main
build_src.Pyrex = Cython
build_src.have_pyrex = True

def have_pyrex():
    import sys
    try:
        import Cython.Compiler.Main
        sys.modules['Pyrex'] = Cython
        sys.modules['Pyrex.Compiler'] = Cython.Compiler
        sys.modules['Pyrex.Compiler.Main'] = Cython.Compiler.Main
        return True
    except ImportError:
        return False
build_src.have_pyrex = have_pyrex
##########################
# BEGIN additionnal code #
##########################
from numpy.distutils.misc_util import appendpath
from numpy.distutils import log
from os.path import join as pjoin, dirname
from distutils.dep_util import newer_group
from distutils.errors import DistutilsError

def generate_a_pyrex_source(self, base, ext_name, source, extension):
    ''' Monkey patch for numpy build_src.build_src method
    Uses Cython instead of Pyrex.
    Assumes Cython is present
    '''
    if self.inplace:
        target_dir = dirname(base)
    else:
        target_dir = appendpath(self.build_src, dirname(base))
    target_file = pjoin(target_dir, ext_name + '.c')
    depends = [source] + extension.depends
    if self.force or newer_group(depends, target_file, 'newer'):
        import Cython.Compiler.Main
        log.info("cythonc:> %s" % (target_file))
        self.mkpath(target_dir)
        options = Cython.Compiler.Main.CompilationOptions(
            defaults=Cython.Compiler.Main.default_options,
            include_path=extension.include_dirs,
            output_file=target_file)
        cython_result = Cython.Compiler.Main.compile(source, options=options)
        if cython_result.num_errors != 0:
            raise DistutilsError("%d errors while compiling %r with Cython" % (cython_result.num_errors, source))
    return target_file
build_src.build_src.generate_a_pyrex_source = generate_a_pyrex_source
########################
# END additionnal code #
########################

def configuration(parent_package='', top_path=None):
    INCLUDE_DIRS = []
    LIBRARY_DIRS = []
    LIBRARIES = []
    # PETSc
    import os
    PETSC_DIR = os.environ['PETSC_DIR']
    PETSC_ARCH = os.environ.get('PETSC_ARCH', '')
    from os.path import join, isdir
    if PETSC_ARCH and isdir(join(PETSC_DIR, PETSC_ARCH)):
        INCLUDE_DIRS += [join(PETSC_DIR, PETSC_ARCH, 'include'),
                         join(PETSC_DIR, 'include')]
        LIBRARY_DIRS += [join(PETSC_DIR, PETSC_ARCH, 'lib')]
    else:
        if PETSC_ARCH:
            pass  # XXX should warn ...
        INCLUDE_DIRS += [join(PETSC_DIR, 'include')]
        LIBRARY_DIRS += [join(PETSC_DIR, 'lib')]
    LIBRARIES += [  # 'petscts', 'petscsnes', 'petscksp',
        # 'petscdm', 'petscmat',  'petscvec',
        'petsc']
    # PETSc for Python
    import petsc4py
    INCLUDE_DIRS += [petsc4py.get_include()]
    # Configuration
    from numpy.distutils.misc_util import Configuration
    config = Configuration('', parent_package, top_path)
    config.add_extension('Bratu3D',
                         sources=['Bratu3D.pyx', 'Bratu3Dimpl.c'],
                         depends=['Bratu3Dimpl.h'],
                         include_dirs=INCLUDE_DIRS + [os.curdir],
                         libraries=LIBRARIES,
                         library_dirs=LIBRARY_DIRS,
                         runtime_library_dirs=LIBRARY_DIRS)
    return config
if __name__ == "__main__":
    from numpy.distutils.core import setup
    setup(**configuration(top_path='').todict())

您需要添加

from Cython.Build import cythonize
config.ext_modules[-1] = cythonize(config.ext_modules[-1])

config.add_extension(...) 之后

问题是sources最终包含了一个由generate_a_pyrex_source添加的空列表。这是因为它本身不知道如何处理cython文件,所以您需要在模块上运行cythonize来告诉它如何处理(如cython文档中所示)。

自己解决这个问题的方法是使用python调试器。运行python3 -m pdb setup.py -q build_ext --inplace,键入cont使其初始运行。你会得到例外。您可以键入up在堆栈中向上移动。

(base, ext) = os.path.splitext(source)行,键入print(source)以查看它是一个空列表。

sources, py_files = self.filter_py_files(sources)行键入print(sources)以查看[[], 'Bratu3Dimpl.c']。这只是在第329行之前查看distutils/build_src.py的情况(调用self.filter_py_files以查看它可能出错的地方)。

我已经给代码编写者发了电子邮件,他说NumPy distutils支持进化,现在代码坏了。然后重新编写了安装文件。我已经测试过它在我的系统中运行良好。

最新更新