Ein Verpackungsvorspiel:
Bevor Sie sich überhaupt um das Lesen von Ressourcendateien kümmern können, müssen Sie zunächst sicherstellen, dass die Datendateien überhaupt in Ihre Distribution gepackt werden. Es ist einfach, sie direkt aus dem Quellbaum zu lesen, aber der wichtige Teil ist das Erstellen Stellen Sie sicher, dass auf diese Ressourcendateien über Code in einem installierten Paket zugegriffen werden kann.
Strukturieren Sie Ihr Projekt folgendermaßen und fügen Sie Datendateien in ein Unterverzeichnis innerhalb des Pakets ein:
.
├── package
│ ├── __init__.py
│ ├── templates
│ │ └── temp_file
│ ├── mymodule1.py
│ └── mymodule2.py
├── README.rst
├── MANIFEST.in
└── setup.py
Sie sollten include_package_data=True
den setup()
Anruf weiterleiten. Die Manifestdatei wird nur benötigt, wenn Sie setuptools / distutils verwenden und Quelldistributionen erstellen möchten. templates/temp_file
Fügen Sie der Manifestdatei eine Zeile wie diese hinzu, um sicherzustellen, dass das Paket für diese Beispielprojektstruktur gepackt wird:
recursive-include package *
Historischer Cruft-Hinweis: Die Verwendung einer Manifestdatei ist für moderne Build-Backends wie Flit oder Poetry nicht erforderlich , die standardmäßig die Paketdatendateien enthalten. Wenn Sie pyproject.toml
also eine setup.py
Datei verwenden und keine haben , können Sie alles ignorieren MANIFEST.in
.
Nun, mit der Verpackung aus dem Weg, auf den Leseteil ...
Empfehlung:
Verwenden Sie Standard-Bibliotheks- pkgutil
APIs. Im Bibliothekscode wird es so aussehen:
# within package/mymodule1.py, for example
import pkgutil
data = pkgutil.get_data(__name__, "templates/temp_file")
print("data:", repr(data))
text = pkgutil.get_data(__name__, "templates/temp_file").decode()
print("text:", repr(text))
Es funktioniert in Reißverschlüssen. Es funktioniert unter Python 2 und Python 3. Es sind keine Abhängigkeiten von Drittanbietern erforderlich. Ich bin mir keiner Nachteile bewusst (wenn ja, dann kommentieren Sie bitte die Antwort).
Schlechte Möglichkeiten zu vermeiden:
Schlechter Weg Nr. 1: Verwenden relativer Pfade aus einer Quelldatei
Dies ist derzeit die akzeptierte Antwort. Bestenfalls sieht es ungefähr so aus:
from pathlib import Path
resource_path = Path(__file__).parent / "templates"
data = resource_path.joinpath("temp_file").read_bytes()
print("data", repr(data))
Was stimmt damit nicht? Die Annahme, dass Dateien und Unterverzeichnisse verfügbar sind, ist nicht korrekt. Dieser Ansatz funktioniert nicht, wenn Code ausgeführt wird, der in einer Zip-Datei oder einem Rad gepackt ist, und es liegt möglicherweise völlig außerhalb der Kontrolle des Benutzers, ob Ihr Paket überhaupt in ein Dateisystem extrahiert wird oder nicht.
Schlechter Weg # 2: Verwenden von pkg_resources-APIs
Dies ist in der am besten bewerteten Antwort beschrieben. Es sieht ungefähr so aus:
from pkg_resources import resource_string
data = resource_string(__name__, "templates/temp_file")
print("data", repr(data))
Was stimmt damit nicht? Es wird eine Laufzeitabhängigkeit von setuptools hinzugefügt , die vorzugsweise nur eine Installationszeitabhängigkeit sein sollte. Das Importieren und Verwenden pkg_resources
kann sehr langsam werden, da der Code einen funktionierenden Satz aller installierten Pakete erstellt, obwohl Sie nur an Ihren eigenen Paketressourcen interessiert waren . Das ist zur Installationszeit keine große Sache (da die Installation einmalig ist), aber zur Laufzeit ist es hässlich.
Schlechter Weg Nr. 3: Verwenden der APIs importlib.resources
Dies ist derzeit die Empfehlung in der am besten bewerteten Antwort. Es handelt sich um eine neue Standardbibliothek ( neu in Python 3.7 ), es ist jedoch auch ein Backport verfügbar. Es sieht aus wie das:
try:
from importlib.resources import read_binary
from importlib.resources import read_text
except ImportError:
# Python 2.x backport
from importlib_resources import read_binary
from importlib_resources import read_text
data = read_binary("package.templates", "temp_file")
print("data", repr(data))
text = read_text("package.templates", "temp_file")
print("text", repr(text))
Was stimmt damit nicht? Nun, leider funktioniert es nicht ... noch nicht. Dies ist immer noch eine unvollständige API. importlib.resources
Wenn Sie diese verwenden, müssen Sie eine leere Datei hinzufügen templates/__init__.py
, damit sich die Datendateien in einem Unterpaket und nicht in einem Unterverzeichnis befinden. Das package/templates
Unterverzeichnis wird auch als package.templates
eigenständiges importierbares Unterpaket verfügbar gemacht . Wenn das keine große Sache ist und Sie nicht stört, können Sie die __init__.py
Datei dort hinzufügen und über das Importsystem auf Ressourcen zugreifen. Wenn Sie schon dabei sind, können Sie es auch zu einer my_resources.py
Datei machen und einfach einige Bytes oder String-Variablen im Modul definieren und sie dann in Python-Code importieren. Es ist das Importsystem, das hier so oder so das schwere Heben erledigt.
Beispielprojekt:
Ich habe ein Beispielprojekt auf erstellt Github und hochgeladen auf PyPI , die alle vier demonstriert oben Ansätze diskutiert. Probieren Sie es aus mit:
$ pip install resources-example
$ resources-example
Weitere Informationen finden Sie unter https://github.com/wimglenn/resources-example .