webentwicklung-frage-antwort-db.com.de

setuptools: Speicherort des Paketdatenordners

Ich verwende setuptools, um mein Paket python zu verteilen. Jetzt muss ich zusätzliche Datendateien verteilen.

Nach dem, was ich aus der setuptools-Dokumentation zusammengetragen habe, muss sich meine Datendatei im Paketverzeichnis befinden. Ich möchte meine Datendateien jedoch lieber in einem Unterverzeichnis im Stammverzeichnis haben.

Was ich vermeiden möchte:

/ #root
|- src/
|  |- mypackage/
|  |  |- data/
|  |  |  |- resource1
|  |  |  |- [...]
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Was ich stattdessen gerne hätte:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Ich fühle mich einfach nicht wohl mit so vielen Unterverzeichnissen, wenn es nicht wesentlich ist. Ich kann keinen Grund finden, warum ich die Dateien in das Paketverzeichnis lege. Es ist auch umständlich, mit so vielen verschachtelten Unterverzeichnissen zu arbeiten, IMHO. Oder gibt es einen guten Grund, der diese Einschränkung rechtfertigen würde?

79
phant0m

Option 1: Als Paketdaten installieren

Der Hauptvorteil des Platzierens von Datendateien im Stammverzeichnis Ihres Python -Pakets besteht darin, dass Sie sich keine Gedanken darüber machen müssen, wo die Dateien auf dem System eines Benutzers gespeichert werden. Dies kann Windows, Mac, Linux oder eine mobile Plattform sein oder in einem Ei. Sie können das Verzeichnis data immer relativ zu Ihrem Python -Paketstamm finden, unabhängig davon, wo oder wie es installiert ist.

Zum Beispiel, wenn ich ein Projektlayout wie folgt habe:

project/
    foo/
        __init__.py
        data/
            resource1/
                foo.txt

Sie können __init__.py Eine Funktion hinzufügen, um einen absoluten Pfad zu einer Datendatei zu finden:

import os

_ROOT = os.path.abspath(os.path.dirname(__file__))
def get_data(path):
    return os.path.join(_ROOT, 'data', path)

print get_data('resource1/foo.txt')

Ausgänge:

/Users/pat/project/foo/data/resource1/foo.txt

Nachdem das Projekt als Ei installiert wurde, ändert sich der Pfad zu data, der Code muss sich jedoch nicht ändern:

/Users/pat/virtenv/foo/lib/python2.6/site-packages/foo-0.0.0-py2.6.Egg/foo/data/resource1/foo.txt

Option 2: An einem festen Ort installieren

Die Alternative wäre, Ihre Daten außerhalb des Pakets Python zu platzieren und dann entweder:

  1. Lassen Sie den Speicherort von data über eine Konfigurationsdatei, Befehlszeilenargumente oder übergeben
  2. Betten Sie den Speicherort in Ihren Python Code ein.

Dies ist weit weniger wünschenswert, wenn Sie Ihr Projekt verteilen möchten. Wenn Sie wirklich möchten, können Sie Ihr data auf dem Zielsystem installieren, indem Sie das Ziel für jede Gruppe von Dateien angeben, indem Sie a übergeben Liste der Tupel:

from setuptools import setup
setup(
    ...
    data_files=[
        ('/var/data1', ['data/foo.txt']),
        ('/var/data2', ['data/bar.txt'])
        ]
    )

pdated: Beispiel einer Shell-Funktion zum rekursiven Abrufen von Python -Dateien:

atlas% function grep_py { find . -name '*.py' -exec grep -Hn $* {} \; }
atlas% grep_py ": \["
./setup.py:9:    package_data={'foo': ['data/resource1/foo.txt']}
99
samplebias

Ich denke, ich habe einen guten Kompromiss gefunden, der es Ihnen ermöglicht, die folgende Struktur beizubehalten:

/ #root
|- data/
|  |- resource1
|  |- [...]
|- src/
|  |- mypackage/
|  |  |- __init__.py
|  |  |- [...]
|- setup.py

Sie sollten die Daten als package_data installieren, um die in samplebias answer beschriebenen Probleme zu vermeiden. Um die Dateistruktur beizubehalten, sollten Sie jedoch Folgendes zu Ihrer setup.py hinzufügen:

try:
    os.symlink('../../data', 'src/mypackage/data')
    setup(
        ...
        package_data = {'mypackage': ['data/*']}
        ...
    )
finally:
    os.unlink('src/mypackage/data')

Auf diese Weise erstellen wir die entsprechende Struktur "just in time" und pflegen unseren Quellbaum organisiert.

Um auf solche Datendateien in Ihrem Code zuzugreifen, verwenden Sie einfach Folgendes:

data = resource_filename(Requirement.parse("main_package"), 'mypackage/data')

Ich mag es immer noch nicht, 'mypackage' im Code angeben zu müssen, da die Daten mit diesem Modul möglicherweise nichts zu tun haben, aber ich denke, es ist ein guter Kompromiss.

11
polvoazul