Coverage for src/ensae_teaching_cs/faq/faq_cython.py: 58%

38 statements  

« prev     ^ index     » next       coverage.py v7.1.0, created at 2023-04-28 06:23 +0200

1# -*- coding: utf-8 -*- 

2""" 

3@file 

4@brief Cython helpers 

5 

6""" 

7 

8import os 

9import sys 

10import warnings 

11 

12from pyquickhelper.loghelper import run_cmd, noLOG 

13 

14 

15class CustomCythonError(Exception): 

16 """ 

17 raised by function @see fn compile_cython_single_script 

18 when a script cannot be compiled with Cython 

19 """ 

20 pass 

21 

22 

23def compile_cython_single_script(script, skip_warn=True, fLOG=noLOG): 

24 """ 

25 This function considers a script ``.pyx``, writes 

26 the proper setup file, and compiles it. 

27 

28 :param script: filename 

29 :param skip_warn: skip warnings 

30 :param fLOG: logging function 

31 :return: output 

32 

33 The function applies the steps described in the basic tutorial 

34 :epkg:`The Basics of Cython`. 

35 The function creates a ``setup.py`` 

36 in the same location and compiles it. 

37 

38 The compilation requires a compiler 

39 (not `MinGW <http://www.mingw.org/>`_ or 

40 `Visual Studio (Community Edition) <https://www.microsoft.com/france/visual-studio/produits/community/Default.aspx>`_). 

41 If none was found, Python usually displays an error message like:: 

42 

43 Unable to find vcvarsall.bat 

44 

45 You can also read this old blog post: 

46 `Build a Python 64 bit extension on Windows <http://www.xavierdupre.fr/blog/2013-07-07_nojs.html>`_ 

47 about this file:: ``C:\\Python35_x64\\lib\\distutils\\msvc9compiler.py``. 

48 

49 .. faqref:: 

50 :tag: cython 

51 :title: Compiler une function Cython ? 

52 

53 Cette fonction compile un script 

54 `Cython <http://cython.org/>`_. 

55 Cette extension permet d'implémenter des fonctions Python dans un 

56 pseudo-langage proche du `C <https://en.wikipedia.org/wiki/C_(programming_language)>`_. 

57 Il faut suivre les instructions décrite dans le tutorial 

58 :epkg:`The Basics of Cython` 

59 pour réussir à utiliser une fonction codée en Cython. 

60 C'est ce que fait la fonction :func:`compile_cython_single_script`. 

61 

62 Etant donné que la partie en pseudo C est compilée afin de la rendre beaucoup 

63 plus rapide, la partie la plus difficile est généralement celle qui consiste à faire 

64 en sorte que l'interpréteur Python trouve le <b>bon</b> compilateur. 

65 Ce compilateur est nécessairement le même que celui utilisé pour compiler 

66 Python et celui-ci change à chaque version. 

67 Voir 

68 `Compiling Python on Windows <https://docs.python.org/3/using/windows.html?highlight=visual%20studio#compiling-python-on-windows>`_ 

69 et faire attention à la version de Python que vous utilisez. 

70 """ 

71 ext = os.path.splitext(script)[-1] 

72 if ext != ".pyx": 

73 raise ValueError("no extension .pyx: " + script) 

74 if not os.path.exists(script): 

75 raise FileNotFoundError(script) 

76 

77 name = os.path.split(script)[-1] 

78 namen = os.path.splitext(name)[0] 

79 setup_script = """ 

80 from setuptools import setup 

81 from Cython.Build import cythonize 

82 setup( 

83 name='{1}', 

84 ext_modules=cythonize("{0}", 

85 compiler_directives={{'language_level': {2}}}) 

86 ) 

87 """.replace(" ", "").format(name, namen, sys.version_info[0]) 

88 

89 current, name = os.path.split(script) 

90 filename = os.path.join(os.path.dirname(script), name + ".setup.py") 

91 with open(filename, "w") as f: 

92 f.write(setup_script) 

93 

94 cmd = sys.executable + f" -u {filename} build_ext --inplace" 

95 

96 out, err = run_cmd(cmd, wait=True, fLOG=fLOG, change_path=current) 

97 if len(err) > 0: 

98 if skip_warn: 

99 do_raise = False 

100 lines = err.split("\n") 

101 for line in lines: 

102 if len(line) > 0 and not line.startswith(" "): 

103 if "UserWarning" not in line: 

104 do_raise = True 

105 break 

106 else: 

107 do_raise = True 

108 if do_raise: 

109 with open(script, "r", encoding="utf-8") as f: 

110 content = f.read() 

111 raise CustomCythonError( 

112 f"CMD:\n{cmd}\nOUT:\n{out}ERR:\n{err}\nSCRIPT:\n{content}") 

113 else: 

114 warnings.warn( 

115 f"[compile_cython_single_script] CMD:\n{cmd}\nOUT:\n{out}ERR:\n{err}") 

116 return out