# -*- coding: utf-8 -*-
"""
Cython helpers
:githublink:`%|py|7`
"""
import os
import sys
import warnings
from pyquickhelper.loghelper import run_cmd, noLOG
[docs]class CustomCythonError(Exception):
"""
raised by function :func:`compile_cython_single_script <ensae_teaching_cs.faq.faq_cython.compile_cython_single_script>`
when a script cannot be compiled with Cython
:githublink:`%|py|19`
"""
pass
[docs]def compile_cython_single_script(script, skip_warn=True, fLOG=noLOG):
"""
This function considers a script ``.pyx``, writes
the proper setup file, and compiles it.
:param script: filename
:param skip_warn: skip warnings
:param fLOG: logging function
The function applies the steps described in the basic tutorial
:epkg:`The Basics of Cython`.
The function creates a ``setup.py``
in the same location and compiles it.
The compilation requires a compiler
(not `MinGW <http://www.mingw.org/>`_ or
`Visual Studio (Community Edition) <https://www.microsoft.com/france/visual-studio/produits/community/Default.aspx>`_).
If none was found, Python usually displays an error message like::
Unable to find vcvarsall.bat
You can also read this old blog post:
`Build a Python 64 bit extension on Windows <http://www.xavierdupre.fr/blog/2013-07-07_nojs.html>`_
about this file:: ``C:\\Python35_x64\\lib\\distutils\\msvc9compiler.py``.
.. faqref::
:tag: cython
:title: Compiler une function Cython ?
Cette fonction compile un script
`Cython <http://cython.org/>`_.
Cette extension permet d'implémenter des fonctions Python dans un
pseudo-langage proche du `C <https://en.wikipedia.org/wiki/C_(programming_language)>`_.
Il faut suivre les instructions décrite dans le tutorial
:epkg:`The Basics of Cython`
pour réussir à utiliser une fonction codée en Cython.
C'est ce que fait la fonction :func:`compile_cython_single_script <ensae_teaching_cs.faq.faq_cython.compile_cython_single_script>`.
Etant donné que la partie en pseudo C est compilée afin de la rendre beaucoup
plus rapide, la partie la plus difficile est généralement celle qui consiste à faire
en sorte que l'interpréteur Python trouve le <b>bon</b> compilateur.
Ce compilateur est nécessairement le même que celui utilisé pour compiler
Python et celui-ci change à chaque version.
Voir
`Compiling Python on Windows <https://docs.python.org/3/using/windows.html?highlight=visual%20studio#compiling-python-on-windows>`_
et faire attention à la version de Python que vous utilisez.
:githublink:`%|py|69`
"""
ext = os.path.splitext(script)[-1]
if ext != ".pyx":
raise ValueError("no extension .pyx: " + script)
if not os.path.exists(script):
raise FileNotFoundError(script)
name = os.path.split(script)[-1]
namen = os.path.splitext(name)[0]
setup_script = """
from distutils.core import setup
from Cython.Build import cythonize
setup(
name='{1}',
ext_modules=cythonize("{0}",
compiler_directives={{'language_level': {2}}})
)
""".replace(" ", "").format(name, namen, sys.version_info[0])
current, name = os.path.split(script)
filename = os.path.join(os.path.dirname(script), name + ".setup.py")
with open(filename, "w") as f:
f.write(setup_script)
cmd = sys.executable + " -u {0} build_ext --inplace".format(filename)
out, err = run_cmd(cmd, wait=True, fLOG=fLOG, change_path=current)
if len(err) > 0:
if skip_warn:
do_raise = False
lines = err.split("\n")
for line in lines:
if len(line) > 0 and not line.startswith(" "):
if "UserWarning" not in line:
do_raise = True
break
else:
do_raise = True
if do_raise:
with open(script, "r", encoding="utf-8") as f:
content = f.read()
raise CustomCythonError(
"CMD:\n{0}\nOUT:\n{1}ERR:\n{2}\nSCRIPT:\n{3}".format(cmd, out, err, content))
else:
warnings.warn(
"[compile_cython_single_script] CMD:\n{0}\nOUT:\n{1}ERR:\n{2}".format(cmd, out, err))
return out