2014-10-06 4 views
3

Есть ли способ получить эквивалент инструкции try для Python в Cython для cimport?Пример утверждения в Cython для cimport (для использования с mpi4py)

Нечто подобное:

try: 
    cimport something 
except ImportError: 
    pass 

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

Я попытался это, но он не работает:

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

ответ

3

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

С другой стороны, в теории вы должны быть способны получить эффект, который вы ищете, используя Cyteon's support for conditional compilation. В файле setup.py вы можете проверить, определены ли необходимые модули, а затем определить переменные среды, которые должны быть переданы компилятору Cython, которые, в свою очередь, зависят от наличия или отсутствия необходимых модулей.

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

1

Благодарим вас за очень полезный ответ @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 не установлен, все равно можно построить модуль и использовать его в последовательном порядке.

+1

Связанный: http://stackoverflow.com/questions/27273302/cython-conditional-compile-based-on-external-value-given-via-setuptools – dubiousjim

Смежные вопросы