如何为Cython自动编写setup.py文件并使用subprocess.Popen运行它?

时间:2015-03-03 17:11:47

标签: python compilation installation cython

我的想法是,每次我想编译cython模块或包时,我都不必自己编写一个setup.py文件,而是创建一个函数。该函数还应该自己运行创建的setup.py。

所以问题也在于“你如何编写代码编写代码?”。

1 个答案:

答案 0 :(得分:0)

我找到了一个有效的解决方案。 使用以下代码创建compile_cython.py文件:

import os
from subprocess import Popen
temp_dir = os.environ['TEMP']


def package(directory, packages, logfile=False, byproducts=None, annotate=False, language='C'):
    """Compile cython .pyx files to a .pyd file inplace
    :param directory: the directory where the main package lies.
    :param packages: List of packages to compile. A package is described in a
        :type tuple that consists of package name, relative path from main package, list of include files,
        list of include directories and the language (default is C).
    :param logfile: Path or :type bool. If true, a log will be created in :param directory:.
    :param byproducts: The directory in which the by-products will be stored.
        If None, by-products will be saved in "%TEMP%\cython_byproducts".
    :param annotate: The Cython annotate option.
    :param language: Default language

    Example:
    package('C:\\projects', [('graphics', 'game\\graphics', ['OpenGL32'], [r'C:\Program Files\Microsoft SDKs\Windows\v7.1\Lib\x64']])
    """
    if not byproducts: byproducts = os.path.join(temp_dir, "cython_byproducts")
    if logfile is True: logfile = os.path.join(directory, "log.txt")
    elif not logfile: logfile = os.path.join(byproducts, "log.txt")
    if isinstance(packages, str):
        dir, package = os.path.split(packages)
        packages = [(package, package)]


    setup_file = os.path.join(byproducts, "setup.py")
    #Write a setup file
    with open(setup_file, 'w') as file:
        file.write("""
from distutils.core import setup, Extension
from Cython.Build import cythonize
import numpy as np
import sys
print(sys.argv) #print for logging

packages = {packages}

extensions = []
for package in packages:
    default = ['name', 'path', [], [], '{default_language}']
    default[:len(package)]=package
    extensions.append(Extension("%s.*" %default[0], ["%s\\\\*.pyx" %default[1]], libraries=default[2],
    library_dirs=default[3], language=default[4].lower()))

setup(include_dirs = np.get_include(),ext_modules = cythonize(extensions, annotate={annotate})) # accepts a glob pattern
""".format(packages=packages, annotate=annotate, default_language=language)
                        )
    p = Popen(r'python "{setup}" build_ext --inplace --build-temp "{byproducts}"'.format(setup=setup_file,
                                                                                         byproducts=byproducts,)                                                                                         ,
                                                                                        cwd=directory)
    with open(logfile, 'w') as log:
        stdout, stderr = p.communicate()
        log.write(stdout or 'NO OUTPUT\n')
        log.write(stderr or 'NO ERRORS\n')

def module(directory, modules, logfile=False, byproducts=None, annotate=False, language='C'):
    """Compile cython .pyx files to a .pyd file inplace
    :param directory: the directory where the module lies
    :param modules: The Path relative from :param directory or a List of modules to compile. A module is described in a
        :type tuple that consists of module name, relative path from :param directory, list of include files,
        list of include directories and the language (default is C).
    :param logfile: Path or :type bool. If true, a log will be created in :param directory:.
    :param byproducts: The directory in which the by-products will be stored.
        If None, by-products will be saved in "%TEMP%\cython_byproducts".
    :param annotate: The Cython annotate option.
    :param language: Default language

    Example:
    module('C:\\projects', [('effects', 'game\\graphics\\effects.pyx'])
    """
    if not byproducts: byproducts = os.path.join(temp_dir, "cython_byproducts")
    if logfile is True: logfile = os.path.join(directory, "log.txt")
    elif not logfile: logfile = os.path.join(byproducts, "log.txt")
    if isinstance(modules, str):
        dir, name_ext = os.path.split(modules)
        name, ext = os.path.splitext(name_ext)
        modules = [(name, name_ext)]

    setup_file = os.path.join(byproducts, "setup.py")
    #Write a setup file
    with open(setup_file, 'w') as file:
        file.write("""
from distutils.core import setup, Extension
from Cython.Build import cythonize
import numpy as np
import sys
print(sys.argv) #print for logging

modules = {modules}

extensions = []
for module in modules:
    default = ['name', 'path', [], [], '{default_language}']
    default[:len(module)]=module
    extensions.append(Extension("%s" %default[0], ["%s" %default[1]], libraries=default[2],
    library_dirs=default[3], language=default[4].lower()))

setup(include_dirs = np.get_include(),ext_modules = cythonize(extensions, annotate={annotate})) # accepts a glob pattern
""".format(modules=modules, annotate=annotate, default_language=language)
                        )
    p = Popen(r'python "{setup}" build_ext --inplace --build-temp "{byproducts}"'.format(setup=setup_file,
                                                                                         byproducts=byproducts,)                                                                                         ,
                                                                                         cwd=directory)
    with open(logfile, 'w') as log:
        stdout, stderr = p.communicate()
        log.write(stdout or 'NO OUTPUT\n')
        log.write(stderr or 'NO ERRORS\n')

还需要进行一些修改才能编译纯C或C ++文件,但它适用于Cython文件。

在此设置中,自动包含Numpy。但我想这没有问题,因为除非你使用numpy函数,否则它不会包含任何东西,或者它是什么?