Einschließen von Nicht-Python-Dateien in setup.py

200

Wie kann ich setup.pyeine Datei einschließen, die nicht Teil des Codes ist? (Insbesondere handelt es sich um eine Lizenzdatei, aber es kann auch eine andere Sache sein.)

Ich möchte den Speicherort der Datei steuern können. Im ursprünglichen Quellordner befindet sich die Datei im Stammverzeichnis des Pakets. (dh auf der gleichen Ebene wie die oberste __init__.py.) Ich möchte, dass es genau dort bleibt, wenn das Paket installiert wird, unabhängig vom Betriebssystem. Wie mache ich das?

Ram Rachum
quelle
Wie machst du das im Moment? Ihre vorherige Frage zeigt an, dass Sie mit dem Hinzufügen der Lizenzdatei vertraut sind. Was ist also Ihr Code, der "nicht funktioniert"?
SilentGhost
2
data_files = [('', ['lgpl2.1_license.txt',]),]legt es im Python26-Ordner ab.
Ram Rachum
Nach einigen negativen Rückmeldungen las ich Ihre Frage erneut und stellte fest, was mir fehlte. Ich habe meine Antwort aktualisiert, um eine nicht hackige Lösung für Ihre Frage bereitzustellen, für die keine zusätzlichen Module erforderlich sind (z. B. Setuptools oder Distribute).
Evan Plaice
Danke Evan. Ich bin jedoch vollkommen in Ordnung mit Setuptools, da es so weit verbreitet ist.
Ram Rachum

Antworten:

224

Der wahrscheinlich beste Weg, dies zu tun, ist die Verwendung der setuptools package_dataRichtlinie. Dies bedeutet zwar die Verwendung von setuptools(oder distribute) anstelle von distutils, aber dies ist ein sehr nahtloses "Upgrade".

Hier ist ein vollständiges (aber nicht getestetes) Beispiel:

from setuptools import setup, find_packages

setup(
    name='your_project_name',
    version='0.1',
    description='A description.',
    packages=find_packages(exclude=['ez_setup', 'tests', 'tests.*']),
    package_data={'': ['license.txt']},
    include_package_data=True,
    install_requires=[],
)

Beachten Sie die spezifischen Zeilen, die hier kritisch sind:

package_data={'': ['license.txt']},
include_package_data=True,

package_dataist ein dictPaketname (leer = alle Pakete) zu einer Liste von Mustern (kann Globs enthalten). Wenn Sie beispielsweise nur Dateien in Ihrem Paket angeben möchten, können Sie dies auch tun:

package_data={'yourpackage': ['*.txt', 'path/to/resources/*.txt']}

Die Lösung hier ist definitiv nicht , Ihre Nicht- pyDateien mit einer .pyErweiterung umzubenennen .

Siehe Ian Bicking Präsentation für weitere Informationen.

UPDATE: Ein weiterer [besserer] Ansatz

Ein anderer Ansatz, der gut funktioniert, wenn Sie nur den Inhalt der Quelldistribution ( sdist) steuern und Dateien außerhalb des Pakets haben möchten (z. B. Verzeichnis der obersten Ebene), ist das Hinzufügen einer MANIFEST.inDatei. Informationen zum Format dieser Datei finden Sie in der Python-Dokumentation .

Seit ich diese Antwort geschrieben habe, habe ich festgestellt, dass die Verwendung MANIFEST.inin der Regel weniger frustrierend ist, um sicherzustellen, dass Ihre Quelldistribution ( tar.gz) die benötigten Dateien enthält.

Wenn Sie beispielsweise das requirements.txtVerzeichnis der obersten Ebene einschließen möchten, schließen Sie das Datenverzeichnis der obersten Ebene rekursiv ein:

include requirements.txt
recursive-include data *

Damit diese Dateien bei der Installation in den Ordner des Pakets in Site-Paketen kopiert werden können, müssen Sie include_package_data=Truedie setup()Funktion bereitstellen . Weitere Informationen finden Sie unter Hinzufügen von Nicht-Code-Dateien .

Hans L.
quelle
5
package_data ist seit Python 2.3 auch für reine Distutils-Setup-Skripte verfügbar.
Éric Araujo
15
Diese Antwort sieht vernünftig aus, funktioniert aber bei mir nicht. Da package_data notorisch unzuverlässig ist (erfordert die Koordination von MANIFEST.in und setup.py, um Dateien zur sdist hinzuzufügen und sie als separate Schritte zu installieren) und der Autor dieser Antwort feststellt, dass sie "nicht getestet" wird, kann jeder sonst bestätigen, ob es für sie funktioniert? Meine LIZENZ-Datei ist in der SDIST enthalten, aber nicht installiert, wenn ich "python setup.py install" oder "pip install Package" ausführe
Jonathan Hartley
11
Die Präsentation von Ian Bicking zeigt nur, wie Paketdaten für Dateien installiert werden, die sich in einem Paket befinden. Meine LIZENZ-Datei befindet sich auf der obersten Ebene meines Projekts, dh nicht in Paketen. Kann ich package_data trotzdem verwenden? Die Verwendung von data_files ist kein Starter, da die Dateien an einem systemweiten Speicherort abgelegt werden. nicht mit meinem Projekt verbunden, und um es noch schlimmer zu machen, ändert sich der Speicherort abhängig davon, ob ich "setup.py install" oder "pip install" von derselben SD-Liste aus ausführe.
Jonathan Hartley
8
Ich vermute , dass der Grund , warum es nicht für mich Arbeit ist , dass die Datei nicht in jedem Paket befindet - es ist eine Lizenzdatei in der obersten Ebene des Endlagers und somit nicht mit ‚package_data‘ werden kann installiert
Jonathan Hartley
7
Diese Antwort funktioniert bei mir nicht. Die zusätzlichen Dateien werden nicht in den Tarball gelegt ...
lpapp
44

Um das zu erreichen, was Sie beschreiben, sind zwei Schritte erforderlich ...

  • Die Datei muss dem Quell-Tarball hinzugefügt werden
  • setup.py muss geändert werden, um die Datendatei im Quellpfad zu installieren

Schritt 1: Um die Datei zum Quell-Tarball hinzuzufügen, fügen Sie sie dem MANIFEST hinzu

Erstellen Sie eine MANIFEST- Vorlage in dem Ordner, der setup.py enthält

Das MANIFEST ist im Grunde eine Textdatei mit einer Liste aller Dateien, die im Quell-Tarball enthalten sein werden.

So sehen die MANIFEST für mein Projekt aus:

  • CHANGELOG.txt
  • INSTALL.txt
  • LICENSE.txt
  • pypreprocessor.py
  • README.txt
  • setup.py
  • test.py
  • TODO.txt

Hinweis: Während Sdist hat einige Dateien automatisch hinzufügen , ziehe ich sie explizit angeben , um sicher zu sein , anstatt die Vorhersage , was es tut und nicht tut.

Schritt 2: Um die Datendatei im Quellordner zu installieren, ändern Sie setup.py

Da Sie dem Quellinstallationsordner eine Datendatei (LICENSE.txt) hinzufügen möchten, müssen Sie den Dateninstallationspfad an den Quellinstallationspfad anpassen. Dies ist erforderlich, da Datendateien standardmäßig an einem anderen Speicherort als Quelldateien installiert werden.

So ändern Sie das Dateninstallationsverzeichnis so, dass es mit dem Quellinstallationsverzeichnis übereinstimmt ...

Ziehen Sie die Installationsverzeichnisinformationen von distutils mit:

from distutils.command.install import INSTALL_SCHEMES

Ändern Sie das Dateninstallationsverzeichnis so, dass es mit dem Quellinstallationsverzeichnis übereinstimmt:

for scheme in INSTALL_SCHEMES.values():
    scheme['data'] = scheme['purelib']

Fügen Sie die Datendatei und den Speicherort zu setup () hinzu:

data_files=[('', ['LICENSE.txt'])]

Hinweis: Mit den obigen Schritten sollten Sie genau das erreichen, was Sie standardmäßig beschrieben haben, ohne dass Erweiterungsbibliotheken erforderlich sind.

Evan Scholle
quelle
10
MANIFEST steuert nur Steuerdateien, die im Quell-Tarball enthalten sind (erstellt von sdist). Dort aufgeführte Dateien werden nicht installiert.
David Cournapeau
@ David Ich wusste nicht, wie weit ich bei meinem ersten Ansatz entfernt war. Ich habe die Antwort so aktualisiert, dass sie korrekt ist, um die gestellten Fragen zu erfüllen, ohne dass zusätzliche Bibliotheken von Drittanbietern erforderlich sind.
Evan Plaice
3
@ Éric Gibt es einen bestimmten Grund warum? Haben Sie eine praktikable Installationsalternative, für die keine Pakete von Drittanbietern (wie setup_tools) erforderlich sind? Ich habe Distutils gegenüber Setuptools gewählt, weil es in einer Vanille-Installation von Python enthalten ist und ich Module für PYPI erstellt habe. Es sollte jetzt einen besseren Weg geben, dies mit distutils2 zu tun, aber ich habe Python schon eine ganze Weile nicht mehr berührt, also würde ich nicht wissen wie. Da Sie sich mit distutils2 auskennen, denke ich, dass es für den Rest von uns von Vorteil wäre, eine geeignete distutils2-Alternative zu haben.
Evan Plaice
6
Wie bereits in anderen Threads erwähnt, package_datafunktioniert dies nicht, wenn sich die Datei nicht im Paket befindet.
Gringo Suave
2
@ ÉricAraujo: Es ist keine schlechte Idee, diese Lösung zu verwenden, da es keinen anderen Weg gibt. Es ist ein schlechtes Distutils-Design - das stimmt. Aber es ist de facto eine öffentliche API, die sich nie ändern wird, weil sie viele Dinge kaputt macht. Hoffen wir, dass distutils2 besser empfohlene Möglichkeiten bietet.
Anatoly Techtonik
15

Erstellen Sie MANIFEST.inim Projektstamm mit recursive-includedem gewünschten Verzeichnis oder includemit dem Dateinamen.

include LICENSE
include README.rst
recursive-include package/static *
recursive-include package/templates *

Dokumentation finden Sie hier

Alles ist gut
quelle
7

Ich wollte einen Kommentar zu einer der Fragen posten, aber ich habe nicht genug Ruf, um das zu tun>.>

Folgendes hat bei mir funktioniert (nachdem ich auf die Dokumente verwiesen hatte):

package_data={
    'mypkg': ['../*.txt']
},

include_package_data: False

Die letzte Zeile war seltsamerweise auch für mich von entscheidender Bedeutung (Sie können dieses Schlüsselwortargument auch weglassen - es funktioniert genauso).

Dadurch werden alle Textdateien in Ihr oberstes oder Stammverzeichnis kopiert (eine Ebene höher als das Paket, das mypkgSie verteilen möchten).

Hoffe das hilft!

rv.kvetch
quelle
Ich suchte nach einer Möglichkeit, keine erstellen zu müssen MANIFEST.in, das funktionierte für mich. Die letzte Zeile war auch für mich entscheidend. Meine Zeilen wareninclude_package_data=False, package_data={ "": ["../CHANGELOG.md"] },
Mendhak
7

Schritt 1: Erstellen Sie MANIFEST.inmit setup.py eine Datei im selben Ordner

Schritt 2: Geben Sie den relativen Pfad zu den Dateien an, die Sie hinzufügen möchtenMANIFEST.in

include README.rst
include docs/*.txt
include funniest/data.json

Schritt 3: Stellen Sie include_package_data=Truedie setup()Funktion ein, um diese Dateien in das Site-Paket zu kopieren

Referenz ist hier.

Debuglife
quelle
6

Es ist 2019, und hier ist, was funktioniert - trotz Ratschlägen hier und da wird das, was ich im Internet zur Hälfte dokumentiert gefunden habe setuptools_scm, als Option an übergeben setuptools.setup. Dies schließt alle Datendateien ein, die auf Ihrem VCS versioniert sind, sei es git oder eine andere, in das Radpaket, und führt eine "pip install" aus dem git-Repository durch, um diese Dateien mitzubringen.

Also habe ich gerade diese beiden Zeilen zum Setup-Aufruf auf "setup.py" hinzugefügt. Keine zusätzlichen Installationen oder Importe erforderlich:

    setup_requires=['setuptools_scm'],
    include_package_data=True,

Sie müssen package_data nicht manuell oder in einer MANIFEST.in-Datei auflisten - wenn es versioniert ist, ist es im Paket enthalten. In den Dokumenten zu "setuptools_scm" wird der Schwerpunkt auf die Erstellung einer Versionsnummer aus der Festschreibungsposition gelegt und der wirklich wichtige Teil beim Hinzufügen der Datendateien ignoriert. (Es ist mir egal, ob meine Zwischenraddatei "* 0.2.2.dev45 + g3495a1f" heißt oder die von mir eingegebene fest codierte Versionsnummer "0.3.0dev0" verwendet - aber wichtige Dateien für das Programm belässt Arbeit dahinter ist etwas wichtig)

jsbueno
quelle
5

In setup.py unter setup (:

setup(
   name = 'foo library'
   ...
  package_data={
   'foolibrary.folderA': ['*'],     # All files from folder A
   'foolibrary.folderB': ['*.txt']  #All text files from folder B
   },
Das schneidige Adam Hughes
quelle
1
Dies trägt eigentlich nichts dazu bei, das Ziel des OP zu erreichen. Was auch immer Sie schreiben package_data, hat keinen Einfluss darauf, was setup.py installfunktioniert, es sei denn, Sie ändern den Installationsbefehl selbst. Es sei denn, diese Dateien befinden sich im Paketverzeichnis, was Sie normalerweise vermeiden möchten.
wvxvw
3

Hier ist eine einfachere Antwort, die für mich funktioniert hat.

Erstens sind gemäß dem obigen Kommentar eines Python-Entwicklers setuptools nicht erforderlich:

package_data is also available to pure distutils setup scripts 
since 2.3.  Éric Araujo

Das ist großartig, denn wenn Sie eine Setuptool-Anforderung an Ihr Paket stellen, müssen Sie es auch installieren. Zusamenfassend:

from distutils.core import setup

setup(
    # ...snip...
    packages          = ['pkgname'],
    package_data      = {'pkgname': ['license.txt']},
)
Gringo Suave
quelle
1
Es wird sich beschweren, dass das Verzeichnis pkgamenicht existiert
Anthony Kong
1

Ich wollte nur etwas nachverfolgen, das ich bei der Arbeit mit Python 2.7 unter Centos 6 gefunden habe. Das Hinzufügen der Paketdaten oder Datendateien wie oben erwähnt hat bei mir nicht funktioniert. Ich habe eine MANIFEST.IN mit den gewünschten Dateien hinzugefügt, die die Nicht-Python-Dateien in den Tarball einfügt, sie aber nicht über RPM auf dem Zielcomputer installiert.

Am Ende konnte ich die Dateien mithilfe der "Optionen" in den Setup / Setuptools in meine Lösung aufnehmen. Mit den Optionsdateien können Sie verschiedene Abschnitte der Spezifikationsdatei aus setup.py ändern. Wie folgt.

from setuptools import setup


setup(
    name='theProjectName',
    version='1',
    packages=['thePackage'],
    url='',
    license='',
    author='me',
    author_email='[email protected]',
    description='',
    options={'bdist_rpm': {'install_script': 'filewithinstallcommands'}},
)

Datei - MANIFEST.in:

include license.txt

Datei - Datei mit Installationsbefehlen:

mkdir -p $RPM_BUILD_ROOT/pathtoinstall/
#this line installs your python files
python setup.py install -O1 --root=$RPM_BUILD_ROOT --record=INSTALLED_FILES
#install license.txt into /pathtoinstall folder
install -m 700 license.txt $RPM_BUILD_ROOT/pathtoinstall/
echo /pathtoinstall/license.txt >> INSTALLED_FILES
Scott Bowers
quelle
-12

Umgehung einer Problemumgehung: Ich habe meine lgpl2.1_license.txtin umbenannt lgpl2.1_license.txt.pyund den Text in dreifache Anführungszeichen gesetzt. Jetzt muss ich weder die data_filesOption verwenden noch absolute Pfade angeben. Ich weiß, es ist hässlich, es zu einem Python-Modul zu machen, aber ich halte es für weniger hässlich als die Angabe absoluter Pfade.

Ram Rachum
quelle
7
Siehe meinen Beitrag. Es muss nicht hässlich sein. Es ist nur schwer, ein gutes Beispiel im Internet zu finden, da eine gute Dokumentation zum Einrichten von Paketen schwer zu finden ist.
Evan Plaice