Wie fügt man einem Rad zusätzliche Dateien hinzu?

74

Wie steuern Sie, welche Dateien in einem Rad enthalten sind? Es scheint MANIFEST.innicht von verwendet zu werden python setup.py bdist_wheel.

UPDATE :

Ich habe mich geirrt, was den Unterschied zwischen der Installation von einem Quell-Tarball und einem Rad betrifft. Die Quelldistribution enthält die in angegebenen Dateien MANIFEST.in, das installierte Paket enthält jedoch nur Python-Dateien. Es sind Schritte erforderlich, um zusätzliche Dateien zu identifizieren, die installiert werden sollen, unabhängig davon, ob die Installation über die Quelldistribution, das Ei oder das Rad erfolgt. Paketdaten werden nämlich für zusätzliche Paketdateien und Datendateien für Dateien außerhalb Ihres Pakets wie Befehlszeilenskripte oder Systemkonfigurationsdateien benötigt.

Ursprüngliche Frage

Ich habe ein Projekt, in dem ich python setup.py sdistmein Paket erstellt, MANIFEST.indie eingeschlossenen und ausgeschlossenen Dateien gesteuert und Pyrom und Check-Manifest verwendet habe , um meine Einstellungen zu bestätigen.

Ich habe es kürzlich in dualen Python 2/3 Code konvertiert und eine setup.cfg mit hinzugefügt

[bdist_wheel]
universal = 1

Ich kann ein Rad mit bauen python setup.py bdist_wheel, und es scheint wie gewünscht ein Universalrad zu sein. Es enthält jedoch nicht alle in angegebenen Dateien MANIFEST.in.

Was wird installiert?

Ich habe tiefer gegraben und weiß jetzt mehr über Verpackung und Rad. Folgendes habe ich gelernt:

Ich lade zwei Paketdateien in das multigtfs-Projekt auf PyPi hoch :

  • multigtfs-0.4.2.tar.gz- die Quell-Teerball, die alle Dateien enthält MANIFEST.in.
  • multigtfs-0.4.2-py2.py3-none-any.whl - Die fragliche Binärverteilung.

Ich habe zwei neue virtuelle Umgebungen erstellt, beide mit Python 2.7.5, und jedes Paket installiert ( pip install multigtfs-0.4.2.tar.gz). Die beiden Umgebungen sind nahezu identisch. Sie haben verschiedene .pycDateien, die "kompilierten" Python-Dateien. Es gibt Protokolldateien, die die verschiedenen Pfade auf der Festplatte aufzeichnen. Die Installation von der Quell-Teerball enthält einen Ordner multigtfs-0.4.2-py27.egg-info, in dem die Installation detailliert beschrieben ist, und die Radinstallation enthält einen multigtfs-0.4.2.dist-infoOrdner mit den Details dieses Vorgangs. Unter dem Gesichtspunkt des Codes, der das multigtfs-Projekt verwendet, gibt es jedoch keinen Unterschied zwischen den beiden Installationsmethoden.

Die von meinem Test verwendeten ZIP-Dateien wurden explizit nicht verwendet, sodass die Testsuite fehlschlägt:

$ django-admin startproject demo
$ cd demo
$ pip install psycopg2  # DB driver for PostGIS project
$ createdb demo         # Create PostgreSQL database
$ psql -d demo -c "CREATE EXTENSION postgis" # Make it a PostGIS database 
$ vi demo/settings.py   # Add multigtfs to INSTALLED_APPS,
                        # Update DATABASE to set ENGINE to django.contrib.gis.db.backends.postgis
                        # Update DATABASE to set NAME to test
$ ./manage.py test multigtfs.tests  # Run the tests
...
IOError: [Errno 2] No such file or directory: u'/Users/john/.virtualenvs/test/lib/python2.7/site-packages/multigtfs/tests/fixtures/test3.zip'

Zusätzliche Dateien angeben

Unter Verwendung der Vorschläge aus den Antworten fügte ich einige zusätzliche Anweisungen hinzu setup.py:

from __future__ import unicode_literals
# setup.py now requires some funky binary strings
...
setup(
    name='multigtfs',
    packages=find_packages(),
    package_data={b'multigtfs': ['test/fixtures/*.zip']},
    include_package_data=True,
    ...
)

Dadurch werden die Zip-Dateien (sowie die README-Datei) im Ordner installiert, und die Tests werden jetzt ordnungsgemäß ausgeführt. Danke für die Vorschläge!

jwhitlock
quelle
Welche Dateien fehlen genau?
rje
Alle Nicht-Python-Dateien, z. B. Dokumentationen oder Testgeräte. Meine Anwendung enthält einige in Tests verwendete ZIP-Dateien, von denen einige behaupten, dass sie in einer Binärdistribution nicht benötigt werden. Andere haben möglicherweise Nicht-Python-Dateien, die zur Laufzeit benötigt werden.
Jwhitlock
Ich finde Ihre Frage schwer zu verstehen. Wie soll die setup.py Dateien in ein Rad aufnehmen (Die Tag-Beschreibung ist leer, daher weiß ich nicht, worauf Sie sich beziehen)?
11.
Wheel ist ein Paketformat für Python und wird häufig dem .egg-Format vorgezogen, wenn beide verfügbar sind. Um ein Rad zu bauen, rennst du python setup.py bdist_wheel. Weitere Informationen finden Sie in den Dokumenten unter Wheel.readthedocs.org (die meine Frage nicht beantworten) und auf pythonwheels.com .
Jwhitlock
2
Die magische Kombination besteht darin, die Dateien mit anzugeben MANIFEST.inund dann include_package_data=Truezu setup.py hinzuzufügen .
Geschwindigkeit

Antworten:

43

Haben Sie versucht, package_datain Ihrem zu verwenden setup.py? MANIFEST.inscheint auf Python-Versionen <= 2.6 ausgerichtet zu sein, ich bin mir nicht sicher, ob höhere Versionen es überhaupt sehen.

Nachdem sie https://github.com/pypa/sampleproject erkundet haben , MANIFEST.inheißt es:

# If using Python 2.6 or less, then have to include package data, even though
# it's already declared in setup.py
include sample/*.dat

was zu implizieren scheint, dass diese Methode veraltet ist. Inzwischen setup.pyerklären sie in:

setup(
    name='sample',
    ...
    # If there are data files included in your packages that need to be
    # installed, specify them here.  If using Python 2.6 or less, then these
    # have to be included in MANIFEST.in as well.
    package_data={
        'sample': ['package_data.dat'],
    },
    ...
)

(Ich bin nicht sicher, warum sie einen Platzhalter MANIFEST.inund einen Dateinamen gewählt haben setup.py. Sie verweisen auf dieselbe Datei.)

Dies scheint nicht nur einfacher zu sein, sondern auch zu implizieren, dass die package_dataRoute der MANIFEST.inMethode überlegen ist . Nun, es sei denn, Sie müssen 2.6 unterstützen. In diesem Fall gehen meine Gebete an Sie.

vgel
quelle
1
Vielen Dank! Das hat mich auf den richtigen Weg gebracht. Ich habe der Frage Details für meine eigene Lösung hinzugefügt.
Jwhitlock
Tolle Antwort, das ist in der Tat der Schuldige. Hier ist auch ein schöner Artikel zu diesem Thema .
gaborous
30

Bevor Sie Änderungen an vornehmenMANIFEST.in oder setup.pyalte Ausgabeverzeichnisse entfernen müssen. Setuptools speichert einige Daten zwischen und dies kann zu unerwarteten Ergebnissen führen.

rm -rf build *.egg-info

Wenn Sie dies nicht tun, erwarten Sie, dass nichts richtig funktioniert.

Das ist aus dem Weg.

  1. Wenn Sie eine Quelldistribution ( sdist) erstellen, können Sie eine der folgenden Methoden verwenden.

  2. Wenn Sie ein Rad ( bdist_wheel) bauen, werden include_package_dataund MANIFEST.inignoriert und Sie müssen package_dataund verwenden data_files.

INCLUDE_PACKAGE_DATA

Dies ist eine gute Option, wird aber bdist_wheelnicht berücksichtigt.

setup(
    ...
    include_package_data=True
)

# MANIFEST.in
include package/data.json

DATA_FILES für Nicht-Paketdaten

Dies ist die flexibelste Option, da Sie eine beliebige Datei aus Ihrem Repo zu einem sdistoder hinzufügen könnenbdist_wheel

setup(
    ....
    data_files=[
        ('output_dir',['conf/data.json']),
    ]
    # For sdist, output_dir is ignored!
    #
    # For bdist_wheel, data.json from conf dir in root of your repo 
    # and stored at `output_dir/` inside of the sdist package.
)

PACKAGE_DATA für Nicht-Python-Dateien im Paket

Ähnlich wie oben, aber zum Beispiel bdist_wheellegen Sie Ihre Datendateien in das Paket. Es ist identisch für sdist, hat aber mehr Einschränkungen als data_filesweil Dateien nur aus Ihrem Paket-Unterverzeichnis stammen können.

setup(
    ...
    package_data={'package':'data.json'},
    # data.json must be inside of your actual package
)
cmcginty
quelle
Können Sie ein Beispiel für die Verwendung eines globMusters hinzufügen ? Ich data_files('output_dir': ['conf/*.json'])
vermute,
1
@piRSquared Globbing wird nicht direkt unterstützt, aber Sie können Beispiele aus anderen Antworten hier verwenden:glob('conf/*.json')
cmcginty
Das Format der Datendateien ist falsch und sollte ,nicht :wie folgt sein : data_files=[('my_data', ['data/data_file'])], Referenzdokument Ich hätte es bearbeitet, aber die Änderungen müssen 6 Zeichen lang sein ...
Andrew Fraser
@ AndrewFraser .. jetzt behoben.
cmcginty
"INCLUDE_PACKAGE_DATA ist eine gute Option, aber bdist_wheel berücksichtigt sie nicht"? include pkg/test/*.pyin MANIFEST.infunktioniert gut (in setuptools 45.2.0).
Denis
22

Sie können package_dataund data_filesin verwenden setup.py, um zusätzliche Dateien anzugeben, aber es ist lächerlich schwer, sie richtig (und fehlerhaft) zu machen .

Eine Alternative ist , zu verwenden , MANIFEST.inund fügen Sie include_package_data=Truein setup()Ihrem setup.pywie hier angedeutet .

Mit dieser Anweisung MANIFEST.inwird das verwendet, um die Dateien anzugeben, die nicht nur im Quell-Tarball / Zip, sondern auch im Wheel- und Win32-Installationsprogramm enthalten sein sollen. Dies funktioniert auch mit jeder Python-Version (ich habe an einem Projekt von py2.6 bis py3.6 getestet).

UPDATE 2020: Es scheint, dass MANIFEST.in in Python 3 nicht mehr vom Rad geehrt wird, obwohl es sich immer noch im tar.gz befindet, selbst wenn Sie es einstellen include_package_data=True.

So beheben Sie das: Sie müssen sowohl include_package_dataals auch angeben packages.

Wenn sich Ihr Python-Modul in einem Ordner "pymod" befindet, ist hier das entsprechende Setup:

setup( ...
    include_package_data = True,
    packages = ['pymod'],
)

Wenn sich Ihre Python-Skripte im Stammverzeichnis befinden, verwenden Sie:

setup( ...
    include_package_data = True,
    packages = ['.'],
)

Anschließend können Sie Ihre .whl-Datei mit einer Zip-Archivierungssoftware wie 7-zip öffnen, um zu überprüfen, ob sich tatsächlich alle gewünschten Dateien befinden.

gaborous
quelle
4
Dies sollte die aktuell akzeptierte Antwort sein! Die Verwendung package_data=...wie in der anderen Antwort ist mit Gefahren behaftet (lesen Sie die Links und die Links hinter den Links)
Matt Wilkie
Mit NumPy nimmt auf setupin numpy.distutils.core, kann ich nicht Räder an der Arbeit mit include_package_data=True. Es hört nur zu package_data.
22.
1
(1) Räder tun (trotz der docs) respektieren die Kombination von MANIFEST.in Plus include_package_data = True, aber (2) gilt dies nur für ‚Paketdaten‘ aka Dinge , die in den Verzeichnissen der Pakete sitzen , anstatt zB der Projektwurzel
Brad Solomon
10

Mit der Direktive data_files können Sie zusätzliche Dateien angeben, die installiert werden sollen . Ist es das, wonach du suchst? Hier ist ein kleines Beispiel:

from setuptools import setup
from glob import glob

setup(
    name='extra',
    version='0.0.1',
    py_modules=['extra'],
    data_files=[
        ('images', glob('assets/*.png')),
    ],
)
faul1
quelle
5
Das sieht sehr vielversprechend aus, aber nach 2 Stunden konnte ich data_files oder package_files nicht zum Laufen bringen. Kennen Sie Projekte mit diesen Funktionen, bei denen ich nach Arbeitscode suchen könnte?
Jwhitlock
1

include_package_dataist der richtige Weg, und es funktioniert für SDIST und Räder .

Sie müssen es jedoch richtig machen, und ich habe Monate gebraucht, um das herauszufinden. Deshalb habe ich Folgendes gelernt.

Der Trick wird im Wesentlichen im Namen der Option angegeben include_PACKAGE_data: Die Datendateien müssen sich in einem Paketunterordner befinden

Dann und nur dann, wenn

  • include_package_data ist wahr
  • Die Datendateien sind in aufgeführt MANIFEST.in(* siehe auch meine Anmerkung am Ende über setuptools_scm)
  • und die Datendateien befinden sich in einem Paketverzeichnis

dann werden die Datendateien aufgenommen.

Arbeitsbeispiel:

Vorausgesetzt, das Projekt hat die folgende Struktur und Dateien:

|- MANIFEST.in
|- setup.cfg
|- setup.py
|
\---foo
    |- __init__.py
    |
    \---data
         - example.png

Und die folgende Konfiguration:

Manifest.in:

recursive-include foo/data *

setup.py

import setuptools

setuptools.setup()

setup.cfg

[metadata]
name = wheel-data-files-example
url = www.example.com
maintainer = None
maintainer_email = [email protected]

[options]
packages =
    foo
include_package_data = True

SDIST-Pakete und Ihre Räder, die Sie erstellen, enthalten auch die example.pngDatendatei.

(Anstelle von setup.cfg kann die Konfiguration natürlich auch direkt in setup.py angegeben werden. Dies ist jedoch für das Beispiel nicht relevant.)

Update: Für src-Layout-Projekte

Dies sollte auch für Projekte funktionieren, die ein src-Layout verwenden und folgendermaßen aussehen:

|- MANIFEST.in
|- setup.cfg
|- setup.py
|
\---src
    |
    \---foo
        |- __init__.py
        |
        \---data
             - example.png

Informieren Sie setuptools über das src-Verzeichnis, indem Sie Folgendes verwenden package_dir:

setup.cfg

[metadata]
name = wheel-data-files-example
url = www.example.com
maintainer = None
maintainer_email = [email protected]

[options]
packages =
    foo
include_package_data = True
package_dir =
    =src

Und im Manifest den Pfad anpassen:

Manifest.in:

recursive-include src/foo/data *

Hinweis: Bei Verwendung ist kein Manifest erforderlich setuptools_scm

Wenn Sie setuptools verwenden und das setuptools_scmPlugin ( auf pypi ) hinzufügen , müssen Sie keine Manifest.in-Datei verwalten. Stattdessen sorgt setuptools_scm dafür, dass alle Dateien, die von git verfolgt werden, zum Paket hinzugefügt werden.

In diesem Fall lautet die Regel für das Hinzufügen oder Nicht-Hinzufügen einer Datei zum SDIST / Wheel: Wenn und nur wenn

  • include_package_data ist wahr
  • Die Datei wird von git (oder einem anderen von setuptools_scm unterstützten Tool) verfolgt.
  • und die Datendateien befinden sich in einem Paketverzeichnis

dann werden die Datendateien aufgenommen.

Stefan D.
quelle
Ich habe diesen Ansatz ausprobiert, hatte aber kein Glück, mich datains Steuer zu setzen. Ich vermute, es ist mein srcbasiertes Layout und die zusätzliche Indirektionsebene, die es einführt. Ich habe jetzt mein dataVerzeichnis wie vorgeschlagen in das Paketverzeichnis verschoben , aber stattdessen entfernt MANIFEST.inund include_package_dataverwendet package_data={'package': ['data/specific_file']}. Alles ist golden für beide sdistund bdist_wheel. Viel Glück beim Verwenden von Globs mit all den impliziten beteiligten Unterverzeichnissen ... :-)
llude
Ich habe meine Antwort aktualisiert und sie sollte jetzt auch für Projekte mit src-Layout funktionieren.
Stefan D.
Fantastisch! Ich habe Ihr Beispielpaket wörtlich in meine Umgebung kopiert und es funktioniert wunderbar. Dadurch konnte ich herausfinden, warum sich mein eigentliches Paket anders verhält ... Ich habe vergessen, dass ich numpy.distutils.core.setupunterwegs eine Fortran-Erweiterung erstelle, aber dann musste ich auch zufällig importieren setuptools.setup, damit der bdist_wheelBefehl überhaupt funktioniert . Es baut das Rad, besteht aber darauf, es zu benutzen package_data. Diese Frachtkult-Hacky-Sacky-Route, um zu einem funktionierenden Python-Paket zu gelangen, bringt mich dazu, die wenigen Haare herauszuziehen, die ich noch habe :-D
llude
-1

Ich hatte config / directory mit JSON-Dateien, die ich dem Radpaket hinzufügen musste. Also habe ich diese Zeilen hinzugefügt zu MANIFEST.in:

recursive-include config/ *.json

Die folgende Richtlinie an setup.py:

setup(
 ...
 include_package_data=True,
)

Und nichts hat funktioniert. Bis ich eine leere Datei namens __init__.pyinside config/directory erstellt habe.

(Python 3.6.7, Rad 3.6.7, Setuptools 39.0.1)

Michael Spector
quelle