Cython не может импортировать и выполнять функцию: хранение небезопасных C производных от временной Python ссылки - PullRequest
2 голосов
/ 24 февраля 2020

Я новичок в Cython и просто пытаюсь поэкспериментировать с простым скриптом. Я не могу импортировать функцию из модуля и использовать ее в другом файле, но если я объявляю функцию в том же файле, она работает просто отлично. В чем может быть проблема здесь. Я что-то упустил?

Это оригинальный фрагмент кода (test_cy.pyx):

# test_cy.pyx

import json

cpdef char* say_hello():
  message = json.dumps({"message": "Hello world"})
  return message.encode()

cdef char* fn(int n):
  cdef char* hello = say_hello()
  return hello

def test():
  cdef char* n = fn(1000)
  print(n)

Я могу запустить его как (После компиляции):

>>> import test_cy
>>> test_cy.test()
b'Hello world'

Однако, если я перенесу функцию say_hello в другой файл (utils.pyx):

# utils.pyx

import json

cpdef char* say_hello():
  message = json.dumps({"message": "Hello world"})
  return message.encode()

... и импортирую ее в мой оригинальный файл test_cy.pyx, например, так:

# test_cy.pyx

from utils import say_hello

cdef char* fn(int n):
  cdef char* hello = say_hello()
  return hello

def test():
  cdef char* n = fn(1000)
  print(n)

Я не могу его скомпилировать, так как получаю следующую ошибку:

>>> python3 setup.py build_ext --inplace
...
Error compiling Cython file:
------------------------------------------------------------
...
from utils import say_hello

cdef char* fn(int n):
  cdef char* hello = say_hello()
      ^
------------------------------------------------------------

test_cy.pyx:4:7: Storing unsafe C derivative of temporary Python reference
Traceback (most recent call last):
  File "setup.py", line 11, in <module>
    sources=["utils.pyx"]
  File "/Users/asim/.pyenv/versions/3.6.9/lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1101, in cythonize
    cythonize_one(*args)
  File "/Users/asim/.pyenv/versions/3.6.9/lib/python3.6/site-packages/Cython/Build/Dependencies.py", line 1224, in cythonize_one
    raise CompileError(None, pyx_file)
Cython.Compiler.Errors.CompileError: test_cy.pyx

Вот мой файл setup.py:

from distutils.core import setup
from distutils.extension import Extension
from Cython.Build import cythonize

setup(
  ext_modules = cythonize([
    Extension("test_cy",
              sources=["test_cy.pyx"]
    ),
    Extension("utils",
              sources=["utils.pyx"]
    )
  ])
)

Пожалуйста, помогите мне как-нибудь так просто например, импорт функции из другого модуля не должен быть таким сложным. Я мог бы пропустить что-то тривиальное. Спасибо!

1 Ответ

1 голос
/ 24 февраля 2020
cpdef char* say_hello():
  message = json.dumps({"message": "Hello world"})
  return message.encode()

Это катастрофа, ожидающая наступления (хотя, похоже, Cython не может выдать предупреждение). Данные, на которые указывает char*, хранятся в результате message.encode() - временного объекта Python, который уничтожается сразу после его создания. Поэтому ваш char* мгновенно становится недействительным.

Подумайте об этом в C терминах: чтобы вернуть char*, кому-то принадлежит char*, и вы должны точно знать , кто этот владелец Нет простого и разумного способа вернуть char*, не принимая на себя ответственность за распределение / освобождение себя. Если вы не готовы сделать это и не понимаете, как это сделать, я предлагаю вам удалить все char* из вашего кода и просто вернуть Python объектов.


Ошибка, которую вы получаете, в частности, потому что Cython использует cimport, чтобы узнать о функциях Cython.

from utils cimport say_hello

Без cimport он считает, что say_hello - это просто обычная Python функция, возвращающая обычный Python объект. Однако даже с этим изменением ваш код все еще недействителен - вы только что переместили ошибку куда-то, что Cython не может ее увидеть.

...