Me enfrento a un error cuando uso una combinación de petsc4py y cython: AttributeError: el objeto 'list' no tiene el atributo 'rfind'

El siguiente código es parte de ... / petsc4py / demo / wrap-cython / setup.py, un ejemplo del proyecto, que tiene un error:

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())

Los mensajes de error son:

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

Otro ejemplo en ... / petsc4py / demo / wrap-swig / setup.py tiene casi el mismo código pero sin ningún error. La única excepción es:

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)

Muchas gracias.

2
Ji Zhang 12 may. 2016 a las 08:32

3 respuestas

La mejor respuesta

He tenido el mismo problema con el proyecto petsc4py/demo/wrap-cython

De hecho, el código funciona bien con un parche de mono en este archivo: numpy/distutils/command/build_src.py

El método se define en numpy 1.11.0 como:

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 []

Debe anular este método con su propia definición. Aquí está el script actualizado probado con python 3.5.1, petsc4py 2.0.0:

wrap-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())
1
neok 13 nov. 2016 a las 18:12

Necesitas agregar

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

Justo después de config.add_extension(...)

El problema fue que sources terminó conteniendo una lista vacía, agregada por generate_a_pyrex_source. Esto se debe a que no sabe por sí mismo cómo tratar con los archivos de cython, por lo que debe ejecutar cythonize en el módulo para indicarle cómo hacerlo (como se demuestra en documentación de cython).

La forma de resolver esto por sí mismo es usar el depurador de python. Ejecute python3 -m pdb setup.py -q build_ext --inplace, escriba cont para que se ejecute inicialmente. Obtendrás la excepción. Puede escribir up para avanzar por la pila.

En la línea (base, ext) = os.path.splitext(source) escriba print(source) para ver que es una lista vacía.

En la línea sources, py_files = self.filter_py_files(sources) escriba print(sources) para ver [[], 'Bratu3Dimpl.c']. Es solo un caso de mirar a través de distutils/build_src.py justo antes de la línea 329 (donde se llama a self.filter_py_files para ver dónde podría haber salido mal.

0
DavidW 12 may. 2016 a las 17:44

Le envié un correo electrónico al escritor del código, y él dijo que el soporte de NumPy distutils evolucionó y ahora el código está roto. Luego, el archivo de instalación se ha reescrito. He probado que funciona bien en mi sistema.

0
Ji Zhang 15 may. 2016 a las 04:37