2017-10-17 2 views
3

Ich habe eine Frage. Ich möchte meine Cython-basierten Pakete verteilen, aber ich sehe keine einfache Möglichkeit, sie in setup.py zu erstellen. Ich möchte setup.py zu:Erstellen Sie ein Paket mit Cython, damit Benutzer es installieren können, ohne Cython bereits installiert zu haben

  • was am wichtigsten ist: mein Paket ohne cython (aus der vorab generierten C-Dateien oder durch vorher Installation cython) installieren
  • Wiederaufbau (run cythonize) Paket auf Sdist
  • nicht Ich muss eine Liste meiner Cython-Module hartcodieren (verwende einfach glob oder sowas)
  • kann ohne .c-Dateien arbeiten (sollte nicht in git gespeichert werden) oder .pyx (kann nicht verteilt werden). Mindestens einer dieser Sätze wird natürlich immer vorhanden sein.

Zeit in meinem juckenden Paket, verwende ich diesen ziemlich komplizierten Code:

import os 
from glob import glob 
from distutils.command.build_ext import build_ext as _build_ext 
from distutils.command.sdist import sdist as _sdist 
from distutils.core import setup 
from distutils.core import Extension 



def generate_extensions(): 
    return [ 
     # Compile cython-generated .c files into importable .so libraries. 
     Extension(os.path.splitext(name)[0], [name]) 
     for name in C_FILES 
    ] 


# In distribution version, there are no pyx files, when you clone package from git, there will be no c files. 
CYTHON_FILES = glob('itchy/*.pyx') 
C_FILES = glob('itchy/*.c') 
extensions = generate_extensions() 


class build_ext(_build_ext): 
    def run(self): 
     # Compile cython files (.pyx, some of the .py) into .c files if Cython is available. 
     try: 
      from Cython.Build import cythonize 
      if CYTHON_FILES: 
       cythonize(CYTHON_FILES) 

       # Update C_FILES in case they were originally missing. 
       global C_FILES, extensions 
       C_FILES = glob('itchy/*.c') 
       extensions = generate_extensions() 
      else: 
       print('No .pyx files found, building extensions skipped. Pre-built versions will be used.') 
     except ImportError: 
      print('Cython is not installed, building extensions skipped. Pre-built versions will be used.') 
      assert C_FILES, 'C files have to be present in distribution or Cython has to be installed' 
     _build_ext.run(self) 


class sdist(_sdist): 
    def run(self): 
     # Make sure the compiled Cython files in the distribution are up-to-date 
     self.run_command("build_ext") 
     _sdist.run(self) 


setup(
    (...) 
    ext_modules = extensions, 
    cmdclass = { 
     'build_ext': build_ext, 
     'sdist': sdist, 
    }, 
) 

Antwort

3

die Regel durch den Versuch gemacht cython und Anpassung Erweiterungen entweder

  1. Erstellen pyx Dateien mit cython zu importieren Wenn Cython vorhanden ist
  2. Erstellen Sie C-Dateien, wenn Cython nicht vorhanden ist

Zum Beispiel:

try: 
    from Cython.Distutils.extension import Extension 
    from Cython.Distutils import build_ext 
except ImportError: 
    from setuptools import Extension 
    USING_CYTHON = False 
else: 
    USING_CYTHON = True 

ext = 'pyx' if USING_CYTHON else 'c' 
sources = glob('my_module/*.%s' % (ext,)) 
extensions = [ 
    Extension(source.split('.')[0].replace(os.path.sep, '.'), 
       sources=[source], 
    ) 
for source in sources] 
cmdclass = {'build_ext': build_ext} if USING_CYTHON else {} 

setup(<..>, ext_modules=extensions, cmdclass=cmdclass) 

Die source.split Sachen benötigt wird, als cythonized Erweiterung Namen in Form my_module.ext sein müssen, während glob Pfadnamen wie my_module/ext erfordert.

See this repository für ein reales Beispiel.

Sie sollten jedoch umfassen .c Dateien in Ihrem git Repo sowie die ausschüttbaren sonst, wenn es an der Zeit eine Verteilung der .c Dateien neu gebaut werden zu bauen kommt und kann oder auch nicht die gleichen Dateien wie gebaut wurden auf deiner Maschine.

Sie können zum Beispiel von einer anderen Version von Cython oder auf einer anderen Plattform erstellt werden, die anderen Code erzeugt.

Cython ist ein statischer Compiler - es wird empfohlen, die erzeugten Dateien in das Repository zu übernehmen.

Es wird dringend empfohlen, die generierten .c-Dateien sowie Ihre Cython-Quellen zu verteilen, damit Benutzer Ihr Modul installieren können, ohne dass Cython verfügbar sein muss.

See Cython documentation on distributing modules.

+0

Danke für die Antwort! Dies löst die meisten Probleme recht einfach. Allerdings müsste ich .pyx-Dateien nur dann verteilen, wenn der Benutzer, der mein Paket installiert, Cython bereits installiert hat. Auch wenn ich vergessen hätte, build_ext vor sdist auszuführen, wäre das erstellte Paket nicht auf dem neuesten Stand. – Lefty

+0

Es wird generell empfohlen, beides zu versenden, ja. Ein anderer Ansatz wäre, eine env-Variable zu benötigen, um das Erstellen mit Cython zu ermöglichen, und andernfalls das Erstellen von C-Dateien. Das würde es unnötig machen, pyx-Dateien zu verteilen.Der letzte Kommentar bekräftigt, warum C-Dateien im Repo sein sollten - das Erstellen einer Distribution sollte niemals davon abhängen, etwas erzeugen zu müssen, das nicht im Repository ist. – danny

+0

Können Sie 'cmdclass = {'build_ext': build_ext} wenn USING_CYTHON else {}' zu Ihrem Beispielcode hinzufügen? Danach funktioniert es gut :) (auch ich bevorzuge 'Extension (file_name.split ('.') [0] .replace (os.path.sep, '.'), Sources = [file_name]) für file_name in Quellen und cythonize Import ist nicht erforderlich) – Lefty