Оператор Try в Cython для cimport (для использования с mpi4py)

Есть ли способ получить эквивалент оператора try Python в Cython для cimport?

Что-то такое:

try:
    cimport something
except ImportError:
    pass

Мне нужно это, чтобы написать расширение Cython, которое можно скомпилировать с mpi4py или без него. Это очень стандартно для скомпилированных языков, где команды mpi могут быть помещены между директивами препроцессора #ifdef и #endif. Как мы можем получить тот же результат в Cython?

Я пробовал это, но это не работает:

try:
    from mpi4py import MPI
    from mpi4py cimport MPI
    from mpi4py.mpi_c cimport *
except ImportError:
    rank = 0
    nb_proc = 1

# solve a incompatibility between openmpi and mpi4py versions
cdef extern from 'mpi-compat.h': pass

does_it_work = 'Not yet'

На самом деле это работает хорошо, если mpi4py правильно установлен, но если import mpi4py вызывает ImportError, файл Cython не компилируется, и я получаю сообщение об ошибке:

Error compiling Cython file:
------------------------------------------------------------
...

try:
    from mpi4py import MPI
    from mpi4py cimport MPI
   ^
------------------------------------------------------------

mod.pyx:4:4: 'mpi4py.pxd' not found

Файл setup.py:

from setuptools import setup, Extension
from Cython.Distutils import build_ext

import os
here = os.path.abspath(os.path.dirname(__file__))

include_dirs = [here]

try:
    import mpi4py
except ImportError:
    pass
else:
    INCLUDE_MPI = '/usr/lib/openmpi/include'
    include_dirs.extend([
        INCLUDE_MPI,
        mpi4py.get_include()])

name = 'mod'
ext = Extension(
    name,
    include_dirs=include_dirs,
    sources=['mod.pyx'])

setup(name=name,
      cmdclass={"build_ext": build_ext},
      ext_modules=[ext])

person paugier    schedule 06.10.2014    source источник


Ответы (2)


Вы не сможете использовать блок try-catch таким образом. Модуль расширения, который вы создаете, должен быть статически скомпилирован и связан с тем, что он использует cimport для загрузки на уровне C. Блок try-catch будет выполняться при импорте модуля, а не при его компиляции.

С другой стороны, теоретически вы должны быть в состоянии получить желаемый эффект, используя поддержка условной компиляции. В вашем файле setup.py вы можете проверить, определены ли необходимые модули, а затем определить переменные среды, которые будут переданы компилятору Cython, которые, в свою очередь, зависят от того, присутствуют ли необходимые модули.

Пример того, как это сделать, есть в одном из тестов Cython< /а>. Там они передают словарь, содержащий нужные переменные среды, конструктору класса Cython Extension в качестве аргумента ключевого слова pyrex_compile_time_env, который был переименован в cython_compile_time_env, а для Cython.Build.Dependencies.cythonize называется compile_time_env).

person IanH    schedule 07.10.2014

Спасибо за ваш очень полезный ответ @IanH. Я включаю пример, чтобы показать, что это дает.

Файл setup.py:

from setuptools import setup
from Cython.Distutils.extension import Extension
from Cython.Distutils import build_ext

import os
here = os.path.abspath(os.path.dirname(__file__))

import numpy as np
include_dirs = [here, np.get_include()]

try:
    import mpi4py
except ImportError:
    MPI4PY = False
else:
    MPI4PY = True
    INCLUDE_MPI = '/usr/lib/openmpi/include'
    include_dirs.extend([
        INCLUDE_MPI,
        mpi4py.get_include()])

name = 'mod'
ext = Extension(
    name,
    include_dirs=include_dirs,
    cython_compile_time_env={'MPI4PY': MPI4PY},
    sources=['mod.pyx'])

setup(name=name,
      cmdclass={"build_ext": build_ext},
      ext_modules=[ext])

if not MPI4PY:
    print('Warning: since importing mpi4py raises an ImportError,\n'
          '         the extensions are compiled without mpi and \n'
          '         will work only in sequencial.')

И файл mod.pyx с небольшим количеством настоящих mpi команд:

import numpy as np
cimport numpy as np

try:
    from mpi4py import MPI
except ImportError:
    nb_proc = 1
    rank = 0
else:
    comm = MPI.COMM_WORLD
    nb_proc = comm.size
    rank = comm.Get_rank()

IF MPI4PY:
    from mpi4py cimport MPI
    from mpi4py.mpi_c cimport *

    # solve an incompatibility between openmpi and mpi4py versions
    cdef extern from 'mpi-compat.h': pass

    print('mpi4py ok')
ELSE:
    print('no mpi4py')

n = 8
if n % nb_proc != 0:
    raise ValueError('The number of processes is incorrect.')

if rank == 0:
    data_seq = np.ones([n], dtype=np.int32)
    s_seq = data_seq.sum()
else:
    data_seq = np.zeros([n], dtype=np.int32)

if nb_proc > 1:
    data_local = np.zeros([n/nb_proc], dtype=np.int32)
    comm.Scatter(data_seq, data_local, root=0)
else:
    data_local = data_seq

s = data_local.sum()
if nb_proc > 1:
    s = comm.allreduce(s, op=MPI.SUM)

if rank == 0:
    print('s: {}; s_seq: {}'.format(s, s_seq))
    assert s == s_seq

Создайте с помощью python setup.py build_ext --inplace и протестируйте с помощью python -c "import mod" и mpirun -np 4 python -c "import mod". Если mpi4py не установлен, можно собрать модуль и использовать его последовательно.

person paugier    schedule 07.10.2014
comment
Связано: stackoverflow.com/questions/27273302/ - person dubiousjim; 21.02.2017